|
Sierra Toolkit
Version of the Day
|
The diagnostic writer provides features for the runtime selection of diagnostic output of data and objects. More...
|
The diagnostic writer provides features for the runtime selection of diagnostic output of data and objects.
The runtime diagnostic output selection is achieved utilizing message marking within the source code and selection from the command line or input deck.
The First,
by implementing a dump member function and an operator<< free function, the class may be easily dumped using the << operator. And, second, by assigning appropriate bit masks, the diagnostic writer allows the diagnostic output to be selectively output by the developer or user at runtime.
For Example:
class AnotherClass { public: AnotherClass(int i) {} double hairyCalc(int i) { return sin((double) i); } Writer &dump(Writer &dout) const { if (dout.shouldPrint()) { dout << "AnotherClass " << push << dendl; dout.m(LOG_MEMBERS) << "m_m1, " << m_m1 << dendl; dout.m(LOG_MEMBERS) << "m_mem2, " << m_mem2 << dendl; if (dout.shouldPrint(LOG_MEMBERS)) { dout << "hairyCalc" << push << dendl; for (int i = 0; i < 1000; ++i) dout << "hairyCalc(" << i << ") = " << hairyCalc(i) << dendl; dout << pop; } dout << pop; } return dout; } private: int m_m1; int m_mem2; }; inline Writer &operator<<(Writer &dout, const AnotherClass &another_class) { return another_class.dump(dout); } class ShowWriter { void Func() { sierra::diag_writer dout(std::cout); dout.setPrintMask(LOG_LIGHT | LOG_HEAVY | LOG_MEMBERS); int j = 7; m_anotherClassVector.push_back(AnotherClass(j)); m_anotherClassVector.push_back(AnotherClass(j*2)); m_anotherClassVector.push_back(AnotherClass(j*2 + 1)); dout.m(LOG_LIGHT) << "Light weight diagnostic message, j is " << j << dendl; dout.m(LOG_HEAVY) << "Heavy weight diagnostic message " << m_anotherClassVector << dendl; } Writer &dump(Writer &dout) const { if (dout.shouldPrint()) { dout << "ShowWriter, " << push << dendl; dout.m(LOG_MEMBERS) << "m_anotherClassVector, " << m_anotherClassVector << dendl; dout << pop; } return dout; } std::vector<AnotherClass> m_anotherClassVector; }; inline Writer &operator<<(Writer &dout, const ShowWriter &show_diag_writer) { return show_diag_writer.dump(dout); }
Adding a diagnostic writer to your application allows you to leave diagnostic code in your application which is only seen when desired.
Create a App_DiagWriter_fwd.h
The forward definition file defines the LOG_name bit masks which will be used to select the messages to be written to the diagnostic stream.
#ifndef SIERRA_App_DiagWriter_fwd_h #define SIERRA_App_DiagWriter_fwd_h #include <stk_util/diag/Writer_fwd.hpp> namespace sierra { namespace app { enum { LOG_ALWAYS = sierra::LOG_ALWAYS, LOG_TRACE = sierra::LOG_TRACE, LOG_TRACE_STATS = sierra::LOG_TRACE_STATS, LOG_TRACE_SUB_CALLS = sierra::LOG_TRACE_SUB_CALLS, LOG_MEMBERS = sierra::LOG_MEMBERS, LOG_NOLOAD = 0x00000100, LOG_NOEXECUTE = 0x00000200, LOG_SYNTAX_CHECK = 0x00000400, LOG_INPUT_CHECK = 0x00000800, LOG_DUMP_SETUP = 0x00001000, LOG_DUMP_LOAD = 0x00002000, LOG_CONTROL = 0x00003F00, LOG_COMM = 0x00010000, LOG_CONTACT = 0x00020000, LOG_GEOMETRY = 0x00040000, LOG_APP04 = 0x00080000, LOG_MATERIAL = 0x00100000, LOG_MESH_MOD = 0x00200000, LOG_PARAMS = 0x00400000, LOG_PLUGINS = 0x00800000, LOG_SCONTROL = 0x01000000, LOG_SEARCH = 0x02000000, LOG_SOLVER = 0x04000000, LOG_XFER = 0x08000000, LOG_FIELD = 0x10000000, LOG_DIAGNOSTICS = 0xFFFF0000 }; } // namespace app } // namespace sierra #endif // SIERRA_App_DiagWriter_fwd_h
Create a App_DiagWriter.h
The diagnostic writer header file defines the parser for parsing the command line and line commands to produce the mask of bits which the user has selected. It also implements the sentry which the application uses to bootstrap the diagnostic writer.
#ifndef SIERRA_App_DiagWriter_h #define SIERRA_App_DiagWriter_h #include <stk_util/diag/Writer.hpp> #include <Slib_DiagWriterOStream.h> #include <stk_util/diag/WriterParser.hpp> #include <App_DiagWriter_fwd.h> namespace sierra { namespace app { class DiagWriterParser : public Diag::WriterParser { public: DiagWriterParser(); }; stk_classic::diag::Writer &theDiagWriter(); DiagWriterParser &theDiagWriterParser(); #define applog sierra::App::theDiagWriter() } // namespace app } // namespace sierra #endif // SIERRA_Aria_DiagWriter_h
Create a App_DiagWriter.C
The implementation of the parser constructor adds the command line and line command value used to generate the bit mask. Any combinations of bit masks can be given a name. This will simplify specification of commonly grouped options for the user.
#include <stk_util/util/Bootstrap.hpp> #include <stk_util/environment/OutputLog.hpp> #include <stk_util/diag/WriterRegistry.hpp> #include <App_DiagWriter.h> namespace sierra { namespace app { DiagWriterParser & theDiagWriterParser() { /* %TRACE% */ /* %TRACE% */ static DiagWriterParser parser; return parser; } stk_classic::diag::Writer & theDiagWriter() { /* %TRACE[NONE]% */ /* %TRACE% */ static stk_classic::diag::Writer s_diagWriter(sierra::dwout().rdbuf(), theDiagWriterParser().parse(std::getenv("APPLOG"))); return s_diagWriter; } DiagWriterParser::DiagWriterParser() : Diag::WriterParser() { /* %TRACE% */ /* %TRACE% */ mask("bc", (unsigned long) (LOG_BC), "Display boundary condition information"); mask("debug", (unsigned long) (LOG_DEBUG), "Display debug diagnostic information"); mask("eq", (unsigned long) (LOG_EQ), "Display equation information"); mask("expression", (unsigned long) (LOG_EXPRESSION), "Display expression information"); mask("hadapt", (unsigned long) (LOG_HADAPT), "Display h_adapt diagnostic information"); mask("finite_check", (unsigned long) (LOG_FINITE_CHECK), "Display warning messages about any non-finite expression values or sensitivities"); mask("nonlinear", (unsigned long) (LOG_NONLINEAR), "Diaplay nonlinear solver information"); mask("pp", (unsigned long) (LOG_PP), "Display postprocessor diagnostic information"); mask("sens_check", (unsigned long) (LOG_SENS_CHECK), "Display messages generated by expression sensitivity checker"); mask("fast_sens_check", (unsigned long) (LOG_FAST_SENS_CHECK), "Display messages generated by sensitivity checker (skipping FAD, element, and kernel expressions)"); mask("species", (unsigned long) (LOG_SPECIES), "Display species information"); mask("transfer", (unsigned long) (LOG_TRANSFER), "Display transfer information"); mask("plugin", (unsigned long) (LOG_PLUGIN), "Display plugin information"); mask("chaparral", (unsigned long) (LOG_CHAPARRAL), "Display chaparral information"); mask("utility", (unsigned long) (LOG_UTILITY), "Display equation system utility information"); mask("pre_assembly", (unsigned long) (LOG_PREASSEMBLY), "Display pre-assembly diagnostic information"); mask("expression_dump", (unsigned long) (LOG_EXPRESSION_DUMP), "Display the values for every expression and its sensitivities"); mask("calore", (unsigned long) (LOG_CALORE), "Display additional calore output (hack)"); mask("ccfvm", (unsigned long) (LOG_CCFVM), "Display additional ccfvm output (hack)"); mask("user-sub", (unsigned long) (LOG_USER_SUB), "Print user subroutine execution diagnostics"); } namespace { void bootstrap() { Diag::registerWriter("applog", applog, theDiagWriterParser()); } stk_classic::Bootstrap x(&bootstrap); } // namespace <empty> } // namespace app } // namespace sierra
Adding a new mask and option name requires only adding the new bit to the enumeration in App_DiagWriter_fwd.h, where 0xmmmmmm00 is a previously unusd bit:
#ifndef SIERRA_App_DiagWriter_fwd_h #define SIERRA_App_DiagWriter_fwd_h #include <stk_util/diag/Writer_fwd.hpp> namespace sierra { namespace app { enum { LOG_ALWAYS = sierra::LOG_ALWAYS, LOG_TRACE = sierra::LOG_TRACE, LOG_TRACE_STATS = sierra::LOG_TRACE_STATS, LOG_TRACE_SUB_CALLS = sierra::LOG_TRACE_SUB_CALLS, LOG_MEMBERS = sierra::LOG_MEMBERS, ... LOG_NEWUNUSEDBIT = 0x<i>mmmmmm00</i>, ... }; } // namespace app } // namespace sierra #endif // SIERRA_App_DiagWriter_fwd_h
And, adding a mask function call in the parser constructor in the App_DiagWriter.C file.
DiagWriterParser::DiagWriterParser()
: Diag::WriterParser()
{
/* %TRACE% */ /* %TRACE% */
...
mask("new-bit-name", (Diag::PrintMask) (LOG_NEWUNUSEDBIT), "Some brief description");
...
}
To incorporate the diagnostic writer into a class or application, there are only a few steps to follow. And, by following these guidelines, the newly created class can fully participate is class data diagnostic output.
In your class header:
Include your application's diagnostic writer forward header.
#include <App_DiagWriter_fwd.h>
Declare the dump member function. If this is a base class, be sure the define this member function as virtual if inheritance may be in play.
Writer &dump(Writer &dout) const;
Define the output insertion operator, operator<< to call your member function.
inline Writer &operator<<(Writer &dout, const MyClass &my_class) { return my_class.dump(dout); }
In your class implementation:
Include your application's diagnostic writer header.
#include <App_DiagWriter.h>;
Define your MyClass::dump(Writer &dout) const member function. I suggest using the following as a start:
Writer &
MyClass::dump(
Writer & dout) const
{
if (dout.shouldPrint()) {
dout << demangle(typeid(MyClass).name()) << ", " << m_name << push << std::endl;
MyParentClass::dump(dout).dendl(); // If there is a base class
dout.m(LOG_MEMBERS) << "m_information, " << m_information << std::endl;
dout << pop;
}
return dout;
}
There are several functions and manipulators which control the selection and formatting of the diagnostic output.
The print mask and the line mask
It is the interaction of the print mask and line mask which determines is the data is to be written to the diagnostic stream. The print mask is set by the user on the command line or in the input deck. The line mask is set by the developer using the m member function and Diag::setlinemask and Diag::resetlinemask manipulators.
As each << operator is executed, the print mask and current line mask are bitwise anded together. If the result of this is non zero, then the line is output.
std::endl and Diag::dendl
Be sure to include std::endl or Diag::dendl at the end of each output line. The end line functions serve as a mask application terminator. In other word, the mask specified using the m member function are in effect until the std::endl of Diag::dendl functions are called on the diagnostic stream. Leaving one out can effect the what is selected to be output.
Diag::push and Diag::pop
The push manipulator pushes the current LineMask onto a stack and the pop manipulator restores the LineMask from the top element of the push stack.
When exception occur, the pops put-to the output stream are skipped by the exception catch block. As a result, the indentation can start to march across the screen. To correct this behavior, add a ThrowSafe sentry within your try block prior to any push operations.
try { // TimeStepError stk_classic::diag::WriterThrowSafe throw_safe_sentry; appout.m(LOG_TEST) << "Executing run_it() on " << name() << push << dendl; run_it(); appout.m(LOG_TEST) << pop; } catch (std::exception &x) { appout.m(LOG_TEST) << "Exception caught running run_it() on " << name() << dendl; }