Back to home page

Project CMSSW displayed by LXR

 
 

    


Warning, /FWCore/MessageService/doc/suppression.txt is written in an unsupported language. File is not indexed.

0001                 Suppression of Messages in MessageLogger
0002                 ----------------------------------------
0003 
0004 Basics of message suppression
0005 -----------------------------
0006 
0007 By definition, suppression of messages means that some subset of
0008 messages issued by <LogWhatever> will never (in the case of multithread
0009 operation) even be put on the queue for the MessageLoggerScribe thread
0010 to look at.  (In single-thread mode, no calls to any messafge service logging
0011 functions are made.)  
0012 
0013 To suppress a message, the software must know, "at the top", that no message 
0014 of this ilk can possibly be reacted to. 
0015 
0016 For practical purposes, this has several consequences:
0017 
0018 1. Savings of activity:  
0019    a. Overhead of adding to the queue, waking the scribe thread, consuming
0020       the message is eliminated.
0021    b. Preparation of the header and the extended message id never happens.
0022    c. Message is not "shopped" to the various destinations, saving 
0023       various map lookups.  This includes the statistics destinations.
0024    d. Of course no actual I/O or  is done. 
0025 
0026 2. Granularity of choice:
0027    a. Suppression cannot distinguish between one destination and another.
0028    b. Suppression knows nothing about the category of the message.
0029    c. Suppression does know about the severity level of the message.
0030    c. Suppression CAN depend on which module is running.
0031 
0032 3. Effect:
0033    a. A suppressed message cannot contribute to any statistics destination.
0034    b. There cannot be a way to over-ride suppression at the destination level.
0035 
0036 Other configuration-specified preferences can cause various messages to be 
0037 ignored by various destinations, but that is moot to the issue of suppresssion
0038 because to decide to ignore a message, the destination has to see the message,
0039 thus the message must have made it to the MessageLoggerScribe.
0040 
0041 Suppression can be controlled by compile-time defines, by thresholds and
0042 module specifications set up in the configuration file, or by a combination 
0043 of both.  
0044 
0045 Although by definition the activities above are elided, there is potential for
0046 avoiding (or incurring) various other burdens.  In particular, the message
0047 content itself might take non-trivial time to build up.  It is desirable that
0048 in the case of a suppressed message, this work be avoided:
0049 
0050 4. Further savings of activity:
0051    a. The various operator<<'s appearing in the message-issuing statement  
0052       are not executed.
0053    b. Any functions called to prepare for those operator<<'s are not called.
0054    
0055 If suppression is dictated by a compile-time define, then a fifth savings can 
0056 be imagined:
0057 
0058 5. Savings of space:  
0059    a. The code for the various operator<<'s and or functions appearing in the 
0060       statement issuing the message does not even apear in the executable.  
0061 
0062 The non-execution of functions appearing in a statement involving a 
0063 suppressed LogDebug, while quite desirable to our community, violates C++
0064 semantics about expression evaluation.  The user "has a right to expect"
0065 that in A(b) << foo(), the function foo() is called and any side effects
0066 will happen.  Creating a situation in which these semantics are violated 
0067 can be accomplished via a pre-preocessor macro or define (and I suspect 
0068 ONLY that way).  The reason we are somewhat comfortable with this violation 
0069 of C++ expression evaluation semantics is that this is the identical situation
0070 occuring in the context of the assert macro.  Users are familiar with the fact
0071 that using a function with side effects inside an assert is poison, and 
0072 nobody takes arms against the writer of assert on that issue.
0073 
0074 Enabling LogDebug by compile-time define
0075 ----------------------------------------
0076 
0077 In the new code, LogDebug and LogTrace are by default always suppressed.
0078 To enable them, one must define EDM_ML_DEBUG.  
0079 
0080 To do this on the build command, one does
0081 
0082 scram b clean; scram b USER_CXXFLAGS="-DEDM_ML_DEBUG"
0083 
0084 There is a bit of a subtlety.  Scram does check for changes in these user flags 
0085 when deciding what does and does not require recompilation.  But this implies
0086 that everything will be recompiled.  In principle you would rather recompile
0087 only files that included MessageLogger.h (however, we notice that is **is**
0088 pretty much everything).  It would be even better to recompile only files that
0089 use LogDebug or LogTrace.  This we cannot force the build system to do.  Thus
0090 we either need to do scram v clean first (as in the above mantra) or touch
0091 MessageLogger.h.
0092 
0093 A consequence is that if some irrelevant file has advanced to a new version,
0094 then without using the versioning capabilities of cvs, there is no way USING
0095 THE SCRAM COMMAND LINE ALONE to just enable LogDebug for your files without
0096 moving to the new version of the irrelevant files.  
0097 
0098 Of course, one can place a 
0099   #define EDM_ML_DEBUG
0100 into one's own code, and that will enable LogDebug for just that file.
0101 
0102 Status before Sep 2010
0103 ----------------------
0104 
0105 In the original idea for LogDebug, a compile-time define would cause LogDebug 
0106 to become a complete no-op, resulting in code that was identical to the code 
0107 with the LogDebug statement commented out.  This turned out not to be as easy 
0108 as originally anticipated, and indeed the original code missed the mark in
0109 (at least) two ways:
0110 
0111 1. When the suppression was specified at compile time, the code for 
0112    functions needed to prepare the data composing the message was still
0113    being generated, and the code implementing the functions to prepare 
0114    the message was still being executed, because the Suppress_LogDebug_ class 
0115    was still doing operator<<, treated in this way:
0116    
0117     Suppress_LogDebug_ &operator<< (T const & t) { return *this; }      
0118     
0119    That implies that if the rhs of the operator<< is foo(), then foo() must
0120    be called since from the compiler's perspective foo() might have side 
0121    effects. 
0122 
0123 2. When the suppression was specified dynamically, the compiler inherently 
0124    could not avoid generating the code for foo(), but it also would be 
0125    executing foo(), and that (wee shall see) could have been avoided.
0126 
0127 Although some care was taken to expose, by inlining, as much of the mechanism 
0128 as possible so that a good compiler could know to throw everything away, the 
0129 compiler does not know (and cannot be told) that the individual streaming 
0130 operators and/or the functions called to compute the data being streamed are 
0131 "pure."   Empirically, therefore, LogDebug occupied non-negligible time (in 
0132 some cases) even when suppressed.
0133 
0134 The solution to this dilemma, at least for the compile-time case, lies in
0135 a lexical technique suggested by C. Jones.  This forces the pre-preocessor to
0136 generate a statement such that the possibly-expensive function and operator<< 
0137 calls are REQUIRED (by the C++ standard) to be elided by the compiler.
0138 
0139 In the dynamic case, the solution is to not to leave the realm of the
0140 macros until after the decision-point about suppression.  This complicates
0141 matters due to the two-levels of macro, but is not conceptually any different.
0142 
0143 Given that LogDebug() behaves logically like a ctor, it is less than 
0144 trivial to implement these techniques; more about that below in the Coding 
0145 Details section.
0146 
0147 
0148 Details of how suppression booleans are maintained
0149 --------------------------------------------------
0150 
0151 I will use edm::LogInfo as an example; the WARNING level loggers behave
0152 similarly, ERROR level messages cannot be suppressed, and LogDebug has a 
0153 special macro structure discussed below for placing the line number into 
0154 the message.
0155 
0156 Let's start from the business end (how the suppression boolean is used)
0157 and trace back to see how it is modified and finally how it is set up
0158 and how modification policy is established.
0159 
0160 The variable obtained by edm::MessageDrop::instance()->infoEnabled 
0161 is the relevant suppression boolean.  It is used in the ctor of LogInfo
0162 to decide whether to initialize a unique_ptr to a MessageSender with
0163 an actual MessageSender, or with 0.  
0164 
0165 In MessageLogger/interface/MessageLogger.h:
0166 class LogInfo                           
0167 {
0168 public:
0169   explicit LogInfo( std::string const & id ) 
0170     : ap ( edm::MessageDrop::instance()->infoEnabled ? 
0171       new MessageSender(ELinfo,id) : 0 )
0172   { }
0173 // ...
0174 }
0175 
0176 The act of destroying the MessageSender, which occurs when in the dtor of
0177 LogInfo this unique_ptr is destructed, is what triggers preparation of the
0178 ErrorObject and placing that ErrorObject onto the MessageLoggerQueue.  
0179 That is, if the MessageSender is constructed, then INEVITABLY the message 
0180 will appear on the queue, and such a message by definition was not suppressed.
0181 
0182 How does edm::MessageDrop::instance()->infoEnabled evolve?
0183 
0184 If we look in MessageService/src/MessageLogger.cc (note that the other header
0185 we looked at was in MessageLogger, not MessageService), we see that various
0186 calls to the following helper functions are set up as callbacks:
0187 
0188 establishModuleCtor  is called by
0189   preModuleConstruction
0190   preSourceConstruction
0191   
0192 unEstablishModule is called by 
0193   preModuleConstruction
0194   preSourceConstruction
0195   
0196 establishModule is called by
0197   preModuleEndJob
0198   preModuleEndRun
0199   preModuleEndLumi
0200   preModule
0201   preModuleEndLumi
0202   preModuleEndRun  
0203   preModuleEndJob
0204       
0205 unEstablishModule is called by
0206   preModuleEndJob
0207   preModuleEndRun  
0208   preModuleEndLumi
0209   preModule
0210   preModuleEndLumi
0211   preModuleEndRun  
0212   preModuleEndJob
0213   
0214 So we see that whenever code for some module is running, establishModule
0215 with that module descriptor will have been called.
0216 
0217 In establishModuleCtor and establishModule, values are set up for 
0218 messageDrop->debugEnabled, messageDrop->infoEnabled, and warningEnabled.
0219 
0220 The code first searches for this moduleLable() (label, not name) in the map 
0221 suppression_levels.  If it fines a suppression level for this module,
0222 then it sets each of enabling variables (for example infoEnabled) true if 
0223 the indicated supression level is less than the corresponding severity level.
0224 For example, if suppression_levels["thisModule"] = ELsev_info (or higher),
0225 then infoEnabled is set false; if it is ELsev_debug then infoEnabled is
0226 set true.  If the module label is not found in the list, then  infoEnabled
0227 and warningEnabled are true.
0228 
0229 Current code (as of 9/17/10) treats debugEnabled differently:  First,
0230 it sets up debugEnabled according to two booleans anyDebugEnabledand
0231 everyDebugEnabled (the usage is the obvious) and the map
0232 debugEnabledModules_ (if the bools don't settle the issue and the map 
0233 contains the module lable, then debugEnabled is set true).  Then if 
0234 the suppression_levels map dose not contain this module label, that 
0235 value of debugEnabled is left holding.
0236 
0237 When the module is unEstablished, the value goes back to the original
0238 infoEnabled, as stored in nonModule_infoEnabled (similarly for the others.
0239 nonModule_debugEnabled, nonModule_infoEnabled, and nonModule_warningEnabled 
0240 are given initial values (false, true, true) as a matter of good C++ practice, 
0241 but these initial values are not used.  On the other hand, initial values of
0242 messageDrop->debugEnabled, infoEnabled, and warningEnabled  (which are relevant
0243 for messages issued outside of all modules) are set true in MessageDrop.cc.
0244 
0245 So far, we see that infoEnabled is used to decide whether to create a 
0246 MessageSender, and that infoEnabled is established each time a module is 
0247 entered, based on suppression_levels.  All that remains is to see how
0248 suppression_levels (and, for logDebug, anyDebugEnabled, everyDebugEnabled, 
0249 and debugEnabledModules_) get set up.
0250 
0251 In MessageLogger.cc, the ctor of MessageLogger sets suppression_levels
0252 based on the three vectors suppressDebug, suppressInfo and suppressWarning.
0253 Each of those, in turn, were obtained as a vstring from the configuration
0254 (parameters "suppressDebug", "suppressInfo", and "suppressWarning").
0255 Similarly, debugEnabledModules_ is obtained from debugModules, which is
0256 obtained from the vstring parameter "debugModules".  anyDebugEnabled is
0257 set based on debugModules being empty, and if this is the case, 
0258 MessageDrop::debugEnabled  is also set false (which will be relevant 
0259 when outside of all modules). Finally, if any of the values in debugModules
0260 is "*", then everyDebugEnabled_ is set true.
0261    
0262 Current (until Sep 2010) LogDebug Suppression
0263 --------------------------------------------
0264 
0265 The code defining the (macro) LogDebug looked like this:
0266 
0267 #ifdef EDM_MESSAGELOGGER_SUPPRESS_LOGDEBUG 
0268 #define LogDebug(id) edm::Suppress_LogDebug_()
0269 #define LogTrace(id) edm::Suppress_LogDebug_()
0270 #else
0271 #define LogDebug(id)                                 \
0272   ( !edm::MessageDrop::debugEnabled )                \
0273     ?  edm::LogDebug_()                              \
0274     :  edm::LogDebug_(id, __FILE__, __LINE__)
0275 // ...
0276 
0277 class Suppress_LogDebug_ 
0278 { 
0279 public:
0280   template< class T >
0281     Suppress_LogDebug_ &operator<< (T const & t) { return *this; }      
0282 };  // Suppress_LogDebug_
0283 
0284 This implies that **even if debugEnabled is false** a line like
0285 
0286 LogDebug ("cat") << foo(objectA) << objectB;
0287 
0288 will force the compiler, to properly execute any side effects of operator<<
0289 and of foo(objectA), to do the operator<<'s and to call foo(), even though 
0290 the user's intent was surely to skip them.  While operator<<, taken from
0291 SuppressLogDebug_, is trivial, foo() probably is not.
0292 
0293 Similarly, if debugEnabled is false when LogDebug is not suppressed by the 
0294 define, then even though no MessageSender will be created, the operator<<
0295 and foo() calls to construct the message still will be executed.  
0296 
0297 Placing line numbers into a message
0298 -----------------------------------
0299 
0300 In order to place line number and source file name into a message, 
0301 the LogDebug token **must** be a macro -- nobody is interested in
0302 the line and file of the place in MessageLogger.h or .cc that the
0303 ctor of some LogDebug class appears.
0304 
0305 In retrospect, it was this forcing of the usse of a macro that enticed 
0306 us to provide assert-like behavior in terms of eliding LogDebug in the
0307 debug-disabled case.  For code in the MessageFacility package, where users
0308 have requested forms of LogInfo etc. having line numbers and file names,
0309 this same issue will arise again, and we should carefully think out what
0310 we and theusers want to do. 
0311 
0312 Coding details in moving to optimal suppression
0313 -----------------------------------------------
0314 
0315 For LogDebug (and LogTrace) ONLY we wish to have a general suppression 
0316 variable which does not do anything with the messageDrop singleton and 
0317 is controlled solely by a define.  
0318 
0319 The goal is to prevent the execution of operator<< and foo(), and ideally to 
0320 prevent even the inclusion of non-executed code for the operator<< and foo(), 
0321 when the user does something like
0322 
0323   LogDebug ("cat") << foo(objectA )<< objectB;
0324 
0325 and the LogDebug is suppressed.  Part one of this goal is to achieve that
0326 optimization when the information indicating suppression is via a macro.
0327 Part two is to (if we can) achieve the optimization under control of a 
0328 run-time variable.
0329 
0330 The trick for part one (suggested by C. Jones) exploits the lexical 
0331 properties of the preprocssor, to constuct a statement that contains
0332 a ?: conditional, with the predicate being compile-time true, and the
0333 operator<< constructs coming out as the tail end of the (unexecuted)
0334 if-false statement. Thus:
0335 
0336   #define LogDebug(id) true ? edm::SuppressLogDebug() :edm::SuppressLogDebug() 
0337 
0338 such that the user statement becomes 
0339 
0340   true ? edm::SuppressLogDebug() :edm::SuppressLogDebug() 
0341                                                 << foo(objectA) << objectB;
0342 
0343 and this is equivalent (known at compile time) to
0344 
0345   edm::SuppressLogDebug();
0346 
0347 which, we notice does not even contain the code for foo(objectA) or any
0348 operator<<.
0349 
0350 Part two can also be accomplished, although in that case the code for the
0351 operator<<'s and foo() obviously will be generated.  Consider the 
0352 following two-tier construct.
0353   
0354   #define MIGHTSUPPRESSLOGDEBUG(id) disabling_variable ? \\
0355           SuppressLogDebug_() ? LogDebug_(id)
0356 
0357   #ifdef DISABLING_SYMBOL 
0358   #define LogDebug(id) true ? SuppressLogDebug_() : SuppressLogDebug_() 
0359   #else
0360   #define LogDebug(id) MIGHTSUPPRESSLOGDEBUG(id)
0361   #endif
0362                                           
0363 Then if DISABLING_SYMBOL is true, the user statement becomes                                      
0364 
0365   true ? edm::SuppressLogDebug() :edm::SuppressLogDebug() 
0366                                                 <<foo(objectA) << objectB;
0367 
0368 which we already saw satisfies the part 1 goal.  If DISABLING_SYMBOL is false,
0369 the user statement becomes
0370 
0371   disabling_variable ? edm::SuppressLogDebug_() 
0372   : LogDebug_(id) << foo(objectA) << objectB;
0373   
0374 Clearly, if disabling_variable is false, you get the proper LogDebug_ call with
0375 the associated streaming.  If disabling_variable is true, you get a ctor of
0376 SuppressLogDebug_() as the entire statement; the anonymous object immediately
0377 goes out of scpe when tthe statement ends, and thus nothing is done.  So this
0378 satisfies part two.  
0379 
0380 We should choose symbol names that are appropriate for the CMS EDM, of course.
0381 It has already been specified that the define the user sees to enable 
0382 LogDebug should be EDM_ML_DEBUG.  Since this is now to be disconnected from
0383 NDEBUG and other such symbols, we can use it directly.  The  
0384 MIGHTSUPPRESSLOGDEBUG is an internal to the MessageLogger, but those too
0385 should start with EDM_ML_; a good name is EDM_ML_ENABLED_LOGDEBUG.  But we
0386 don't even need that extra level of "indirection" since we are already 
0387 going to a #define for LogDebug(id).  So the code would look like:
0388 
0389   #ifndef EDM_ML_DEBUG 
0390   #define LogDebug(id) true ? edm::Suppress_LogDebug_() : edm::Suppress_LogDebug_() 
0391   #else
0392   #define LogDebug(id) \\
0393         isDebugEnabled() ? edm::Suppress_LogDebug_() : \\
0394          edm::LogDebug_(id, __FILE__, __LINE__) 
0395   #endif
0396 
0397 However, the dynamic case of this (if EDM_ML_DEBUG is defined) is not quite
0398 correct C++, since the two possible values for the ?: operator have different
0399 types which cannot be converted to one another.  
0400 
0401 
0402 Here is the "Aha!" moment:  Since in each of the cases where debug is disabled,
0403 there is nothing in the expression after Suppress_LogDebug_(), there would
0404 be no harm in using LogDebug() -- the default ctor -- instead.   The insight 
0405 is that even though nothing was done to make the operator<< be effortless,
0406 that is fine because the operator<< won't appear in executed code!
0407 
0408   #ifndef EDM_ML_DEBUG 
0409   #define LogDebug(id) true ? edm::Suppress_LogDebug_() : edm::Suppress_LogDebug_() 
0410   #else
0411   #define LogDebug(id) \\
0412         isDebugEnabled() ? edm::LogDebug() : \\
0413          edm::LogDebug_(id, __FILE__, __LINE__) 
0414   #endif
0415 
0416 Finally, because these techniques use preprocessor macros, we need to 
0417 look at what happens if the user statement is broken into multiple lines.
0418 As our example:
0419 
0420   LogDebug ("cat") << foo(objectA )
0421                    << objectB;
0422 
0423 In the absense of EDM_ML_DEBUG this would become
0424 
0425   true ?edm::SuppressLogDebug_() : edm::SuppressLogDebug_() << foo(objectA)
0426                     << objectB;
0427 
0428 which is fine.  If EDM_ML_DEBUG is defined it becomes
0429 
0430   !debugEnabled ? edm::SuppressLogDebug_() : \\
0431   edm::LogDebug_(id, __FILE__, __LINE__) << foo(objectA) 
0432                      << objectB;
0433 
0434 which is also fine.
0435 
0436 Effect on "message blocks"
0437 --------------------------
0438 
0439 A technique recommended if the user needs to prepare a message in a loop
0440 or with conditional code looks like:
0441 
0442   if (isInfoEnabled()) { 
0443     edm::LogInfo m("cat");
0444     for (i=0; i<imax; ++i) m << foo(i);
0445   } // m goes out of scope, causing the message to be sent.
0446   
0447 Let's see what happens when we use that with LogDebug instead, in light of 
0448 these macros.     
0449 
0450   if (isDebugEnabled()) { 
0451     LogDebug m("cat");
0452     for (i=0; i<imax; ++i) m << foo(i);
0453   } // m goes out of scope, causing the message to be sent.
0454   
0455 becomes  a pre-processing error whether or not EDM_ML_DEBUG is defined 
0456 (since LogDebug is a macro requiring an argument, but LogDebug m("cat")
0457 breaks that up at the m).  Pre-processor changes in the next version of C++
0458 might allow us to do something more robust, but we need to provide a mantra
0459 that works now.
0460 
0461 We will need to recommend a different mantra for LogDebug.  The following
0462 will work:
0463 
0464   #ifdef EDM_ML_DEBUG
0465   if (isDebugEnabled()) { 
0466     edm::LogDebug_ m (LogDebug ("cat"));
0467     for (i=0; i<imax; ++i) m << foo(i);
0468   } // m goes out of scope, causing the message to be sent.
0469   #endif
0470   
0471 If EDM_ML_DEBUG is defined that becomes 
0472 
0473   if (isDebugEnabled()) { 
0474     edm::LogDebug_ m (edm::LogDebug_("cat", __FILE__, __LINE__));
0475     for (i=0; i<imax; ++i) m << foo(i);
0476   } // m goes out of scope, causing the message to be sent.
0477   
0478 while if EDM_ML_DEBUG is not defined that becomes nothing at all.
0479 
0480 We will need to document this technique difference for LogDebug.
0481 
0482 By the way, is right to inline the functions isDebugEnabled(), 
0483 isInfoEnabled() and isWarningEnabled(), to make this quicker.
0484 In particular, isDebugEnabled() is important since it will be in the 
0485 path of dynamically suppressed LogDebug's.
0486 
0487 Strategy for testing EDM_ML_DEBUG
0488 ---------------------------------
0489 
0490 Existing tests (as of Sep 2010) assumed LogDebug would be enabled buy default.
0491 This was about to change, so naively the tests would need to be modified to 
0492 reflect the change.  
0493 
0494 A first step was to temporarily define EDM_ML_DEBUG in MessageLogger.h
0495 just to see that existing unit tests wre not directly broken by the changes
0496 made, in circumstances where the behavior should not have changed.  That worked
0497 fine.  
0498 
0499 Next we want to see whether our mantra for users to enable LogDebug will work.
0500 So we do a build with the right define on the build line and see if the unit
0501 tests still pass.  
0502   scram b clean; scram b USER_CXXFLAGS="-DEDM_ML_DEBUG"
0503 worked.  Without the clean, scram does not 
0504 
0505 Next, we eliminated that temporary define and had to look at the outputs of all
0506 unit tests that broke.  For the ones breaking in the now-proper way, we
0507 moved the new correct outputs into the output area.  But this is not sufficient:
0508 it would leave us with unit tests that completely skip testiong LogDebug.
0509 
0510 The last step is to create tests that work with LogDebug enabled, and there
0511 are two sorts of these tests:  a) Those where LogDebugs are still suppressed
0512 by configuration; b) those where LogDebugs should cause output.  Fortunately,
0513 once we enable LogDebug, both sorts are tested by our suite, because typical
0514 unit tests produce both a file with LogDebug, and file(s) with higher
0515 thresholds.  But how to force the define of DEDM_ML_DEBUG in some parts
0516 of the build but not others?  That can be done in the buildfile, via 
0517 flags lines.  We choose some of the unit tests to do those on.  
0518 
0519 Where is it determined that u1, for instance, works with UnitTestClient_A?
0520 Certainly not in u1.cpp or u1.sh, which are trivial.  It is in u1_cfg.py,
0521 where we have 
0522   process.sendSomeMessages = cms.EDAnalyzer("UnitTestClient_A")
0523 So if we wish to create a debug-enabled test corresponding to u1, we should
0524 be able to copy u1_cfg.py to u1d_cfg.py, change the UnitTestClient_A to
0525 UnitTestClient_Ad, and in the Buildfile.xml, have something like the lines
0526   <library   file="UnitTestClient_A.cc" name="UnitTestClient_A">
0527     <flags   EDM_PLUGIN="1" -DEDM_ML_DEBUG/>
0528     <use   name="FWCore/MessageLogger"/>
0529     <use   name="FWCore/Framework"/>
0530   </library>
0531 Unfortunately that does not work (parse error on -DEDM_ML_DEBUG) and after a few
0532 more tries, it seemed wrong to continue wandering around without documentation.
0533 Instead of changing the build file, we can make a new client source  
0534 UnitTestClient_Ad.cc, identical to UnitTestClient_A.cc but with 
0535 #define EDM_ML_DEBUG at the top.
0536 
0537 
0538 Optimization when no threshold is low enough
0539 --------------------------------------------
0540 
0541 There was as of 9/21/110 no mechanism to disable LogInfo if all thresholds are 
0542 higher than logInfo, and similarly for logWarning (not sure if the issue
0543 is relevant for logDebug).  This could be a huge savings.  Not easy to do,
0544 but should not be too tricky.  
0545 
0546 The tough part is that while looking for suppressInfo in the configuration 
0547 is easy enough, seeing whether any destination has a high enough threshold
0548 is involved, so that it needs to be done in the configuration code that is
0549 down in the scribe.  So the strategy will have to be setting up a vairiable
0550 that is used in the scribe thread to set the field in the MessageDrop, just
0551 as when a suppressed module is entered.  
0552 
0553 The appropriate variables really are statics in MessageDrop, and we name them
0554 debugAlwaysSuppressed, infoAlwaysSuppressed, warningAlwaysSuppressed.
0555 The usage for the LogInfo and LogWarning cases is that the ctor looks like
0556   explicit LogInfo( std::string const & id ) 
0557     : ap ( (!MessageDrop::infoAlwaysSuppressed          // Change log 21
0558             && edm::MessageDrop::instance()->infoEnabled) ?
0559            new MessageSender(ELinfo,id) : 0 )
0560 Observe that since !MessageDrop::infoAlwaysSuppressed is checked first, 
0561 in the important case of universal suppression, the overhead for the
0562 MessageDrop::instance() call is not incurred -- this is the secondary 
0563 performance improvement.  (The main improvement is that in config files which
0564 do not explicitly suppress INFO, but where no thresholds are as low as INFO,
0565 the info messages get suppressed anyway.)
0566 
0567 The usage is a bit different for the debug case:  The define line (in the
0568 case where EDM_ML_DEBUG is defined so that LogDebug is not out of the question)
0569 becomes:
0570 
0571 #define LogDebug(id) \\
0572 (edm::MessageDrop::debugAlwaysSuppressed \\
0573 || !edm::MessageDrop::debugEnabled) ? \\
0574 edm::LogDebug_() : edm::LogDebug_(id, __FILE__, __LINE__) 
0575 
0576 and, since if  the logDebug is going to be suppressed (for whichever reason)
0577 the default ctor of LogDebug_ is called, which sets the class instance variable
0578 debugEnabled to false, nothing needs to be changed in the LogDebug_ class.
0579 (For reasons of paranoia, in the meaty ctor for LogDebug_, we do initialize 
0580 debugEnabled to !debugAlwaysSuppressed rather than true, but this may be
0581 overkill.)
0582 
0583 That leaves the issue of how (and where) to establish the values of 
0584 debugAlwaysSuppressed and its bretheren.  Since for EVERY destination 
0585 configure_dest (in the scribe) sets the threshold severity explicitly,
0586 this is a common choke point, at which we can adjust these variables.
0587 What that implies is that they must start off true, and become false 
0588 when some destination is configured to have a low enough threshold.
0589 Actually, just in case some path avoids configuration altogether, we
0590 start these off as false by initialization, change them to true once
0591 configure_ordinary_destinations() is reached, and then change them in
0592 configure_dest() if and when a low enough threshold is encountered.
0593 
0594 Having implemented these changes, we now wish to find a clean way of
0595 testing whether the changes were effective.  This is harder than it
0596 looks, since the suprressed messages were not going to have any effect
0597 even before this performance-only improvement.  And one would like to
0598 make this a test that can be part of the test suite.
0599 
0600 The technique to do this testing is as follows:
0601 
0602 1) Add an extra class in MessageLogger.h, namely 
0603    LogWarningThatSuppressesLikeLogInfo.  To protect
0604    against some user using this, hide it in a sub-namespace
0605    edm::edmtest.  This class does its test for suppression
0606    using infoEnabled but uses a severity level of LogWarning.
0607    
0608 2) Create a unit test that does some LogWarning's and some 
0609    LogInfo's, and also some LogWarningThatSuppressesLikeLogInfo's.
0610    
0611 3) Create a cfg file (A) with one destination,which sets a 
0612    threshold of LogWarning, and another (B) with two destinations, one
0613    of which has threshold of LogWarning, the other LogInfo.  Each 
0614    uses this new unit test.
0615    
0616 4) In cmsRun (A), no trace of the LogWarningThatSuppressesLikeLogInfo
0617    should appear.  In cmsRun (B), it should appear in the both destinations,
0618    as a %MSG-w message (warning).
0619 
0620 Possible further improvement opportunities
0621 ------------------------------------------
0622 
0623 The initial value of MessageDrop->debugEnabled (in MessageDrop.cc) is
0624 true.  This is perhaps relevant in cases where debug is overall enabled,
0625 and one is not in any module.  So you run with debug on, and if the 
0626 framework itself has LogDebugs in it, you can't suppress them without
0627 suppressing all debug messages.  It might be better to make this false.
0628 (Trivial change to make.)  It might also be desirable to have some way
0629 to turn on/off non-module LogDebugs.  However, this issue is very likely 
0630 moot since users don't write non-module code, and probably framework 
0631 people are not leaving many LogDebugs around.
0632 
0633 ---
0634 
0635 Currently, LogDebug depends on the define EDM_ML_DEBUG at header time.
0636 So the user who wants to define this symbol must do so before the header,
0637 and has control only on a per-file basis.  It might be better to give users
0638 finer control by putting the #ifdef inside the definition of LogDebug, as in
0639   #define LogDebug(id) \\
0640   #ifndef EDM_ML_DEBUG \\
0641     true ? edm::Suppress_LogDebug_() : edm::Suppress_LogDebug_() \\
0642   #else \\
0643   #define LogDebug(id) \\
0644         isDebugEnabled() ? edm::LogDebug() : \\
0645          edm::LogDebug_(id, __FILE__, __LINE__) \\
0646   #endif
0647 This of course is not quite right, and the business of line ends and
0648 continuations might be a severe pain in the neck, which is why we have not 
0649 made this improvement.
0650 
0651 ---
0652 
0653 This is not strictly speaking only a suppression issue but is in the same code
0654 area.  Currently every time a module is entered or exitted, a call to
0655 MessageDrop::instance() is made to tell where to put information such as
0656 the suppression variables and the module label.  Although nobody is currently
0657 griping about these calls, they may be taking non-negligible time.
0658 If the user could declare "no need for module names in messages" then
0659 when there is also no ML configuration information that is module sensitive 
0660 (that is, any suppress or enable directives use "*"), then we can do without
0661 this instance() call.  Some simple decisions would need to be made, such as 
0662 what module name to output or whether to modify ELoutput to eliminate the 
0663 module name when this option is chosen; and the option would have to be coded
0664 into the configuration and validation and the documentation and into the 
0665 establish_module routines (or into the code that puts the establish_module
0666 routine onto the callback stack in the first place; probably the whole callback
0667 would now be superfluous).
0668 
0669 This would be several day's work but might be worthwhile.
0670 
0671 ---