Saturday, January 3, 2009

Don't use defines for string constants in C++

I wrote this one up because I could not find anything about this on the web. Someone wrote some code to
delete temporary files. But the files were not being deleted. And then we ported the code to another platform.
Well this problem turned out to be a destructor chain problem. It was not a memory leak per say or a logic
bug. Rather this was due to the ambiguity in the ISO C++ standard according to a C++ expert who has worked
on C++ debuggers.

The ISO C++ standard does not clearly spell out when the text segment is deallocated.
We had a bug which showed on Mac OS X and not on CENTOS Linux.

A destructor chain was calling a function in a object that used a global string variable.
The string variable was undefined at the time the destructor chain was executed. On
the Mac STL threw a strlen exception. Valgrind did not find the problem on Linux. Leaks
did not find the problem on the Mac. We replaced the defines with a C++ class containing
static strings (a singleton class) and the exception went away.

The way we found the problem was by running gdb and noticing that the destructor chain
contained a call to a non-destructor function. Then through a process of elimination we found
the offending var. The string had the correct value in Linux and the incorrect value in the Mac.
We then put a local var declaration and assignment in the function called by the destructor and
the bug went away.

(gdb) backtrace
#0 0x938e6b9e in __kill ()
#1 0x938e6b91 in kill$UNIX2003 ()
#2 0x9395dec2 in raise ()
#3 0x9396d47f in abort ()
#4 0x96e5e005 in __gnu_cxx::__verbose_terminate_handler ()
#5 0x96e5c10c in __gxx_personality_v0 ()
#6 0x96e5c14b in std::terminate ()
#7 0x96e5c261 in __cxa_throw ()
#8 0x96e1ccaa in std::__throw_length_error ()
#9 0x96e43f5a in std::string::_Rep::_S_create ()
#10 0x96e4429a in std::string::_Rep::_M_clone ()
#11 0x96e4540e in std::basic_string, std::allocator >::basic_string ()
#12 0x00c3cfbf in NSCLi::CLiCommon::getRoot (path=@0xbfffee2c) at cslcCLI_Support.cpp:363<<<<<<<<<#13 0x00c3d7ac in NSCLi::CLiCommon::deleteFile (fileName=@0x1cbc610) at
/opt/he_fpl_svn/fpl_repo/cslc/trunk/src/support/cli/cslcCLI_Support.cpp:227
#14 0x00002c11 in NSCslc::CSLcMain::deleteTempFile (this=0x1cbab00, fileName=@0x1cbab80) at new_cslc.cpp:199
#15 0x00002d8f in NSCslc::CSLcMain::~CSLcMain (this=0x1cbab00) at
/opt/he_fpl_svn/fpl_repo/cslc/trunk/src/cslc/new_cslc.cpp:49
#16 0x00021171 in boost::checked_delete (x=0x1cbab00) at checked_delete.hpp:34
#17 0x000265ea in boost::detail::sp_counted_impl_p::dispose (this=0x1cbb960) at
detail/sp_counted_impl.hpp:79
#18 0x0001a6e0 in boost::detail::sp_counted_base::release (this=0x1cbb960) at detail/sp_counted_base_gcc_x86.hpp:145
#19 0x0001a71c in boost::detail::shared_count::~shared_count (this=0x10578ac) at detail/shared_count.hpp:205
#20 0x0001bd18 in boost::shared_ptr::~shared_ptr (this=0x10578a8) at shared_ptr.hpp:131
#21 0x0001914d in __tcf_223 () at new_cslc.cpp:34
#22 0x938a0fdc in __cxa_finalize ()
#23 0x938a0ed0 in exit ()
#24 0x00001ec7 in start ()
(gdb)

After determining what the problem is we then changed the defines into a tool class. This required moving the defines into the class. We then prefixed the variables where they were used with the tool class scope qualifier.

No comments:

Post a Comment