Question about singleton class design for tracing

S

Stephen Torri

I am trying to produce a singleton class that I can use throughout my
library to write tracing information to a file. My intent was to design
such that someone using the library in its debug mode would be able to
see what was happening without having to use a debugger to step through
each instruction. What they would do is run their program and view the
tracing file output. If there was something wrong then they would use the
debugger of their choosing.

What I am running into is a segfault when I run a test program that uses
on part of the library rather than through the main api interface. The
segfault is coming from the std::num_put as a part of the STL. Since that
is so well tested I am assuming my problem lies in my design of the
singleton class. I modeled it after the GoF singleton pattern.

I would appreciate any helps on the design:

#include <boost/format.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <sstream>
#include <fstream>

#ifndef WIN32
#include <unistd.h>
#else
#include <windows.h>
#endif /* WIN32 */
namespace libreverse { namespace api {

class TraceLevel {
public:
static boost::uint32_t TraceNone; // No trace
static boost::uint32_t TraceWarn; // Only trace warning
static boost::uint32_t TraceError; // Only trace error
static boost::uint32_t TraceInfo; // Some extra information
static boost::uint32_t TraceDebug; // Debugging information
static boost::uint32_t TraceDetail; // Detailed debugging
information
static boost::uint32_t TraceData; // Output data
};

boost::uint32_t TraceLevel::TraceNone = 0;
boost::uint32_t TraceLevel::TraceWarn = 10;
boost::uint32_t TraceLevel::TraceError = 20;
boost::uint32_t TraceLevel::TraceInfo = 30;
boost::uint32_t TraceLevel::TraceDebug = 40;
boost::uint32_t TraceLevel::TraceDetail = 50;
boost::uint32_t TraceLevel::TraceData = 60;

} /* namespace api */
} /* namespace libreverse */

namespace libreverse { namespace trace {

class Trace_State {
public:

typedef boost::shared_ptr<Trace_State> ptr_t;

static Trace_State& Instance()
{
if ( m_instance == 0 )
{
m_instance = new Trace_State();
}

return *m_instance;
}

void set_Trace_File_Prefix ( std::string name )
{
assert ( ! name.empty() );

// Lock the resource
// Close the present file
m_file_prefix = name;
// Unlock the resource
}

void set_Trace_Level ( boost::uint32_t level )
{
// Lock the resource
// Change level
m_trace_level = level;
// Unlock the resource
}

void open_Trace_File ( void )
{
if ( ! m_log_stream.is_open() )
{
// Create file name
std::stringstream name;

name << boost::format("%s_%s.txt")
% m_file_prefix
% this->get_ID_String();

m_log_stream.open ( (name.str()).c_str() );
}
}

std::string get_ID_String ( void )
{
// Create id string
std::stringstream name;

// Get current time
boost::posix_time::ptime now =
boost::posix_time::second_clock::local_time();

std::tm tm_ref = boost::posix_time::to_tm ( now );

boost::gregorian::date today = now.date();

name << boost::format ( "%s_%02d:%02d:%02d" )
% boost::gregorian::to_iso_extended_string ( today )
% tm_ref.tm_hour
% tm_ref.tm_min
% tm_ref.tm_sec;

return name.str();
}

void close_Trace_File ( void )
{
if ( m_log_stream.is_open() )
{
m_log_stream.close();
}
}

boost::uint32_t get_Trace_Level ( void ) const
{
boost::uint32_t level = 0;

// Lock the resource

// get the level
level = m_trace_level;

// unlock the resource

// return the level
return level;
}

void write_Message ( boost::uint32_t level, std::string msg )
{

// Write ID
m_log_stream << boost::format("%s_%d: " )
% this->get_ID_String()
#ifndef WIN32
% getpid()
#else
% GetCurrentProcessId()
#endif /* WIN32 */
<< std::flush;

// Write message prefix
if ( level == libreverse::api::TraceLevel::TraceWarn )
{
m_log_stream << "(WW) ";
}
else if ( level == libreverse::api::TraceLevel::TraceError )
{
m_log_stream << "(EE) ";
}
else if ( level == libreverse::api::TraceLevel::TraceInfo )
{
m_log_stream << "(II) ";
}
else if ( level == libreverse::api::TraceLevel::TraceDebug )
{
m_log_stream << "(DEBUG) ";
}
else if ( level == libreverse::api::TraceLevel::TraceDetail )
{
m_log_stream << "(DETAIL) ";
}
else if ( level == libreverse::api::TraceLevel::TraceData )
{
m_log_stream << "(DATA) ";
}
else
{
// We should not be here
abort();
}

// Write to the file
m_log_stream << msg << std::endl << std::flush;

// Unlock the resource
}

private:

Trace_State()
: m_file_prefix ( "Trace" ),
m_trace_level ( libreverse::api::TraceLevel::TraceNone )
{}

~Trace_State()
{
delete m_instance;
this->close_Trace_File();
}

static Trace_State* m_instance;

std::string m_file_prefix;

boost::uint32_t m_trace_level;

std::eek:fstream m_log_stream;
};

class Trace {
public:
#ifdef LIBREVERSE_DEBUG
bool write_Trace ( boost::uint32_t level,
std::string message )
{
// If the level is equal to or greater than the present
// level we record out message.
if ( ( Trace_State::Instance().get_Trace_Level() != 0 ) &&
( level <= Trace_State::Instance().get_Trace_Level() ) )
{
Trace_State::Instance().write_Message ( level,
message );
}

return true;
}
#else
bool write_Trace ( boost::uint32_t,
std::string )
{
return true;
}
#endif
};

Trace_State* Trace_State::m_instance = 0;

} /* namespace trace */
} /* namespace libreverse */

