diff --git a/common/Makefile.am b/common/Makefile.am index 53ab345f3..89ea20385 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -111,7 +111,8 @@ common_libswsscommon_la_SOURCES = \ common/c-api/table.cpp \ common/c-api/logger.cpp \ common/c-api/events.cpp \ - common/performancetimer.cpp + common/performancetimer.cpp \ + common/debugframework.cpp common_libswsscommon_la_CXXFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(LIBNL_CFLAGS) $(CODE_COVERAGE_CXXFLAGS) common_libswsscommon_la_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(LIBNL_CPPFLAGS) $(CODE_COVERAGE_CPPFLAGS) diff --git a/common/debugframework.cpp b/common/debugframework.cpp new file mode 100644 index 000000000..a770c998c --- /dev/null +++ b/common/debugframework.cpp @@ -0,0 +1,464 @@ +#include "debugframework.h" + +#include +#include +#include +#include +#include +#include +#include "syslog.h" +#include "schema.h" +#include "select.h" +#include "dbconnector.h" +#include "redisclient.h" +#include "producerstatetable.h" +#include "subscriberstatetable.h" +#include "notificationconsumer.h" +#include "notificationproducer.h" + +#define ALL_COMPONENTS "all" +#define ASSERT_FILE "/var/log/assert_btrace.log" +#define BTRACE_BUF_SIZE 100 + +namespace swss { + + +DebugFramework &DebugFramework::getInstance() +{ + static DebugFramework m_debugframework; + return m_debugframework; +} + +DebugFramework::DebugFramework() { + DBConnector db(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + m_configParams = std::unique_ptr(new Table(&db, APP_DEBUGFM_CONFIG_TABLE_NAME)); +} + +DebugFramework::~DebugFramework() { +} + +void DebugFramework::linkWithFrameworkNoThread(const std::string &componentName) +{ + SWSS_LOG_ENTER(); + updateRegisteredComponents(componentName); +} + +void DebugFramework::linkWithFramework(const std::string &componentName, const DumpInfoFunc funcPtr) +{ + SWSS_LOG_ENTER(); + if (funcPtr == NULL) + { + /* log error and return */ + SWSS_LOG_ERROR("Invalid arguments passed to debug framework"); + return; + } + + // Add the component to the registry + updateRegisteredComponents(componentName); + + // Now create a thread and let the rest be handled there + std::thread t(DebugFramework::runnableThread, componentName, funcPtr); + t.detach(); +} + +void DebugFramework::updateRegisteredComponents(const std::string &component) +{ + DBConnector db(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + + Table table(&db, APP_DEBUG_RCOMPONENT_TABLE_NAME); + FieldValueTuple fv("THREAD", "noOp"); + std::vectorfieldValues = { fv }; + table.set(component, fieldValues); +} + +NO_RET_TYPE void DebugFramework::runnableThread(const std::string &componentName, const DumpInfoFunc funcPtr) +{ + SWSS_LOG_ENTER(); + Select s; + DBConnector dumpDb(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector doneDb(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + NotificationProducer p(&doneDb, "APPDEBUGCOMPDONETABLENAME"); + NotificationConsumer dumpTrigger(&dumpDb, "APPDEBUGCOMPONENTTABLENAME"); + + s.addSelectable(&dumpTrigger); + + while (true) + { + Selectable *temp = nullptr; + int ret = s.select(&temp, -1); + + if ( (ret == Select::ERROR) || (ret == Select::TIMEOUT)) + { + SWSS_LOG_DEBUG("%s select error %s", __PRETTY_FUNCTION__, strerror(errno)); + continue; + } + + if (temp == (Selectable *)&dumpTrigger) + { + /* match component name and invoke dump */ + std::deque entries; + dumpTrigger.pops(entries); + + for (auto entry: entries) + { + std::string key = kfvKey(entry); + if (key == componentName) + { + FieldValueTuple fvResp("RESPONSE", "SUCCESS"); + try{ + (*funcPtr)(componentName, entry); + }catch(...){ + SWSS_LOG_ERROR("%s dump routine failed.", componentName.c_str()); + fvResp.second = "FAILURE"; + std::vectorfieldValues = { fvResp }; + p.send(componentName, componentName, fieldValues); + + throw "Registered dump routine returned error"; + } + + /* Inform done with same key */ + std::vectorfieldValues = { fvResp }; + p.send(componentName, componentName, fieldValues); + } + } + } + } // end while loop +} + +void DebugFramework::invokeTrigger(const std::string &componentName, std::string argList) +{ + SWSS_LOG_ENTER(); + SWSS_LOG_DEBUG("DebugFramework invokeTrigger called for %s with args %s", componentName.c_str(), argList.c_str()); + + auto& dbgfm = getInstance(); + + //parse and construct KeyOpFieldsValuesTuple + KeyOpFieldsValuesTuple kco; + std::vector attrs; + + std::stringstream ss(argList); + std::string pair; + while(std::getline(ss, pair, ';')) + { + std::string f,v; + long unsigned int pos = pair.find(':'); + if (pos == std::string::npos) + { + continue; + } + + f = pair.substr(0, pos); + v = pair.substr(pos+1); + + FieldValueTuple fvp(f,v); + + //seperate out args for framework consumption + if ( f == "TARGET") + { + dbgfm.setTarget(v); + continue; + } + + if ( f == "ACTION") + { + dbgfm.setPostAction(v); + continue; + } + attrs.push_back(fvp); + } + + kfvFieldsValues(kco) = attrs; + kfvKey(kco) = componentName; + kfvOp(kco) = "SET"; + + relayTrigger(componentName, kco); +} + +void DebugFramework::relayTrigger(const std::string &componentName, KeyOpFieldsValuesTuple args) +{ + SWSS_LOG_ENTER(); + DBConnector db(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + Table table(&db, APP_DEBUG_RCOMPONENT_TABLE_NAME); + + DBConnector dumpDb(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + NotificationProducer producer(&dumpDb, "APPDEBUGCOMPONENTTABLENAME"); + + std::vector components; + std::vector keys; + table.getKeys(keys); + + if (componentName == ALL_COMPONENTS) + { + for (const auto &key: keys) + { + SWSS_LOG_DEBUG("DebugFramework sending notification for %s", key.c_str()); + producer.send(key, key, kfvFieldsValues(args)); + } + } + else + { + for (const auto &key: keys) + { + if (componentName == key) + { + SWSS_LOG_DEBUG("DebugFramework sending notification for %s", key.c_str()); + producer.send(key, key, kfvFieldsValues(args)); + break; + } + } + } + // Now create a thread and wait for done notifications + std::thread t(DebugFramework::listenDoneEvents, componentName); + t.detach(); +} + +void DebugFramework::listenDoneEvents(const std::string &componentName) +{ + SWSS_LOG_ENTER(); + Select sel; + DBConnector db(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + Table table(&db, APP_DEBUG_RCOMPONENT_TABLE_NAME); + + DBConnector doneDb(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + NotificationConsumer doneIndication(&doneDb, "APPDEBUGCOMPDONETABLENAME"); + + sel.addSelectable(&doneIndication); + + std::vector components; + std::vector keys; + table.getKeys(keys); + + if (componentName == ALL_COMPONENTS) + { + for (const auto &key: keys) + { + components.push_back(key); + } + } + else + { + for (const auto &key: keys) + { + if (componentName == key) + { + components.push_back(key); + break; + } + } + } + // Now create a thread and wait for done notifications + // Wait for done notifications from component(s) + while (!components.empty()) + { + Selectable *temp = nullptr; + int ret = sel.select(&temp, 6000); + + if ( (ret == Select::ERROR) || (ret == Select::TIMEOUT)) + { + SWSS_LOG_ERROR("%s select error %s", __PRETTY_FUNCTION__, strerror(errno)); + break; + } + + if (temp == (Selectable *)&doneIndication) + { + std::vector entries; + std::string op, data; + doneIndication.pop(op, data, entries); + + SWSS_LOG_DEBUG("DebugFramework done indication from %s", data.c_str()); + + for (unsigned i = 0; i != components.size(); ++i) + { + if (data == components[i]) + { + components.erase(components.begin()+i); + break; + } + } + } + } // end while + + //trigger the post action + SWSS_LOG_DEBUG("DebugFramework triggered post action script"); + + //performPost(); TBD +} + +void DebugFramework::dumpBegin(const std::string &component) +{ + auto& dbgfm = getInstance(); + DebugFramework::TargetLocation t = dbgfm.getTarget(); + + if (t == SWSS_FILE) + { + // open a file in append mode and keep it open + std::fstream *outfile; + std::string options("/var/log/debugframework_" + component + "_debug.log"); + outfile = new std::fstream(); + outfile->open(options, std::fstream::out | std::fstream::app); + + if (!outfile->is_open()) + { + SWSS_LOG_ERROR("cannot open file"); + std::string s("SWSS_SYSLOG"); + dbgfm.setTarget(s); + delete outfile; + return; + } + + // hold on to the fd + dbgfm.m_compFds.insert(std::make_pair(component, outfile)); + } +} + +void DebugFramework::dumpEnd(const std::string &component) +{ + auto& dbgfm = getInstance(); + DebugFramework::TargetLocation t = dbgfm.getTarget(); + + if (t == SWSS_FILE) + { + auto itr = dbgfm.m_compFds.find(component); + if (itr != dbgfm.m_compFds.end()) + { + std::fstream *out = itr->second; + out->close(); + delete out; + dbgfm.m_compFds.erase(itr); + } + } + return; +} + +void DebugFramework::dumpWrite(const std::string &component, const char *fmt, ...) +{ + auto& dbgfm = getInstance(); + + va_list ap; + va_start(ap, fmt); + + if (dbgfm.getTarget() == SWSS_SYSLOG) + { + vsyslog(LOG_DEBUG, fmt, ap); + va_end(ap); + } + else + { + char buffer[0x1000]; + vsnprintf(buffer, 0x1000, fmt, ap); + va_end(ap); + + std::fstream *f; + auto itr = dbgfm.m_compFds.find(component); + if (itr != dbgfm.m_compFds.end()) + { + f = itr->second; + *f << buffer << std::endl; + } + } +} + +DebugFramework::TargetLocation DebugFramework::getTarget() +{ + std::string key("TARGET"), val; + m_configParams->hget(key, key, val); + if (val == "SWSS_SYSLOG") + return SWSS_SYSLOG; + else + return SWSS_FILE; +} + +void DebugFramework::setTarget(const std::string &val) +{ + std::string key("TARGET"); + m_configParams->hset(key, key, val); +} + +DebugFramework::PostAction DebugFramework::getPostAction() +{ + std::string key("ACTION"), val; + m_configParams->hget(key, key, val); + if (val == "UPLOAD") + return UPLOAD; + else + return COMPRESS; +} + +void DebugFramework::setPostAction(const std::string &val) +{ + std::string key("ACTION"); + m_configParams->hset(key, key, val); +} + +void DebugFramework::setAssertAction(const std::string val) +{ + auto& dbgfm = getInstance(); + std::string key("ASSERTACT"); + dbgfm.m_configParams->hset(key, key, val); +} + +DebugFramework::AssertAction DebugFramework::getAssertAction() +{ + std::string key("ASSERTACT"), val; + m_configParams->hget(key, key, val); + if (val == "SYSLOG") + return SYSLOG; + else if(val == "DUMP") + return DUMP; + else if(val == "BTRACE") + return BTRACE; + else + return ABORT; +} + +#if 0 +void DebugFramework::customAssert(bool exp, AssertAction act, const char *func, const unsigned line) +{ + SWSS_LOG_ENTER(); + auto& dbgfm = getInstance(); + if (exp) + { + return; + } + + if (act == UNSPECIFIED) + { + act = dbgfm.getAssertAction(); + } + + // log to syslog irrespective of action + syslog(LOG_CRIT, "Assertion failed at %s : %d\n", func, line); + + switch (act) + { + case SYSLOG: + break; + + // break not added intentionally + case DUMP: + { + std::string comp("all"); + std::string args("DETAIL:FULL;"); + invokeTrigger(comp, args); + __attribute__((__fallthrough__)); + } + + case BTRACE: + { + std::FILE *fp = fopen(ASSERT_FILE, "w"); + void * buffer[BTRACE_BUF_SIZE]; + const int calls = backtrace(buffer, sizeof(buffer) / sizeof(void *)); + backtrace_symbols_fd(buffer, calls, fileno(fp)); + } + break; + + case ABORT: + default: + { + abort(); + } + break; + } // end switch +} +#endif //if 0 +} diff --git a/common/debugframework.h b/common/debugframework.h new file mode 100644 index 000000000..755a7fef0 --- /dev/null +++ b/common/debugframework.h @@ -0,0 +1,118 @@ +#ifndef SWSS_COMMON_DEBUGFRAMEWORK_H +#define SWSS_COMMON_DEBUGFRAMEWORK_H + +#include +#include +#include +#include +#include "dbconnector.h" +#include "table.h" + +using namespace swss; + +#if 0 +#ifdef assert +#undef assert +#define assert(exp) DebugFramework::customAssert(exp, DebugFramework::AssertAction::UNSPECIFIED, __PRETTY_FUNCTION__, __LINE__) +#endif + +#define SWSS_DEBUG_ASSERT_LOG(exp) DebugFramework::customAssert(exp, DebugFramework::AssertAction::SYSLOG, __PRETTY_FUNCTION__, __LINE__) +#define SWSS_DEBUG_ASSERT_DUMP(exp) DebugFramework::customAssert(exp, DebugFramework::AssertAction::DUMP, __PRETTY_FUNCTION__, __LINE__) +#define SWSS_DEBUG_ASSERT_BTRACE(exp) DebugFramework::customAssert(exp, DebugFramework::AssertAction::BTRACE, __PRETTY_FUNCTION__, __LINE__) +#define SWSS_DEBUG_ASSERT_ABORT(exp) DebugFramework::customAssert(exp, DebugFramework::AssertAction::ABORT, __PRETTY_FUNCTION__, __LINE__) +#endif + +namespace swss { + +#define SWSS_DEBUG_PRINT swss::DebugFramework::getInstance().dumpWrite +#define SWSS_DEBUG_PRINT_BEGIN swss::DebugFramework::getInstance().dumpBegin +#define SWSS_DEBUG_PRINT_END swss::DebugFramework::getInstance().dumpEnd + +typedef void (*DumpInfoFunc)(std::string, KeyOpFieldsValuesTuple); + +class DebugFramework +{ +public: + static DebugFramework &getInstance(); /* To have only one instance aka singleton */ + + /* This function will internally create a runnable thread, + * create selectable to receive events and waits on events + */ + static void linkWithFramework(const std::string &componentName, const DumpInfoFunc funcPtr); + + /* This function will not create any thread and calling component + * need to handle triggers via Redis to invoke DumpInfoFunc + */ + static void linkWithFrameworkNoThread(const std::string &componentName); + + /* This function will create FieldValueTuples and publish to Redis + * args: comma seperated strings opaque to framework passed to components + */ + static void invokeTrigger(const std::string &componentName, std::string args); + + void dumpBegin(const std::string &component); + void dumpEnd(const std::string &component); + void dumpWrite(const std::string &component, const char *fmt, ...) +#ifdef __GNUC__ + __attribute__ ((format (printf, 3, 4))) +#endif + ; + + enum AssertAction + { + SYSLOG, + DUMP, + BTRACE, + ABORT, + UNSPECIFIED + }; + static void setAssertAction(const std::string val); +#if 0 + static void customAssert(bool exp, AssertAction act, const char *func, const unsigned line); +#endif + +private: + DebugFramework(); + ~DebugFramework(); + DebugFramework(const DebugFramework&); + DebugFramework &operator=(const DebugFramework&); + + static void listenDoneEvents(const std::string &componentName); + static void relayTrigger(const std::string &componentName, KeyOpFieldsValuesTuple args); + static void updateRegisteredComponents(const std::string &component); + + enum PostAction + { + COMPRESS, + UPLOAD + }; + + enum TargetLocation + { + SWSS_SYSLOG, + SWSS_FILE + }; + + TargetLocation getTarget(); + void setTarget(const std::string &t); + PostAction getPostAction(); + void setPostAction(const std::string &p); + + std::vector m_registeredComps; + std::map m_compFds; + std::unique_ptr
m_configParams; + + TargetLocation m_targetLocation; + PostAction m_postAction; + +#ifndef NO_RET_TYPE +#define NO_RET_TYPE [[noreturn]] +#endif + NO_RET_TYPE static void runnableThread(const std::string &componentName, const DumpInfoFunc funcPtr); + + AssertAction getAssertAction(); +}; + +} + +#endif /* SWSS_COMMON_DEBUGFRAMEWORK_H */ diff --git a/common/schema.h b/common/schema.h index e71cc7c5d..1fc9cfc7f 100644 --- a/common/schema.h +++ b/common/schema.h @@ -97,6 +97,11 @@ namespace swss { #define APP_SFLOW_SESSION_TABLE_NAME "SFLOW_SESSION_TABLE" #define APP_SFLOW_SAMPLE_RATE_TABLE_NAME "SFLOW_SAMPLE_RATE_TABLE" +#define APP_DEBUGFM_CONFIG_TABLE_NAME "DEBUGFM_CONFIG_TABLE" +#define APP_DEBUG_RCOMPONENT_TABLE_NAME "DEBUG_RCOMPONENT_TABLE" +#define APP_DEBUG_COMPONENT_TABLE_NAME "DEBUG_COMPONENT_TABLE" +#define APP_DEBUG_COMP_DONE_TABLE_NAME "DEBUG_COMP_DONE_TABLE" + #define APP_NAT_TABLE_NAME "NAT_TABLE" #define APP_NAPT_TABLE_NAME "NAPT_TABLE" #define APP_NAT_TWICE_TABLE_NAME "NAT_TWICE_TABLE" @@ -122,6 +127,12 @@ namespace swss { #define APP_STP_MST_PORT_TABLE_NAME "STP_MST_PORT_TABLE" #define APP_STP_INST_PORT_FLUSH_TABLE_NAME "STP_INST_PORT_FLUSH_TABLE" +/* Multicast related APP_DB Tables */ +#define APP_L2MC_VLAN_TABLE_NAME "L2MC_VLAN_TABLE" +#define APP_L2MC_SUPPRESS_TABLE_NAME "L2MC_SUPPRESS_TABLE" +#define APP_L2MC_MEMBER_TABLE_NAME "L2MC_MEMBER_TABLE" +#define APP_L2MC_MROUTER_TABLE_NAME "L2MC_MROUTER_TABLE" + #define APP_SAG_TABLE_NAME "SAG_TABLE" @@ -398,6 +409,12 @@ after libswsscommon deb make. #define CHASSIS_APP_SYSTEM_NEIGH_TABLE_NAME "SYSTEM_NEIGH" #define CHASSIS_APP_LAG_TABLE_NAME "SYSTEM_LAG_TABLE" #define CHASSIS_APP_LAG_MEMBER_TABLE_NAME "SYSTEM_LAG_MEMBER_TABLE" + +#define CFG_L2MC_TABLE_NAME "L2MC" +#define CFG_L2MC_SUPPRESS_TABLE_NAME "L2MC_SUPPRESS" +#define CFG_L2MC_MROUTER_TABLE_NAME "L2MC_MROUTER" +#define CFG_L2MC_STATIC_TABLE_NAME "L2MC_STATIC_MEMBER" +#define CFG_L2MC_STATIC_GROUP_TABLE "L2MC_STATIC_GROUP" /***** STATE DATABASE *****/ #define STATE_SWITCH_CAPABILITY_TABLE_NAME "SWITCH_CAPABILITY" @@ -495,6 +512,9 @@ after libswsscommon deb make. #define STATE_BGP_PEER_CONFIGURED_TABLE_NAME "BGP_PEER_CONFIGURED_TABLE" +#define STATE_L2MC_MEMBER_TABLE_NAME "L2MC_STATE_MEMBER_TABLE" +#define STATE_L2MC_MROUTER_TABLE_NAME "L2MC_STATE_MROUTER_TABLE" + // ACL table and ACL rule table #define STATE_ACL_TABLE_TABLE_NAME "ACL_TABLE_TABLE" #define STATE_ACL_RULE_TABLE_NAME "ACL_RULE_TABLE" diff --git a/pyext/swsscommon.i b/pyext/swsscommon.i index a7e31f52e..8b9040427 100644 --- a/pyext/swsscommon.i +++ b/pyext/swsscommon.i @@ -60,6 +60,7 @@ #include #include #include "interface.h" +#include "debugframework.h" %} %include @@ -361,3 +362,4 @@ T castSelectableObj(swss::Selectable *temp) %include "status_code_util.h" #include "redis_table_waiter.h" %include "restart_waiter.h" +%include "debugframework.h"