From 9e7a0ef2bac24f82f646fa01ff6d68a49597df52 Mon Sep 17 00:00:00 2001 From: Anil Sadineni Date: Mon, 10 Jun 2019 06:45:45 -0700 Subject: [PATCH 1/3] Documents: Debug Framework Design Spec * A framework that allows components to register and dump running snapshots of component internals using dump routines. * Handle assert conditions to collect more info. Signed-off-by: Anil Sadineni anil.sadineni@broadcom.com --- doc/debug_framework_design_spec.md | 405 +++++++++++++++++++++++ images/debug_framework_block_diagram.png | Bin 0 -> 23783 bytes images/debug_framework_flow_diagram.png | Bin 0 -> 44736 bytes 3 files changed, 405 insertions(+) create mode 100644 doc/debug_framework_design_spec.md create mode 100644 images/debug_framework_block_diagram.png create mode 100644 images/debug_framework_flow_diagram.png diff --git a/doc/debug_framework_design_spec.md b/doc/debug_framework_design_spec.md new file mode 100644 index 0000000000..138544035d --- /dev/null +++ b/doc/debug_framework_design_spec.md @@ -0,0 +1,405 @@ +# Debug Framework in SONiC +# Design Specification +#### Rev 0.2 + +# Table of Contents + * [List of Tables](#list-of-tables) + * [Revision](#revision) + * [About This Manual](#about-this-manual) + * [Scope](#scope) + * [Definition/Abbreviation](#definitionabbreviation) + * [1. Requirements ](#1-requirements) + * [2. Functional Description](#2-functional-description) + * [3. Design](#3-design) + * [4. Flow Diagrams](#4-flow-diagrams) + * [5. Serviceability and Debug](#5-serviceability-and-debug) + * [6. Warm Boot Support](#6-warm-boot-support) + * [7. Scalability](#7-scalability) + * [8. Unit Test](#8-unit-test) + +# List of Tables +[Table 1: Abbreviations](#table-1-abbreviations) +[Table 2: Configuration options and defaults](#table-2-defaults) + +# Revision +| Rev | Date | Author | Change Description | +|-----|------------|-----------------------------|----------------------------| +| 0.1 | 05/19/2019 | Anil Sadineni, Laveen T | Initial version | +| 0.2 | 05/31/2019 | Anil Sadineni, Laveen T | Address comments from Ben | + +# About this Manual +This document provides general information about the debug framework and additional debug features implementation in SONiC. + +# Scope +Current implementation of SONiC offers logging utility and utilities to display the contents of Redis. In an effort to enhance debug ability, A new debug framework is added with below functionality: + * Provide a framework that allows components to register and dump running snapshots of component internals using dump routines. + * Handle assert conditions to collect more info. + * Implement dump routines in OrchAgent using debug framework. + +Additionally, below debug features are done to enhance debug ability. + * Enhance existing show tech-support utility. + * Add additional scripts to enforce policies on debug related files. + +This document describes the above scope. + +# Definition/Abbreviation + +# Table 1: Abbreviations + +| **Term** | **Meaning** | +|-------------------|-------------------------------------------------------------------------------------------------------| +| Singleton | The singleton pattern is a design pattern that restricts the instantiation of a class to one object. | | + +# 1 Requirements +## 1.1 Functional Requirements +1. A framework to allow component registration and subsequently trigger dump routines. Triggers are from administrative CLI commands or from assert for this release. +2. Assert routines for production environment to delay the reload for gathering information or notify admin and delegate the decision of reload to admin. +3. Enhance tech-support to collect comprehensive info. +4. Add a helper script to facilitate an upload of the debug info in a non-intrusive way. +5. Rate limit and redirect logs for better debug ability without affecting stability of running processes. + +## 1.2 Configuration and Management Requirements +1. Need a config file to configure default options on the framework actions. +2. New CLI for triggering dump routines of Daemons. +3. New CLI to display drop/error interface counters. +4. New CLI to trigger upload of debug information. +5. Existing CLI is extended to filter the display counters on a specific interface. + +## 1.3 Scalability Requirements +None + +## 1.4 Warm Boot Requirements +None + +# 2 Functionality +## 2.1 Functional Description + +**1. Dump Routines** +- Framework facilitates to collect developer-level component internal data state under certain system conditions. +- The triggers to dump information might come from administrative commands, from assert or in response to some critical notifications. +- Framework provides interface for components to register and an interface for utilities to trigger the components to invoke dump routines. + +**2. Assert utility** +- This utility adds some data collection of certain system information when an assert condition is hit. + +**3. Tech-support enhancements** +- Current tech-support collects exhaustive information. The tech-support is enhanced to additionally collect STATE_DB dump, dump of ASIC specifics, filter and redirect critical logs to persistent log file for quick reference. + +**4. Helper Scripts** +New utility scripts are provided to: +- Print "headline" information for a quick summary of the system state. +- Facilitate upload of the collected information. +- Enforce policy on the number of debug-related files. + +# 3 Design +## 3.1 Overview +### 3.1.1 Framework for dump routines + +**Block Diagram** +![Debug Framework Block Diagram](images/debug_framework_block_diagram.png) + + +Debug framework provides an API for components to register with the framework. It also provides interface for administrator, assert utilities, error handling functions to invoke framework actions. Framework uses Redis Publish/Subscribe implementation for communicating the trigger message from entity invoking the trigger to the registered components. Framework actions are configurable. These actions are applicable uniformly to all registered components. Please refer [Table-2](#table-2) for configurable actions and default actions. + +Debug Framework itself will not have an executing context. Framework actions are performed in the registered component's thread context. Trigger APIs are executed in the context of invoking utility or invoking process. Debug Framework will be implemented as a Singleton meaning there is going to be only one instance of framework in the system. +Please refer [Flow Diagram](#4-flow-diagrams) for execution sequence. + +**Framework Responsibilities** + +Each registered component provides the routines to dump the component-specific state via callback functions. It is then the responsibility of the framework to +- Monitor for specific system triggers or listen on framework trigger APIs invocation +- Process the trigger and call appropriate dump routines +- Manage the output location +- Follow up with post actions like redirect summary to console/ compress the files and upload the file. +- Report summary or upload complete info is done either to a pre-defined location or a configured location. + +**Component Responsibilities** + +Components implement dump routines following below guidelines +- Implement a function of type DumpInfoFunc. + `typedef std::function< void (std::string componentName, KeyOpFieldsValuesTuple args) > DumpInfoFunc;` +- Within the function, dump the information into a string buffer and use a macro `SWSS_DEBUG_PRINT`. Macro will further process the information based on intended framework action. +- Handle KeyOpFieldsValuesTuple as argument. Arguments like dumpType and pass thru arguments that instruct the level of info dump ( short summary/specific info/filtered output/full info) should be handled by components. These arguments are opaque to framework. Additional optional arguments like output location and post-actions are for the framework consumption. +- Interpreting arguments is at discretion of components. Components may choose to dump same info for all trigger types ignoring input arguments. +- Dump routines registered with framework should be thread safe. Necessary synchronization needs to be ensured. + +**Component registration options** + +Framework only manages component registration and invocation of callback functions. Framework itself does not have an executing context. To execute the callback function, components have an option to instruct framework either to create an additional thread that runs in the registrant context or not create a thread but merely publish an event to Redis and expect component to handle the event. +Components have an option to register with debug framework using any one of the below APIs: +Option #1. `SWSSDebug::linkWithFramework()` +Option #2. `SWSSDebug::linkWithFrameworkNoThread()` + +Option #1 is preferred. However if a component already handles RedisDB events and doesn't want an additional thread (to avoid thread synchronization issues) might opt for option #2 + +Option #1 ( Framework creates a Thread ) +--------- +Components register dump routine with debug framework using the API: +`SWSSDebug::linkWithFramework(std::string componentName, std::function componentDumpFuncPtr);` + +Framework does the following: +- Update framework internal data structure to save the registration information of components. +- Create a `swss::Selectable` object. +- Create a subscriber to a table/channel in DebugDumpReq database in RedisDB with a unique channel name for receiving triggers. +- Create a thread and wait on events/triggers. +- Invoke component specific dump routine in this thread context on receipt of a trigger. +- Handle the configured framework action within SWSS_DEBUG_PRINT. For example: Redirect the output to syslog or component specific log file. +- Update Redis DB to indicate callback action completion. +- Post-processing of information based on the configured framework action. For example: bundle the information and upload the information to a server. + +Option #2 ( Framework does not create a Thread ) +--------- +Components register dump routine with debug framework using the API: +`SWSSDebug::linkWithFrameworkNoThread(std::string componentName);` +Framework will limit the job to the following: +- Update framework internal data structure to save the registration information of components. +- Handle the configured framework action within SWSS_DEBUG_PRINT. For example: Redirect the output to syslog or component specific log file. +- Post-processing of information based on the configured framework action. For example: bundle the information and upload the information to a server. + +The below activity is delegated to the component +- Optional creation of a swss::Selectable object. +- Create a subscriber instance to DebugDumpReq database in RedisDB for receiving triggers. +- Optionally create a thread to wake on triggers. +- Invoke component specific dump routine on receipt of a trigger. +- Update Redis DB to indicate callback action completion. + + +**Implementation brief** +Framework will be implemented in C++ as a class in SWSS namespace. + +``` + class SWSSDebug + { + public: + static SWSSDebug &getInstance(); // To have only one instance aka singleton + + typedef std::function< void (std::string componentName, KeyOpFieldsValuesTuple args) > DumpInfoFunc; + + // Component registration option 1 API + static void linkWithFramework(componentName, DumpInfoFunc); + + // Component registration option 2 API + static void linkWithFrameworkNoThread(componentName); + + // Interface to invoke triggers + static void invokeTrigger(std::string componentName, std::string args); + + // API to configure the framework action. Function will be overloaded with arguments + static void setFrameworkAction(); + + private: + SWSSDebug(); + std::map m_registeredComps; + std::map m.configParams; + + // Thread in while(1) for handling triggers and invoking callbacks + [[ noreturn ]] void runnableThread(); + }; +``` + +#### 3.1.1.1 Integration of OrchAgent with framework +##### 3.1.1.1.1 Triggers to OrchAgent +OrchAgent already has a thread that handles events from RedisDB and hence will register with framework (option #2) using below API: + +``` + void OrchAgentDump(std::string componentName, std::vectorFieldsValuesTuple args) + { + if routeOrch + { + SWSS_DEBUG_PRINT(); + } + else if NeighOrch + { + SWSS_DEBUG_PRINT(); + } + } + + SWSSDebug::linkWithFrameworkNoThread(std::string componentName, OrchAgentDump); +``` + +The component subscribes to DebugDumpReq database in RedisDB. OrchAgent then gets a `swss::Selectable` object and updates the existing selectable map. When a trigger is received, OrchAgent executes OrchAgentDump() in the context of its own thread. + +##### 3.1.1.1.2 Triggers to OrchAgent +show commands will act as triggers for OrchAgent. +Syntax: `show debug .... ` + +##### 3.1.1.1.3 Sample CLI +*RouteOrch:* + +| Syntax | Description | +|---------------------------------------------------------|---------------------------------------------------------------------------------------------| +|show debug routeOrch routes [all] [ip_prefix] | Dump all Routes or routes specific to a prefix | +|show debug routeOrch nhgrp [addr-1] [addr-2] [addr-3] | NexthopGroup/ECMP info from RouteOrch::m_syncdNextHopGroups | +|show debug routeOrch counters | Dump internal counters associated with the component | +|show debug routeOrch dumpall | Translates to list of APIs to dump, which can be used for specific component based triggers | + +*NeighborOrch:* + +| Syntax | Description | +|---------------------------|--------------------| +|show debug NeighOrch nhops | Dump Nexthops info | +|show debug NeighOrch neighs| Dump Neighbor info | + +##### 3.1.1.1.4 Sample Databases + +``` +;RouteOrch - Channel/Table Name +; +key = RouteOrch | Routes ; Dump routes map in RouteOrch +vrf = vrf_name ; vrf name for which to dump the routes info +prefix = v4/v6 address ; search for specific ip prefix +idx = seq_id/timestamp ; to uniquely identify this msg/request + +key = RouteOrch | nhgrp ; Dump Nexthop Group table in RouteOrch +prefix = v4/v6 address ; list of nexthop ipaddress +idx = seq_id/timestamp ; identifier + +;NeighborOrch - Channel/Table Name +; +Key = NeighborOrch | nhops ; Dump syncdnexthops map in NeighborOrch + +``` + +#### 3.1.1.3 Configuring Framework +Debug framework will initialize with below default parameters and shall be considered if options are not specified as arguments in the triggers. +The defaults are read from the debugFm.ini file. + +# Table 2: Configuration Options and Defaults + +| **Parameter** | **options** | **Default** | +|-------------------------------|-------------------------------------|-------------------------------| +| DumpLocation | "syslog" / "filename" | /var/log/_dump.log | +| TargetComponent | "all" / "componentname" | all | +| Post-action | "upload" / "compress-rotate-keep" | compress-rotate-keep | +| Server-location | ipaddress | 127.0.0.1 | +| Upload-method | "scp" / "tftp" | tftp | +| Upload-directory | "dir_path" / "default_dir" | default_dir | + + +### 3.1.2 Assert Framework + +#### 3.1.2.1 Overview +Asserts are added in the program execution sequence to confirm that the data/state at a certain point is valid/true. During developement, if the programming sequence fails in an assert condition then the program execution is stopped by crash/exception. In Production code, asserts are normally removed. This framework enhances/extendes the assert to provide more debug details when an assert fails. + +Classify assert failure conditions based on following types, assert() will have type and the module as additional arguments +- DUMP: Invokes the debug framework registered callback API corresponding to the module +- BTRACE: Prints bracktrace and continue +- SYSLOG: Update syslog with the assert failure +- ABORT: Stop/throw exception + + +#### 3.1.2.2 PsuedoCode: + +``` + class dbgAssert: public SWSSDebug + { + public: + enum Type { + SYSLOG, // recoverable: log to trace error conditions + DUMP, // Functional impact: No depencies with other module, System may continue to operate + BTRACE, // Unexpected flow: Print backtrace and continue + ABORT, // System Fault: System reached a state where it cannot recover / should not continue + UNKNOWN + }; + + inline assert(exp) + { + syslog("Assert:expression", __FUNCTION__, __FILE__); + } + + void assert(Type type, string module, boolean expr); + } + + void dbgAssert::assert(Type type, string module, boolean expr) + { + if(!expr) + if(type == BTRACE) + backtrace(); + if(type == DUMP) + SWSSDebug::dump(module>); + else if(type == ABORT) + SWSSDebug::dump(module>); + abort(); + } + + gDbgAssert = new dbgAssert(); + + #ifdef assert + #undef assert + #define assert(exp) gDbgAssert::assert(exp) + #endif +``` + +## 3.2 DB Changes + +### 3.2.1 APP DB + +**Dump table** + +For triggering dump routines + +``` +; Defines schema for DEBUG Event attributes +key = DAEMON:daemon_name ; daemon_session_name is unique identifier; +; field = value +DUMP_TYPE = "short" / "full" ; summary dump or full dump +DUMP_TARGET = "default" / "syslog" ; syslog or specified file +PASSTHRU_ARGS = arglist ; zero or more strings separated by "," + +``` +**Dump done table** +``` +; Defines schema for DEBUG response attributes +key = DAEMON:daemon_name ; daemon_session_name is unique identifier; +; field = value +RESPONSE_TYPE = < enum DumpResponse::result > ; pending/failure/successful +``` + +## 3.3 CLI + +### 3.3.1 Show Commands + +|Syntax | Description | +|-------------------------------|-------------------------------------------------------------------------------| +|show debug all | This command will invoke dump routines for all components with default action | +|show debug < component > | This command will invoke dump routine for specific component | +|show debug actions < options > | This command will display the configured framework actions | + +### 3.3.2 Debug/Error Interface counters +show interfaces pktdrop is added to display debug/error counters for all interfaces. + +# 4 Flow Diagrams + +![Framework and component interaction](images/debug_framework_flow_diagram.png) + +# 5 Serviceability and Debug +None + +# 6 Warm Boot Support + +No change. + + + +# 7 Scalability +No Change + + +# 8 Unit Test +### 8.1.1 Debug Framework +1. Verify that Framework provides a shared library (a) for components to link and register with the framework and (b) for the utilities to issue triggers. +2. Verify that on a running system, without any triggers framework does not add any CPU overhead. +3. Verify that dump routines are invoked when a trigger is issued with all arguments from the trigger utilities/ show debug commands. +4. Verify that number of subscribers is incremented using redis-cli after successful registration of component. +5. Verify that SWSS_DEBUG_DUMP macro writes to dump location specified in the .ini file when the target location for dump is not specified or specified as "default". +6. Verify that SWSS_DEBUG_DUMP macro writes to SYSLOG when DUMP_TARGET in trigger is mentioned as syslog. +7. Verify that if the utility function triggers dump for all, framework loops through the registrations and updates the table for each component. +8. Verify that framework executes the configured post action. +9. Verify the behaviour of framework when some component doesnt send a DONE message. +10. Verify that framework handles multiple consecutive triggers and handles triggers independently. + +# 9 Internal Design Information +Internal BRCM information to be removed before sharing with the community + +Go back to [Beginning of the document](#Debug-Framework-in-SONiC). + diff --git a/images/debug_framework_block_diagram.png b/images/debug_framework_block_diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..c4357fde09faa723867498b6f4631dab244fcaac GIT binary patch literal 23783 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 GLH`3Qk;=XR literal 0 HcmV?d00001 diff --git a/images/debug_framework_flow_diagram.png b/images/debug_framework_flow_diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..59ece8def838f8db279ab15e57d709cfc42ce921 GIT binary patch literal 44736 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 Date: Tue, 23 Jul 2019 01:49:57 -0700 Subject: [PATCH 2/3] Version 3 Debug Framework design spec --- doc/debug_framework_design_spec.md | 211 ++++++++++++++--------------- 1 file changed, 100 insertions(+), 111 deletions(-) diff --git a/doc/debug_framework_design_spec.md b/doc/debug_framework_design_spec.md index 138544035d..3188a85747 100644 --- a/doc/debug_framework_design_spec.md +++ b/doc/debug_framework_design_spec.md @@ -1,6 +1,6 @@ # Debug Framework in SONiC # Design Specification -#### Rev 0.2 +#### Rev 0.3 # Table of Contents * [List of Tables](#list-of-tables) @@ -26,6 +26,7 @@ |-----|------------|-----------------------------|----------------------------| | 0.1 | 05/19/2019 | Anil Sadineni, Laveen T | Initial version | | 0.2 | 05/31/2019 | Anil Sadineni, Laveen T | Address comments from Ben | +| 0.3 | 07/22/2019 | Anil Sadineni, Laveen T | Internal review comments | # About this Manual This document provides general information about the debug framework and additional debug features implementation in SONiC. @@ -56,14 +57,13 @@ This document describes the above scope. 2. Assert routines for production environment to delay the reload for gathering information or notify admin and delegate the decision of reload to admin. 3. Enhance tech-support to collect comprehensive info. 4. Add a helper script to facilitate an upload of the debug info in a non-intrusive way. -5. Rate limit and redirect logs for better debug ability without affecting stability of running processes. +5. Rate limit and redirect critical logs for better debug ability. ## 1.2 Configuration and Management Requirements -1. Need a config file to configure default options on the framework actions. -2. New CLI for triggering dump routines of Daemons. -3. New CLI to display drop/error interface counters. -4. New CLI to trigger upload of debug information. -5. Existing CLI is extended to filter the display counters on a specific interface. +1. New CLI for triggering dump routines of Daemons. +2. New CLI to display drop/error interface counters. +3. New CLI to trigger upload of debug information. +4. Existing CLI is extended to filter the display counters on a specific interface. ## 1.3 Scalability Requirements None @@ -96,12 +96,12 @@ New utility scripts are provided to: ### 3.1.1 Framework for dump routines **Block Diagram** -![Debug Framework Block Diagram](images/debug_framework_block_diagram.png) +![Debug Framework Block Diagram](https://github.com/anilsadineni/SONiC/blob/master/images/debug_framework_block_diagram.png) -Debug framework provides an API for components to register with the framework. It also provides interface for administrator, assert utilities, error handling functions to invoke framework actions. Framework uses Redis Publish/Subscribe implementation for communicating the trigger message from entity invoking the trigger to the registered components. Framework actions are configurable. These actions are applicable uniformly to all registered components. Please refer [Table-2](#table-2) for configurable actions and default actions. +Debug framework provides an API for components to register with the framework. It also provides interface for administrator, assert utilities, error handling functions to invoke framework actions. Framework uses Redis notifications for communicating the trigger message from entity invoking the trigger to the registered components. Framework actions are configurable. These actions are applicable uniformly to all registered components. -Debug Framework itself will not have an executing context. Framework actions are performed in the registered component's thread context. Trigger APIs are executed in the context of invoking utility or invoking process. Debug Framework will be implemented as a Singleton meaning there is going to be only one instance of framework in the system. +Debug Framework itself will not have an executing context. Framework actions are performed in the registered component's thread context. Trigger APIs are executed in the context of invoking utility or invoking process. Please refer [Flow Diagram](#4-flow-diagrams) for execution sequence. **Framework Responsibilities** @@ -111,14 +111,13 @@ Each registered component provides the routines to dump the component-specific s - Process the trigger and call appropriate dump routines - Manage the output location - Follow up with post actions like redirect summary to console/ compress the files and upload the file. -- Report summary or upload complete info is done either to a pre-defined location or a configured location. **Component Responsibilities** Components implement dump routines following below guidelines - Implement a function of type DumpInfoFunc. - `typedef std::function< void (std::string componentName, KeyOpFieldsValuesTuple args) > DumpInfoFunc;` -- Within the function, dump the information into a string buffer and use a macro `SWSS_DEBUG_PRINT`. Macro will further process the information based on intended framework action. + `typedef void (*DumpInfoFunc)(std::string, KeyOpFieldsValuesTuple);` +- Within the function, dump the information into a string buffer and use a macro `SWSS_DEBUG_PRINT`. Macro will further process the information based on intended framework action. Wrap the function that uses multiple invocations of `SWSS_DEBUG_PRINT` with `SWSS_DEBUG_PRINT_BEGIN` and `SWSS_DEBUG_PRINT_END`. - Handle KeyOpFieldsValuesTuple as argument. Arguments like dumpType and pass thru arguments that instruct the level of info dump ( short summary/specific info/filtered output/full info) should be handled by components. These arguments are opaque to framework. Additional optional arguments like output location and post-actions are for the framework consumption. - Interpreting arguments is at discretion of components. Components may choose to dump same info for all trigger types ignoring input arguments. - Dump routines registered with framework should be thread safe. Necessary synchronization needs to be ensured. @@ -127,20 +126,20 @@ Components implement dump routines following below guidelines Framework only manages component registration and invocation of callback functions. Framework itself does not have an executing context. To execute the callback function, components have an option to instruct framework either to create an additional thread that runs in the registrant context or not create a thread but merely publish an event to Redis and expect component to handle the event. Components have an option to register with debug framework using any one of the below APIs: -Option #1. `SWSSDebug::linkWithFramework()` -Option #2. `SWSSDebug::linkWithFrameworkNoThread()` +Option #1. `Debugframework::linkWithFramework()` +Option #2. `Debugframework::linkWithFrameworkNoThread()` Option #1 is preferred. However if a component already handles RedisDB events and doesn't want an additional thread (to avoid thread synchronization issues) might opt for option #2 Option #1 ( Framework creates a Thread ) --------- Components register dump routine with debug framework using the API: -`SWSSDebug::linkWithFramework(std::string componentName, std::function componentDumpFuncPtr);` +`Debugframework::linkWithFramework(std::string &componentName, const DumpInfoFunc funcPtr);` Framework does the following: - Update framework internal data structure to save the registration information of components. - Create a `swss::Selectable` object. -- Create a subscriber to a table/channel in DebugDumpReq database in RedisDB with a unique channel name for receiving triggers. +- Create a subscriber to a table/channel in APPL_DB in RedisDB with a unique channel name for receiving triggers. - Create a thread and wait on events/triggers. - Invoke component specific dump routine in this thread context on receipt of a trigger. - Handle the configured framework action within SWSS_DEBUG_PRINT. For example: Redirect the output to syslog or component specific log file. @@ -150,7 +149,7 @@ Framework does the following: Option #2 ( Framework does not create a Thread ) --------- Components register dump routine with debug framework using the API: -`SWSSDebug::linkWithFrameworkNoThread(std::string componentName);` +`Debugframework::linkWithFrameworkNoThread(std::string &componentName);` Framework will limit the job to the following: - Update framework internal data structure to save the registration information of components. - Handle the configured framework action within SWSS_DEBUG_PRINT. For example: Redirect the output to syslog or component specific log file. @@ -158,7 +157,7 @@ Framework will limit the job to the following: The below activity is delegated to the component - Optional creation of a swss::Selectable object. -- Create a subscriber instance to DebugDumpReq database in RedisDB for receiving triggers. +- Create a subscriber instance to a table/channel in APPL_DB in RedisDB for receiving triggers. - Optionally create a thread to wake on triggers. - Invoke component specific dump routine on receipt of a trigger. - Update Redis DB to indicate callback action completion. @@ -168,27 +167,24 @@ The below activity is delegated to the component Framework will be implemented in C++ as a class in SWSS namespace. ``` - class SWSSDebug + class Debugframework { public: - static SWSSDebug &getInstance(); // To have only one instance aka singleton + static Debugframework &getInstance(); // To have only one instance aka singleton - typedef std::function< void (std::string componentName, KeyOpFieldsValuesTuple args) > DumpInfoFunc; + typedef void (*DumpInfoFunc)(std::string, KeyOpFieldsValuesTuple); // Component registration option 1 API - static void linkWithFramework(componentName, DumpInfoFunc); + static void linkWithFramework(std::string &componentName, const DumpInfoFunc funcPtr); // Component registration option 2 API - static void linkWithFrameworkNoThread(componentName); + static void linkWithFrameworkNoThread(std::string &componentName); // Interface to invoke triggers static void invokeTrigger(std::string componentName, std::string args); - // API to configure the framework action. Function will be overloaded with arguments - static void setFrameworkAction(); - private: - SWSSDebug(); + Debugframework(); std::map m_registeredComps; std::map m.configParams; @@ -196,73 +192,100 @@ Framework will be implemented in C++ as a class in SWSS namespace. [[ noreturn ]] void runnableThread(); }; ``` - #### 3.1.1.1 Integration of OrchAgent with framework -##### 3.1.1.1.1 Triggers to OrchAgent -OrchAgent already has a thread that handles events from RedisDB and hence will register with framework (option #2) using below API: +##### 3.1.1.1.1 Integration of OrchAgent -``` - void OrchAgentDump(std::string componentName, std::vectorFieldsValuesTuple args) - { - if routeOrch - { - SWSS_DEBUG_PRINT(); - } - else if NeighOrch - { - SWSS_DEBUG_PRINT(); - } - } - - SWSSDebug::linkWithFrameworkNoThread(std::string componentName, OrchAgentDump); -``` +DebugDumpOrch is added as an interface for orchagent modules to register with debug framework. DebugDumpOrch uses linkWithFrameworkNoThread(). Orchagent modules (for eg. Routeorch/Neighorch) calls DebugDumpOrch::addDbgCompMap() to register. -The component subscribes to DebugDumpReq database in RedisDB. OrchAgent then gets a `swss::Selectable` object and updates the existing selectable map. When a trigger is received, OrchAgent executes OrchAgentDump() in the context of its own thread. +DebugDumpOrch +- listens for notification events from debugframework +- invokes corresponding component's CLI callback +- notifies debugframework after processing the debug request ##### 3.1.1.1.2 Triggers to OrchAgent + show commands will act as triggers for OrchAgent. -Syntax: `show debug .... ` +Syntax: `show debug ... ` + +Definition of command and required options are left to the component module owners. Sample CLI section descripes few examples on how the "show debug" CLI command can be used. ##### 3.1.1.1.3 Sample CLI -*RouteOrch:* +*RouteOrch:* | Syntax | Description | |---------------------------------------------------------|---------------------------------------------------------------------------------------------| -|show debug routeOrch routes [all] [ip_prefix] | Dump all Routes or routes specific to a prefix | -|show debug routeOrch nhgrp [addr-1] [addr-2] [addr-3] | NexthopGroup/ECMP info from RouteOrch::m_syncdNextHopGroups | -|show debug routeOrch counters | Dump internal counters associated with the component | -|show debug routeOrch dumpall | Translates to list of APIs to dump, which can be used for specific component based triggers | +|show debug routeOrch routes -v -p | Dump all Routes or routes specific to a prefix | +|show debug routeOrch nhgrp | NexthopGroup/ECMP info from RouteOrch::m_syncdNextHopGroups | +|show debug routeOrch all | Translates to list of APIs to dump, which can be used for specific component based triggers | -*NeighborOrch:* +*NeighborOrch:* | Syntax | Description | |---------------------------|--------------------| |show debug NeighOrch nhops | Dump Nexthops info | -|show debug NeighOrch neighs| Dump Neighbor info | +|show debug NeighOrch neigh | Dump Neighbor info | -##### 3.1.1.1.4 Sample Databases +##### 3.1.1.1.4 Sample Output ``` -;RouteOrch - Channel/Table Name -; -key = RouteOrch | Routes ; Dump routes map in RouteOrch -vrf = vrf_name ; vrf name for which to dump the routes info -prefix = v4/v6 address ; search for specific ip prefix -idx = seq_id/timestamp ; to uniquely identify this msg/request - -key = RouteOrch | nhgrp ; Dump Nexthop Group table in RouteOrch -prefix = v4/v6 address ; list of nexthop ipaddress -idx = seq_id/timestamp ; identifier - -;NeighborOrch - Channel/Table Name -; -Key = NeighborOrch | nhops ; Dump syncdnexthops map in NeighborOrch +root@sonic:~# show debug routeorch routes -v VrfRED +------------IPv4 Route Table ------------ + +VRF_Name = VrfRED VRF_SAI_OID = 0x30000000005b1 +Prefix NextHop SAI-OID +100.100.4.0/24 Ethernet4 0x60000000005b3 +33.33.33.0/24 0x55c1ca5b3b98 (ECMP) 0x50000000005db +33.33.44.0/24 100.120.120.11|Ethernet8 0x40000000005d9 +33.33.55.0/24 100.120.120.12|Ethernet8 0x40000000005da +100.102.102.0/24 Ethernet12 0x60000000005d3 +100.120.120.0/24 Ethernet8 0x60000000005b4 + +------------IPv6 Route Table ------------ + +VRF_Name = VrfRED VRF_SAI_OID = 0x30000000005b1 +Prefix NextHop SAI-OID +2001:100:120:120::/64 Ethernet8 0x60000000005b4 + +root@sonic:~# show debug routeorch nhgrp + ax Nexthop Group - 512 +NHGrpKey SAI-OID NumPath RefCnt +0x55c1ca5b7bd0 0x50000000005db 3 1 + 1: 100.120.120.10|Ethernet8 + 2: 100.120.120.11|Ethernet8 + 3: 100.120.120.12|Ethernet8 + +root@sonic:~# show debug neighorch nhops + +NHIP Intf SAI-OID RefCnt Flags +100.120.120.10 Ethernet8 0x40000000005d8 1 0 +100.120.120.11 Ethernet8 0x40000000005d9 2 0 +100.120.120.12 Ethernet8 0x40000000005da 2 0 + +NHIP Intf SAI-OID RefCnt Flags +fe80::648a:79ff:fe5d:6b2a Ethernet4 0x40000000005df 0 0 +fe80::fc54:ff:fe44:de2 Ethernet12 0x40000000005d4 0 0 +fe80::fc54:ff:fe78:5fac Ethernet8 0x40000000005d2 0 0 +fe80::fc54:ff:fe88:6f80 Ethernet4 0x40000000005d0 0 0 +fe80::fc54:ff:fe8e:d91f Ethernet0 0x40000000005d1 0 0 + +root@sonic:~# +root@sonic:~# show debug neighorch neigh +NHIP Intf MAC +100.120.120.10 Ethernet8 00:00:11:22:00:10 +100.120.120.11 Ethernet8 00:00:11:22:00:11 +100.120.120.12 Ethernet8 00:00:11:22:00:12 + +NHIP Intf MAC +fe80::648a:79ff:fe5d:6b2a Ethernet4 fe:54:00:35:18:bb +fe80::fc54:ff:fe44:de2 Ethernet12 fe:54:00:44:0d:e2 +fe80::fc54:ff:fe78:5fac Ethernet8 fe:54:00:78:5f:ac +fe80::fc54:ff:fe88:6f80 Ethernet4 fe:54:00:88:6f:80 +fe80::fc54:ff:fe8e:d91f Ethernet0 fe:54:00:8e:d9:1f ``` #### 3.1.1.3 Configuring Framework Debug framework will initialize with below default parameters and shall be considered if options are not specified as arguments in the triggers. -The defaults are read from the debugFm.ini file. # Table 2: Configuration Options and Defaults @@ -279,7 +302,7 @@ The defaults are read from the debugFm.ini file. ### 3.1.2 Assert Framework #### 3.1.2.1 Overview -Asserts are added in the program execution sequence to confirm that the data/state at a certain point is valid/true. During developement, if the programming sequence fails in an assert condition then the program execution is stopped by crash/exception. In Production code, asserts are normally removed. This framework enhances/extendes the assert to provide more debug details when an assert fails. +Asserts are added in the program execution sequence to confirm that the data/state at a certain point is valid/true. During developement, if the programming sequence fails in an assert condition then the program execution is stopped by crash/exception. In production code, asserts are normally removed. This framework enhances/extendes the assert to provide more debug details when an assert fails. Classify assert failure conditions based on following types, assert() will have type and the module as additional arguments - DUMP: Invokes the debug framework registered callback API corresponding to the module @@ -291,42 +314,11 @@ Classify assert failure conditions based on following types, assert() will have #### 3.1.2.2 PsuedoCode: ``` - class dbgAssert: public SWSSDebug - { - public: - enum Type { - SYSLOG, // recoverable: log to trace error conditions - DUMP, // Functional impact: No depencies with other module, System may continue to operate - BTRACE, // Unexpected flow: Print backtrace and continue - ABORT, // System Fault: System reached a state where it cannot recover / should not continue - UNKNOWN - }; - - inline assert(exp) - { - syslog("Assert:expression", __FUNCTION__, __FILE__); - } - - void assert(Type type, string module, boolean expr); - } - - void dbgAssert::assert(Type type, string module, boolean expr) - { - if(!expr) - if(type == BTRACE) - backtrace(); - if(type == DUMP) - SWSSDebug::dump(module>); - else if(type == ABORT) - SWSSDebug::dump(module>); - abort(); - } - - gDbgAssert = new dbgAssert(); + static void custom_assert(bool exp, const char*func, const unsigned line); #ifdef assert #undef assert - #define assert(exp) gDbgAssert::assert(exp) + #define assert(exp) Debugframework::custom_assert(exp, __PRETTY_FUNCTION__, __LINE__) #endif ``` @@ -370,7 +362,7 @@ show interfaces pktdrop is added to display debug/error counters for all interfa # 4 Flow Diagrams -![Framework and component interaction](images/debug_framework_flow_diagram.png) +![Framework and component interaction](https://github.com/anilsadineni/SONiC/blob/master/images/debug_framework_flow_diagram.png) # 5 Serviceability and Debug None @@ -391,15 +383,12 @@ No Change 2. Verify that on a running system, without any triggers framework does not add any CPU overhead. 3. Verify that dump routines are invoked when a trigger is issued with all arguments from the trigger utilities/ show debug commands. 4. Verify that number of subscribers is incremented using redis-cli after successful registration of component. -5. Verify that SWSS_DEBUG_DUMP macro writes to dump location specified in the .ini file when the target location for dump is not specified or specified as "default". -6. Verify that SWSS_DEBUG_DUMP macro writes to SYSLOG when DUMP_TARGET in trigger is mentioned as syslog. +5. Verify that SWSS_DEBUG_PRINT macro writes to dump location specified . +6. Verify that SWSS_DEBUG_PRINT macro writes to SYSLOG when DUMP_TARGET in trigger is mentioned as syslog. 7. Verify that if the utility function triggers dump for all, framework loops through the registrations and updates the table for each component. 8. Verify that framework executes the configured post action. 9. Verify the behaviour of framework when some component doesnt send a DONE message. 10. Verify that framework handles multiple consecutive triggers and handles triggers independently. -# 9 Internal Design Information -Internal BRCM information to be removed before sharing with the community - Go back to [Beginning of the document](#Debug-Framework-in-SONiC). From 5ddfeb7919167d0ee8ec99cd8108ed2b91e006d8 Mon Sep 17 00:00:00 2001 From: Anil Sadineni Date: Tue, 23 Jul 2019 02:24:17 -0700 Subject: [PATCH 3/3] edit images path --- doc/debug_framework_design_spec.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/debug_framework_design_spec.md b/doc/debug_framework_design_spec.md index 3188a85747..7567e26d99 100644 --- a/doc/debug_framework_design_spec.md +++ b/doc/debug_framework_design_spec.md @@ -96,7 +96,7 @@ New utility scripts are provided to: ### 3.1.1 Framework for dump routines **Block Diagram** -![Debug Framework Block Diagram](https://github.com/anilsadineni/SONiC/blob/master/images/debug_framework_block_diagram.png) +![Debug Framework Block Diagram](https://github.com/anilsadineni/SONiC/blob/debug_framework_HLD/images/debug_framework_block_diagram.png) Debug framework provides an API for components to register with the framework. It also provides interface for administrator, assert utilities, error handling functions to invoke framework actions. Framework uses Redis notifications for communicating the trigger message from entity invoking the trigger to the registered components. Framework actions are configurable. These actions are applicable uniformly to all registered components. @@ -362,7 +362,7 @@ show interfaces pktdrop is added to display debug/error counters for all interfa # 4 Flow Diagrams -![Framework and component interaction](https://github.com/anilsadineni/SONiC/blob/master/images/debug_framework_flow_diagram.png) +![Framework and component interaction](https://github.com/anilsadineni/SONiC/blob/debug_framework_HLD/images/debug_framework_flow_diagram.png) # 5 Serviceability and Debug None