using namespace libreverse::trace;
using namespace libreverse::api;

int main ( int, char** )
{
Trace_State::Instance().set_Trace_Level ( TraceLevel::TraceDetail );
Trace_State::Instance().open_Trace_File ();

Trace_State::Instance().close_Trace_File ();

return 0;
}
 
N

Noah Roberts

I don't see anything inherently wrong with the singleton aspect.
Doesn't mean there isn't one or that your use isn't causing a problem.
I suggest breaking out the debugger.
 
S

Stephen Torri

I don't see anything inherently wrong with the singleton aspect. Doesn't
mean there isn't one or that your use isn't causing a problem. I suggest
breaking out the debugger.

Breaking out the debugger leaves me confused. I changed the class so that Instance() returned a pointer to m_instance.
Here is a trace

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread -1208285488 (LWP 27865)]
0x48022d11 in std::num_put<char, std::eek:streambuf_iterator<char,
std::char_traits<char> > >::_M_insert_int<long> () from /usr/lib/libstdc++.so.6
(gdb) bt
#0 0x48022d11 in std::num_put<char, std::eek:streambuf_iterator<char, std::char_traits<char> > >::_M_insert_int<long> () from /usr/lib/libstdc
+.so.6
#1 0x48022f90 in std::num_put<char, std::eek:streambuf_iterator<char, std::char_traits<char> > >::do_put () from /usr/lib/libstdc++.so.6
#2 0x4802b83d in std::eek:stream::eek:perator<< () from /usr/lib/libstdc++.so.6
#3 0x0027729a in boost::io::detail::put_last<char, std::char_traits<char>, int> (os=@0xbfd13d04, x=@0xbfd13f8c) at /usr/
include/boost/format/feed_args.hpp:113
#4 0x00277561 in boost::io::detail::put<char, std::char_traits<char>, std::allocator<char>, int&> (
x=@0xbfd13f8c, specs=@0x8fdab14, res=@0x8fdab18, buf=@0xbfd13ff8, loc_p=0x0)
at /usr/include/boost/format/feed_args.hpp:167
#5 0x00277b65 in boost::io::detail::distribute<char, std::char_traits<char>, std::allocator<char>, int&> (
self=@0xbfd13fc0, x=@0xbfd13f8c) at /usr/include/boost/format/feed_args.hpp:241
#6 0x00277bc4 in boost::io::detail::feed<char, std::char_traits<char>, std::allocator<char>, int&> (
self=@0xbfd13fc0, x=@0xbfd13f8c) at /usr/include/boost/format/feed_args.hpp:251
#7 0x00277c8a in boost::basic_format<char, std::char_traits<char>, std::allocator<char> >::eek:perator%<int> (
this=0xbfd13fc0, x=@0xbfd13f8c) at /usr/include/boost/format/format_class.hpp:68
#8 0x0026f300 in libreverse::trace::Trace_State::get_ID_String (this=0x8fda338) at Trace.cpp:80
#9 0x0026f68b in libreverse::trace::Trace_State::eek:pen_Trace_File (this=0x8fda338) at Trace.cpp:56
#10 0x0026f8ae in libreverse::trace::Trace_State::Trace_State (this=0x8fda338) at Trace.cpp:165
#11 0x0026f92b in libreverse::trace::Trace_State::Instance () at Trace.cpp:20
#12 0x0026f97e in libreverse::trace::Trace::write_Trace (level=50, message=@0xbfd14288) at Trace.cpp:181
#13 0x003a177c in libreverse::infrastructure::Memory_Data_Source_Config::Memory_Data_Source_Config
(this=0x8fda2d8) at Memory_Data_Source_Config.cpp:17
#14 0x002b8ccb in libreverse::infrastructure::Configuration_Data::Configuration_Data (this=0x8fda2b0)
at Configuration_Data.cpp:19
#15 0x002bf2da in __static_initialization_and_destruction_0 (__initialize_p=1, __priority=65535)
at Configurator.cpp:24
#16 0x002bf34d in global constructors keyed to _ZN10libreverse14infrastructure12Configurator10m_instanceE ()
at Configurator.cpp:159
#17 0x003a4aa6 in __do_global_ctors_aux () from /usr/local/lib/libreverse.so.0
#18 0x002439f1 in _init () from /usr/local/lib/libreverse.so.0
#19 0x47ba1fc3 in call_init () from /lib/ld-linux.so.2
#20 0x47ba20d3 in _dl_init_internal () from /lib/ld-linux.so.2
#21 0x47b9490f in _dl_start_user () from /lib/ld-linux.so.2

