From 15ef6854d231260e61ebe727ed3fdd6be38a763f Mon Sep 17 00:00:00 2001 From: Kamil Cudnik <kcudnik@gmail.com> Date: Sat, 27 Oct 2018 12:06:22 +0200 Subject: [PATCH] Add option to generate current dep graph to saidump (#361) * Add option to generate current dep graph to saidump * Add log method entry --- saidump/saidump.cpp | 241 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 240 insertions(+), 1 deletion(-) diff --git a/saidump/saidump.cpp b/saidump/saidump.cpp index 434ee558f652..c50c6e944300 100644 --- a/saidump/saidump.cpp +++ b/saidump/saidump.cpp @@ -1,4 +1,6 @@ #include <string> +#include <set> +#include <sstream> extern "C" { #include <sai.h> @@ -16,6 +18,7 @@ struct CmdOptions { bool skipAttributes; bool dumpTempView; + bool dumpGraph; }; CmdOptions g_cmdOptions; @@ -39,13 +42,15 @@ CmdOptions handleCmdLine(int argc, char **argv) CmdOptions options; options.dumpTempView = false; + options.dumpGraph = false; - const char* const optstring = "th"; + const char* const optstring = "gth"; while(true) { static struct option long_options[] = { + { "dumpGraph", no_argument, 0, 'g' }, { "tempView", no_argument, 0, 't' }, { "help", no_argument, 0, 'h' }, { 0, 0, 0, 0 } @@ -62,6 +67,11 @@ CmdOptions handleCmdLine(int argc, char **argv) switch (c) { + case 'g': + SWSS_LOG_NOTICE("Dumping graph"); + options.dumpGraph = true; + break; + case 't': SWSS_LOG_NOTICE("Dumping temp view"); options.dumpTempView = true; @@ -150,6 +160,226 @@ void print_attributes(size_t indent, const TableMap& map) } } +#define SAI_OBJECT_TYPE_PREFIX_LEN 16 + +void dumpGraph(const TableDump& td) +{ + SWSS_LOG_ENTER(); + + std::map<sai_object_id_t, const sai_object_type_info_t*> oidtypemap; + std::map<sai_object_type_t,const sai_object_type_info_t*> typemap; + + std::cout << "digraph \"SAI Object Dependency Graph\" {" << std::endl; + std::cout << "size=\"30,12\"; ratio = fill;" << std::endl; + std::cout << "node [style=filled];" << std::endl; + + // build object type map first + + std::set<sai_object_type_t> definedtypes; + + std::map<sai_object_type_t, int> usagemap; + + for (const auto& key: td) + { + sai_object_meta_key_t meta_key; + sai_deserialize_object_meta_key(key.first, meta_key); + + auto info = sai_metadata_get_object_type_info(meta_key.objecttype); + + typemap[info->objecttype] = info; + + if (!info->isnonobjectid) + oidtypemap[meta_key.objectkey.key.object_id] = info; + + if (definedtypes.find(meta_key.objecttype) != definedtypes.end()) + continue; + + definedtypes.insert(meta_key.objecttype); + } + + std::set<std::string> definedlinks; + + std::set<sai_object_type_t> ref; + std::set<sai_object_type_t> attrref; + + for (const auto& key: td) + { + sai_object_meta_key_t meta_key; + sai_deserialize_object_meta_key(key.first, meta_key); + + auto info = sai_metadata_get_object_type_info(meta_key.objecttype); + + // process non object id objects if any + for (size_t j = 0; j < info->structmemberscount; ++j) + { + const sai_struct_member_info_t *m = info->structmembers[j]; + + if (m->membervaluetype == SAI_ATTR_VALUE_TYPE_OBJECT_ID) + { + sai_object_id_t member_oid = m->getoid(&meta_key); + + auto member_info = oidtypemap.at(member_oid); + + if (member_info->objecttype == SAI_OBJECT_TYPE_SWITCH) + { + // skip link of SWITCH to non object id object types, since + // all of them contain switch_id + continue; + } + + std::stringstream ss; + + ss << std::string(member_info->objecttypename + SAI_OBJECT_TYPE_PREFIX_LEN) << " -> " + << std::string(info->objecttypename + SAI_OBJECT_TYPE_PREFIX_LEN) + << "[color=\"0.650 0.700 0.700\", style = dashed, penwidth=2]"; + + std::string link = ss.str(); + + if (definedlinks.find(link) != definedlinks.end()) + continue; + + definedlinks.insert(link); + + std::cout << link << std::endl; + } + } + + // process attributes for this object + + for (const auto&field: key.second) + { + const sai_attr_metadata_t *meta; + sai_deserialize_attr_id(field.first, &meta); + + if (!meta->isoidattribute || meta->isreadonly) + { + // skip non oid attributes and read only attributes + continue; + } + + sai_attribute_t attr; + + sai_deserialize_attr_value(field.second, *meta, attr, false); + + sai_object_list_t list = {0, NULL}; + + switch (meta->attrvaluetype) + { + case SAI_ATTR_VALUE_TYPE_OBJECT_ID: + list.count = 1; + list.list = &attr.value.oid; + break; + + case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_ID: + if (attr.value.aclfield.enable) + { + list.count = 1; + list.list = &attr.value.aclfield.data.oid; + } + break; + + case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_OBJECT_ID: + if (attr.value.aclaction.enable) + { + list.count = 1; + list.list = &attr.value.aclaction.parameter.oid; + } + break; + + case SAI_ATTR_VALUE_TYPE_OBJECT_LIST: + list = attr.value.objlist; + break; + + case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_LIST: + if (attr.value.aclfield.enable) + list = attr.value.aclfield.data.objlist; + break; + + case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_OBJECT_LIST: + if (attr.value.aclaction.enable) + list = attr.value.aclaction.parameter.objlist; + break; + + default: + SWSS_LOG_THROW("attr value type: %d is not supported, FIXME", meta->attrvaluetype); + } + + for (uint32_t i = 0; i < list.count; ++i) + { + sai_object_id_t oid = list.list[i]; + + if (oid == SAI_NULL_OBJECT_ID) + continue; + + // this object type is not root, can be in the middle or leaf + ref.insert(info->objecttype); + + auto attr_oid_info = oidtypemap.at(oid); + + std::stringstream ss; + + attrref.insert(attr_oid_info->objecttype); + + ss << std::string(attr_oid_info->objecttypename + SAI_OBJECT_TYPE_PREFIX_LEN) << " -> " + << std::string(info->objecttypename + SAI_OBJECT_TYPE_PREFIX_LEN) + << "[color=\"0.650 0.700 0.700\"]"; + + std::string link = ss.str(); + + if (definedlinks.find(link) != definedlinks.end()) + continue; + + definedlinks.insert(link); + + std::cout << link << std::endl; + } + + sai_deserialize_free_attribute_value(meta->attrvaluetype, attr); + } + } + + for (auto t: typemap) + { + auto ot = t.first; + auto info = t.second; + + auto name = std::string(info->objecttypename + SAI_OBJECT_TYPE_PREFIX_LEN); + + if (info->isnonobjectid) + { + std::cout << name << " [color=plum, shape = rect];\n"; + continue; + } + + if (ref.find(ot) != ref.end() && attrref.find(ot) != attrref.end()) + { + std::cout << name << " [color=\"0.650 0.500 1.000\"];\n"; + continue; + } + + if (ref.find(ot) != ref.end() && attrref.find(ot) == attrref.end()) + { + std::cout << name << " [color=\"0.355 0.563 1.000\", shape = rect];\n"; + continue; + } + + if (ref.find(ot) == ref.end() && attrref.find(ot) != attrref.end()) + { + std::cout << name << " [color=\"0.650 0.200 1.000\"];\n"; + continue; + } + + /* objects which are there but not referenced nowhere for example STP */ + + std::cout << name << " [color=\"0.650 0.200 1.000\" shape=rect];\n"; + } + + std::cout << "SWITCH -> PORT[dir=\"none\", color=\"red\", peripheries = 2, penwidth=2.0 , style = dashed ];" <<std::endl; + std::cout << "SWITCH [color=orange, shape = parallelogram, peripheries = 2];" <<std::endl; + std::cout << "PORT [color=gold, shape = diamond, peripheries=2];" << std::endl; + std::cout << "}" << std::endl; +} + int main(int argc, char ** argv) { swss::Logger::getInstance().setMinPrio(swss::Logger::SWSS_DEBUG); @@ -199,6 +429,13 @@ int main(int argc, char ** argv) } } + if (g_cmdOptions.dumpGraph) + { + dumpGraph(dump); + + return EXIT_SUCCESS; + } + for (const auto&key: dump) { auto start = key.first.find_first_of(":"); @@ -213,4 +450,6 @@ int main(int argc, char ** argv) std::cout << std::endl; } + + return EXIT_SUCCESS; }