BIND 10 #559: lib/log test crashes with --enable-static-link

BIND 10 Development do-not-reply at isc.org
Mon Feb 7 14:14:54 UTC 2011


#559: lib/log test crashes with --enable-static-link
-------------------------------------+-------------------------------------
                 Reporter:  jinmei   |                Owner:  stephen
                     Type:  defect   |               Status:  new
                 Priority:           |            Milestone:  R-Team-
  critical                           |  Sprint-20110208
                Component:  logging  |           Resolution:
                 Keywords:           |            Sensitive:  0
Estimated Number of Hours:  0.0      |  Add Hours to Ticket:  0
                Billable?:  1        |          Total Hours:  0
                Internal?:  0        |
-------------------------------------+-------------------------------------

Comment (by stephen):

 > Apparently this is a typical symptom of the static initialization
 > order fiasco. In fact, if I moved the definition of "root" inside
 > main(), it at least stopped crashing.
 Doh! I was so focused on avoid initialization problems between classes I'd
 written that I overlooked the ones within classes.  In the case of
 !RootLoggerName, the singleton variable holding the name has been moved
 inside a static function (so is instantiated when that function is
 called).

 > But the test still kept failing.
 This was the more tricky one.  The idea is that each library/subsystem has
 its own message file and that the message text is pulled in simply by
 ensuring that the object code produced by the .cc file was included in the
 image (i.e. without the need to call any run-time initialization).  With
 dynamic linking, the module is in the library and appears to be brought in
 regardless.  But with static linking, as there is no reference to symbols
 in the module, it does not get included in the final image.

 To get round this I've included in the header file generated by the
 message compiler an "extern" reference to the !MessageInitializer object.
 As this may well get optimised away, the header also declares a
 "!MessageInstantiator" object, passing the address of the
 !MessageInitializer to its constructor.  The !MessageInstantiator is a
 dummy object, comprising a constructor (in an external file to avoid it
 being optimised away) that does nothing.  But this should create the
 reference to the !MessageInitializer object needed to ensure that the
 message text gets included in the image.

 > So I looked into some other parts of lib/log, and found that it relies
 > too much on statically initialized (non const) objects. From a quick
 > look, the "logger" variable defined in logger_support.cc has this
 > problem. The namespace level initialization in logger.cc and
 > root_logger_name.cc seems to be dangerous, too.

 There are three places where this occurs:

 a) Declaration of the root logger name.  This just sets a name for use in
 the logging system; providing this is set before the first call to (any)
 logger is made, it doesn't matter where it is declared.  Now that the
 initialization has been corrected, this can be declared externally or in
 main(), whatever seems best.
 b) Declaration of the message identifiers.  You're right, I missed
 "const". (See below for more discussion on message identifiers.)
 c) Declaration of the loggers.  At the risk of premature optimisation, I
 wanted to minimise the overhead of call to a logging function that does
 not result in output. As a logger needs to be created at some point:
    * Putting it in the method making the call incurs the overhead of
 instantiation and destruction each time the method is called.
    * Declaring the logger as part of the class (so instantiating it once
 for an instance of the class) does not seem right, as the logger is not
 logically part of the class.  Also, we may want logging in objects where
 thousands are created and destroyed.
    * Declaring the logger as a static variable in the method.  Certainly
 possible, but we could end up with many declarations of the same logger in
 a file.
    * Declaring it as external - a single declaration in a file.
 The logger is written so that it can be declared in any of these contexts.
 To avoid initialization problems (and the interaction with the (possible)
 static initialization of the root logger name), the logger uses "pimpl"
 but defers instantiating the implementation class until the first time any
 of its methods is called.

 > I suggest revisiting overall initialization in lib/log so that it will
 > be more robust. Since this bug can (potentially) make the application
 > crash, I'd categorize it as "critical".
 Done

 > BTW, I happen to notice messagedef.h uses an unnamed namespace. It's
 > generally a discouraged practice as it can easily break the "one
 > definition rule".
 The message file can now contain a $NAMESPACE directive to set the
 namespace in which the symbols are created, and they are declared as
 "static" in it.


 This brings up the case of message identifiers.  Mainly because of the
 requirement to be able to replace the message text at run-time, message
 identifiers are text.  I originally declared them as strings, owing to the
 possibility that someone might return a message ID as a status return code
 and then compare it against other message IDs.  However, as you brought to
 notice, this could run the risk of a static initialization fiacso if
 someone tried to access a message ID in a statically declared object.

 To avoid this, the code was changed slightly and message identifiers are
 now declared as "const char*".  This still gives the advantage of text
 (and avoids the need to run constructors on potentially thousands of
 message IDs at program start-up time), but now introduces a different
 problem; "a ==b" is only guaranteed to work if both IDs are obtained from
 the same module.  If two modules each include the message header file, it
 is possible that two instances of the string are created, each ending up
 at a separate address.

 To get round this, message_types.h includes a function "equalsMessageID()"
 to perform equality checks.  But a different way occurs to me: declare the
 symbols as external, i.e. in the .h file declare "extern const char*
 symbol;" and in the .cc file define the symbol "const char* symbol =
 "value";".  This:
  * solves the problems of symbol equality - all symbols of the same name
 will have the same value.
  * allows the removal of the "!MessageInstantiator" object - the .cc file
 will always be included in the link in order to resolve the symbol
 definitions.

 However, this solution does break encapsulation by generating many
 external references in each module that defines message IDs, and for that
 reason I'm hesitant of it.

 '''Review'''[[BR]]
 The set of changes addressing these points is included on ticket #558, and
 that should be the one used handle the source code review.

-- 
Ticket URL: <http://bind10.isc.org/ticket/559#comment:2>
BIND 10 Development <http://bind10.isc.org>
BIND 10 Development


More information about the bind10-tickets mailing list