If I look at Frame #8 in gdb

76 name << boost::format ( "%s_%02d:%02d:%02d" )
77 % boost::gregorian::to_iso_extended_string ( today )
78 % tm_ref.tm_hour
79 % tm_ref.tm_min
80 % tm_ref.tm_sec;

I see we are pointing to my call to boost::format. The variable 'today'
is a valid boost::gregorian::date object (0xbfd13fb0). As well as tm_ref:

(gdb) p tm_ref
$3 = {
tm_sec = 7,
tm_min = 53,
tm_hour = 11,
tm_mday = 23,
tm_mon = 4,
tm_year = 107,
tm_wday = 3,
tm_yday = 142,
tm_isdst = -1,
tm_gmtoff = -1076805688,
tm_zone = 0x480105c5 "\211�213E\b1�213"
}

The only thing that is suspect at this point is the Trace object itself:

(gdb) p *this
warning: RTTI symbol not found for class 'libreverse::trace::Trace_State'
$5 = warning: RTTI symbol not found for class 'libreverse::trace::Trace_State'
{
_vptr.Trace_State = 0x499570,
static m_instance = 0x0,
m_file_prefix = {
static npos = 4294967295,
_M_dataplus = {
<allocator<char>> = {
<new_allocator<char>> = {<No data fields>}, <No data fields>},
members of basic_string<char,std::char_traits<char>,std::allocator<char> >::_Alloc_hider:
_M_p = 0x8fda474 "Trace"
}
},
m_trace_level = 0,
m_log_stream = <incomplete type>
}

The variable m_instance is still null, which it should not be. I will investigate it some more.

Stephen
 
S

Stephen Torri

I wondered if the boost::format was a problem. So I removed it from the
Trace class. The cleared the segmentation fault from within the Trace
class. Now the segfault appears to be occurring in another place in the
library. I am wondering why this is the case. I will try to build this
program under Windows with Visual Studio. I have been using gcc-4.1.1 on
Fedora Core 6. Perhaps VS2005 tell me why this is happening.
 
S

Stephen Torri

This has the disadvantage that the destructor will be called,
potentially leading to order of destruction issues.

Right now I am trying to track down a segfault that occurs in my program.
I find the segfault always has my tracing class as the last point in my
program where control goes through before heading into the STL code. If I
comment out that use of the tracing library then the segfault at that
location disappears. It will occur at the next location using the tracing
library. So I believe the problem is the tracing library design as a
singleton. How can I create a singleton that will not suffer from the
"order of destruction" issue?
 
J

James Kanze

Right now I am trying to track down a segfault that occurs in my program.
I find the segfault always has my tracing class as the last point in my
program where control goes through before heading into the STL code. If I
comment out that use of the tracing library then the segfault at that
location disappears. It will occur at the next location using the tracing
library. So I believe the problem is the tracing library design as a
singleton. How can I create a singleton that will not suffer from the
"order of destruction" issue?

As above. Just allocate the object dynamically, and never
delete it.

Note, however, that order of destruction will never be a problem
until you call exit.

I can't look at your original code to see what else might be
wrong. I'm reading news through Google Groups, and they do
something which causes Firefox under Linux to crash in certain
specific cases, at least when using a remote X Server. Looking
at the start of this thread is one of them.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,968
Messages
2,570,150
Members
46,696
Latest member
BarbraOLog

Latest Threads

Top