EXCEPTION REPORTING Based on SystemC Verification Standard Specification, Feb, 2003. The exception reporting facility provides a common and configurable API to report an exceptional situation. The facility is presented by two classes: sc_report_handler and sc_report. The former provides configuration and report generation calls. The latter just contains the report related information. The application defines an exceptional situation by using one of SC_REPORT_ macros to generate a report. The report is identified by its severity (represented by sc_severity enum type) and the message type. The message type is a string of characters, uniquely identifing a specific type of the exception. This sc_severity describes the severity of a report: enum sc_severity { SC_INFO, SC_WARNING, SC_ERROR, SC_FATAL }; SC_INFO The report is informative only. SC_WARNING The report indicates a potentially incorrect condition. SC_ERROR The report indicates a definite problem during execution. The default configuration forces a throw of a C++ exception sc_exception with the corresponding report information attached. SC_FATAL The report indicates a problem which cannot be recovered from. In default configuration, the simulation is terminated immediately using an abort() call after reporting a SC_FATAL report. The application can define actions to be taken for a generated report. Whereas a usual reaction on a exceptional situation includes just printing a message, more complex scenarios could involve a logging of the report into a file, throwing a C++ exception or drop in the debugger. The enum type sc_actions describes such a set of operations. There are several predefined values for this type: enum { SC_UNSPECIFIED = 0x00, SC_DO_NOTHING = 0x01, SC_THROW = 0x02, SC_LOG = 0x04, SC_DISPLAY = 0x08, SC_CACHE_REPORT = 0x10, SC_INTERRUPT = 0x20, SC_STOP = 0x40, SC_ABORT = 0x80 }; SC_UNSPECIFIED Take the action specified by a configuration rule of a lower precedence. SC_DO_NOTHING Don't take any actions for the report, the action will be ignored, if other actions are given. SC_THROW Throw a C++ exception (sc_exception) that represents the report. The method sc_exception::get_report() can be used to access the report instance later. SC_LOG Print the report into the report log, typically a file on disk. The actual behaviour is defined by the report handler function described below. SC_DISPLAY Display the report to the screen, typically by writing it in to the standard output channel using std::cout. SC_INTERRUPT Interrupt simulation if simulation is not being run in batch mode. Actual behaviour is implementation defined, the default configuration calls sc_interrupt_here(...) debugging hook and has no furhter side effects. SC_CACHE_REPORT Save a copy of the report. The report could be read later using sc_report_handler::get_cached_report(). The reports saved by different processes do not overwrite each other. SC_STOP Call sc_stop(). See sc_stop() manual for further detail. SC_ABORT The action requests the report handler to call abort(). The report handler, a function known to the class sc_report_handler, takes the responsibility of execution the requested actions. Application is able to redefine the report handler to take additional steps on execution of a specific action or extend the default set of possible actions. As the report handler is responsible for all predefined actions it can also be used to redefine the behaviour of predefined actions. Each exception report can be configured to take one or more sc_actions. Multiple actions can be specified using bit-wise OR. When SC_DO_NOTHING is combined with any thing other than SC_UNSPECIFIED, the bit is ignored by the facility. In addition to the actions specified within the sc_actions enum, via sc_actions, the exception API also can take two additional actions. The first action is always taken: the sc_stop_here() function is called for every report, thus providing users a convenient location to set breakpoints to detect error reports, warning reports, etc. The second action that can be taken is to force SC_STOP in the set of the actions to be executed. The action is configured via the stop_after() method described below, which allows users to set specific limits on the number of reports of various types that will usually cause simulation to call sc_stop(). The configuration and report generation API is contained within the sc_report_handler class. The sc_report_handler Class The class provides only static API. The user cannot construct an instance of the class. void report( sc_severity severity, const char* msg_type, const char* msg, const char* file, int line ); Generate a report instance, which will cause the facility to take the appropriate actions based on the current configuration. The call will configure a not known before exception of msg_type to take default set of actions for given severity. The first occurence of the particular msg_type starts its stop_after() counter. sc_actions set_actions( sc_severity severity, sc_actions actions = SC_UNSPECIFIED ); Configure the set of actions to take for reports of the given severity (lowest precedence match). The previous actions set for this severity is returned as the result. SC_UNSPECIFIED is returned if there was no previous actions set for this severity. TODO: what if to be taken actions are SC_UNSPECIFIED and there is now highe precedence match? do nothing? sc_actions set_actions( const char* msg_type, sc_actions actions = SC_UNSPECIFIED ); Configure the set of actions to take for reports of the given message type (middle precedence match). The previous actions set for this message type is returned as the result. SC_UNSPECIFIED is returned if there was no previous actions set for this message type. sc_actions set_actions( const char* msg_type, sc_severity severity, sc_actions actions = SC_UNSPECIFIED ); Configure the set of actions to take for reports having both the given message type and severity (high precedence match). The previous actions set for this message type and severity is returned as the result. SC_UNSPECIFIED is returned if there was no previous actions set for this message type and severity. The functions stop_after(...) modify only the limit, they do not affect the counter of the number of reports. Setting the limit below the number of already occured reports will cause sc_stop() for the next matching report. int stop_after( sc_severity severity, int limit = -1 ); Call sc_stop() after encountering limit number of reports of the given severity (lowest precedence match). If limit is set to one, the first occurrence of a matching report will cause the abort. If limit is 0, abort will never be taken due to a matching report. If limit is negative, abort will never be taken for non-fatal error, and abort will be taken for the first occurrence of a fatal error. The previous limit for this severity is returned as the result. The stop_after() call will return UINT_MAX (int -1) in the case where no previous corresponding stop_after() call was made. int stop_after( const char* msg_type, int limit = -1 ); Call sc_stop() after encountering limit number of reports of the given message type (middle precedence match). The previous limit for this message type is returned as the result. If limit is 0, abort will never be taken due to a matching report. If limit is negative, the limit specified by a lower precedence rule is used. The stop_after() call will return UINT_MAX in the case where no previous corresponding stop_after() call was made. int stop_after( sc_msg_type msg_type, sc_severity severity, int limit = -1 ); Call sc_stop() after encountering limit number of reports having both the given message type and severity. (Highest precedence match.) If limit is 0, abort will never be taken due to a matching report. If limit is negative, the limit specified by a lower precedence rule is used. The previous limit for this message type and severity is returned as the result. The call will return UINT_MAX in the case where no previous corresponding stop_after() call was made. sc_actions suppress( sc_actions actions ); Suppress specified actions for subsequent reports regardless of configuration and clears previous calls to suppress(). The return value is the actions that were suppressed prior to this call. The suppressed actions are still active if they are mentioned by force(sc_actions) call. sc_actions suppress(); Restore default behavior by clearing previous calls to suppress(). The return value is the actions that were suppressed prior to this call. The default behaviour does not suppress any actions. sc_actions force( sc_actions actions ); Force specified actions to be taken for subsequent reports in addition to the actions specified in the current configuration and clears previous calls to force(). The return value is the actions that were forced prior to this call. The actions given by this call override similar setting in suppress(). sc_actions force(); Restore default behavior by clearing previous calls to force(). The return value is the actions that were forced prior to this call. There is no forced actions in the default configurations. sc_actions get_new_action_id(); Return an unused sc_actions value. Returns a different value each time it is called (returns SC_UNSPECIFIED if no more unique values are available). Used when establishing user-defined actions, interpreted by a non-default report handler. It is implementation defined whether the call could be used in the global constructors. const char* get_log_file_name(); Return the log file name currently in effect. Return NULL if no logging is active at the moment. It is implementation defined whether the returned string actually represents a file. void set_log_file_name( const char* name ); Set the log file name. The current handler implementation is responsible for interpretation of the given argument. The name may be unused until first SC_LOG action has occured. The default implementation provides a plain text file logging. The file will be opened after as the first SC_LOG action. The default implementation provides a global function for closing the log file: sc_report_close_default_log(). The logging is deactivated after calling the function and subsequent calls to get_log_file_name() will return NULL. The report handler is reponsible for proper terminating of the logging facility at the end. const sc_report* get_cached_report(); Return pointer to the recent report for which was an SC_CACHE_REPORT action defined. In the default configuration reports of severities SC_ERROR and SC_FATAL are cached. void clear_cached_report(); Clear cached report for the current process (if any). void initialize(); Initializes default configuration. The call shall reset the limit counters. The call may not remove or reconfigure messages. The call may not affect logging. The call does not affect cached reports. void release(); Releases the resource possibly allocated by the exception reporting implementation. The facility may not be used after this call. Whether the facility could be used after subsequent initialize() call is defined by the implementation. The default implementation removes all user defined and/or configured messages and close the log file. Configured predefined messages will be not reset. void set_handler( sc_report_handler_proc handler ); typedef void (*sc_report_handler_proc)(const sc_report&, const sc_actions&); Specify the report handler function. The handler functions get an instance of the generated report and can use the methods of sc_report to access the needed information. The set of requested actions is passed through the second argument. void default_handler( const sc_report& report, const sc_actions& actions ); The function is the default handler of the facility provided by the given SystemC implementation. The force() and suppress() methods provide a brute-force way to override the current configuration. For example, force(SC_LOG) could be called during debugging to cause all reports to be logged regardless of the current configuration. As another example, suppress(suppress() | SC_THROW); could be called by code that is not C++ throw-safe when it starts execution, and then suppress(prev) would be called when it completes execution. The class sc_report the report representation. An instance of the class could be accessed either through its cached copy or in the catch block of C++ "try {} catch (const sc_exception & ex) {}" construction. To access the report the method sc_exception::get_report() is available. Use sc_report_handler::get_cached_report() to access the cached copy of the report. Instances of the sc_report can be copied by copy constructor and assignment operator means. It is not allowed to create an empty report. The sc_report Class sc_severity get_severity() const; Return the severity of a report object. const char* get_msg_type() const; Get message type of a report object. The returned string is guaranteed to persist until sc_report_handler::release() is called. const char* get_msg() const; Get message contents of a report object. The lifetime of the returned pointer is that of the report instance. const char* get_file_name() const; Get file name that generated report object. Please see the definition of the SC_REPORT_ macros for the exact contents of the returned value. int get_line_number() const; Get line number that generated report object. See also: get_file_name(). sc_time get_time() const; Get the simulation time when then report object was generated. const sc_process_b* get_process() const; Get the process that generated the report object. The process name could be retrieved by calling the name() of the returned pointer. When a report is logged to a file, the current simulation time and current process name will automatically be included within the report. The implementation defines following actions in the default configuration: Severity Actions INFO SC_LOG | SC_DISPLAY WARNING SC_LOG | SC_DISPLAY ERROR SC_LOG | SC_CACHE_REPORT | SC_THROW FATAL SC_LOG | SC_DISPLAY | SC_CACHE_REPORT | SC_ABORT The error level reports are displayed by the default handler of sc_exception type exceptions. The following macros are globally visible as part of the standard and should be used to generate reports: #define SC_REPORT_INFO(msg_type, msg) \ sc_report_handler::report( SC_INFO, msg_type, msg, __FILE__, __LINE__ ) #define SC_REPORT_WARNING(msg_type, msg) \ sc_report_handler::report( SC_WARNING, msg_type, msg, __FILE__, __LINE__ ) #define SC_REPORT_ERROR(msg_type, msg) \ sc_report_handler::report( SC_ERROR, msg_type, msg, __FILE__, __LINE__ ) #define SC_REPORT_FATAL(msg_type, msg) \ sc_report_handler::report( SC_FATAL, msg_type, msg, __FILE__, __LINE__ ) The following examples illustrates how the exception API might be custom configured and how reports are generated. Note that message types are best captured within one or more header files, where they are declared using #define macros. This technique insures that strings representing message types are only declared once and that any typos that might occur when message types are specified in the SC_REPORT_* macros are caught by the compiler. #define PCI_RPT_PROTOCOL_EXCEPTION "PCI Protocol Exception" const char PCI_RPT_PROTOCOL_READ_RETRY[] = "PCI Read Retry"; int sc_main(int, char**) { // stop after having seen 10 error-level reports sc_report_handler::stop_after(SC_ERROR, 10); // make the PCI_RPT_PROTOCOL_EXCEPTION error non-critical // Note that 10 this errors will still cause a stop, as // configured by previous statement. sc_report_handler::set_actions(PCI_RPT_PROTOCOL_EXCEPTION, SC_ERROR, SC_DISPLAY); // disable the report PCI_RPT_PROTOCOL_READ_RETRY sc_report_handler::set_actions(PCI_RPT_PROTOCOL_READ_RETRY, SC_DO_NOTHING); sc_start(1, SC_MS); // allow the report PCI_RPT_PROTOCOL_READ_RETRY to be displayed sc_report_handler::set_actions(PCI_RPT_PROTOCOL_READ_RETRY, SC_DISPLAY); sc_start(1, SC_MS); // PCI_RPT_PROTOCOL_READ_RETRY reports will now be configured to // SC_UNSPECIFIED. Therefore, a lower precedence rule applies and the // actions in SC_DEFAULT_..._ACTIONS will take effect for the report. sc_report_handler::set_actions(PCI_RPT_PROTOCOL_READ_RETRY); sc_start(1, SC_MS); } void foo() { sc_time max_time(500, SC_NS); if (...) SC_REPORT_ERROR(PCI_RPT_PROTOCOL_EXCEPTION, "PCI burst read exceeded max time limit of " + max_time.to_string()); if (...) SC_REPORT_INFO(PCI_RPT_PROTOCOL_READ_RETRY, "PCI read retry at time " + sc_time_stamp().to_string()); } The following example illustrates how reports using SC_CACHE_REPORT actions can be accessed: ... sc_report_handler::set_actions(PCI_RPT_PROTOCOL_READ_RETRY, SC_INFO, SC_CACHE_REPORT|SC_LOG); ... void module::do_something() { if (...) SC_REPORT_INFO(PCI_RPT_PROTOCOL_READ_RETRY, "..."); } void module::foo() { sc_report_handler::clear_cached_report(); do_something(); sc_report* rp = sc_report_handler::get_cached_report(); if ( rp ) { cout << rp->get_msg() << endl; } } The following example illustrates how reports using SC_THROW actions can be accessed: ... sc_report_handler::set_actions(PCI_RPT_PROTOCOL_EXCEPTION, SC_ERROR, SC_THROW); ... void module::do_something() { if (...) SC_REPORT_ERROR(PCI_RPT_PROTOCOL_EXCEPTION, "..."); } void module::bar() { try { do_something(); } catch (const sc_exception & e) { cerr << e.get_report().get_msg() << endl; } }