473,320 Members | 1,699 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,320 software developers and data experts.

Question about singleton class design for tracing

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_Stateptr_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::ofstream 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;
}

May 23 '07 #1
6 3349
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.
May 23 '07 #2
On Wed, 23 May 2007 09:49:37 -0700, Noah Roberts wrote:
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::ostreambuf_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::ostreambuf_iterator<char, std::char_traits<char >::_M_insert_int<long() from /usr/lib/libstdc
+.so.6
#1 0x48022f90 in std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char >::do_put () from /usr/lib/libstdc++.so.6
#2 0x4802b83d in std::ostream::operator<< () 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::operator%<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::open_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_Con fig::Memory_Data_Source_Config
(this=0x8fda2d8) at Memory_Data_Source_Config.cpp:17
#14 0x002b8ccb in libreverse::infrastructure::Configuration_Data::Co nfiguration_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_i nstanceE ()
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::allo cator<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
May 23 '07 #3
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.
May 23 '07 #4
On May 23, 7:10 pm, TvN <tvn...@yahoo.comwrote:
Hi Stephen,
static Trace_State& Instance()
{
if ( m_instance == 0 )
{
m_instance = new Trace_State();
}
return *m_instance;
}
I'd prefer declare m_instance as static variable in this function, and
of course initiate it :)
This has the disadvantage that the destructor will be called,
potentially leading to order of destruction issues.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

May 24 '07 #5
On Thu, 24 May 2007 02:36:44 -0700, James Kanze wrote:
static Trace_State& Instance()
{
if ( m_instance == 0 )
{
m_instance = new Trace_State();
}
return *m_instance;
}
>I'd prefer declare m_instance as static variable in this function, and
of course initiate it :)

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?

May 24 '07 #6
On May 24, 8:38 pm, Stephen Torri <sto...@torri.orgwrote:
On Thu, 24 May 2007 02:36:44 -0700, James Kanze wrote:
static Trace_State& Instance()
{
if ( m_instance == 0 )
{
m_instance = new Trace_State();
}
return *m_instance;
}
I'd prefer declare m_instance as static variable in this function, and
of course initiate it :)
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?
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.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

May 25 '07 #7

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

3
by: Alicia Roberts | last post by:
Hello everyone, I have been researching the Singleton Pattern. Since the singleton pattern uses a private constructor which in turn reduces extendability, if you make the Singleton Polymorphic...
6
by: Kyle | last post by:
Hi, I have a library containing some global Variable. However, it seems that when the library is a static lib the initialization of the global var does not happen. I could not find any answer in...
5
by: Bob | last post by:
Does anyone know of any adverse affects of using singleton data access objects in an ASP.NET Web Service? I've been forced to implement it this way but it just doesn't seem right to me. I haven't...
3
by: Bart Simpson | last post by:
I am writing a communications library, and have several classes in my project. The "main" class (BaseEngine) is responsible for setting up the low level comms machinery (queues, sockets, ports...
15
by: Nick Keighley | last post by:
Hi, I found this in code I was maintaining template <class SingletonClass> SingletonClass* Singleton<SingletonClass>::instance () { static SingletonClass _instance; return &_instance; }
4
by: Andrew Robinson | last post by:
I am working on a large (for me) ASP.NET project that contains about 100 SQL tables on the back end. We built a set of "entity" objects and service provider objects to access our data with one...
3
weaknessforcats
by: weaknessforcats | last post by:
Design Pattern: The Singleton Overview Use the Singleton Design Pattern when you want to have only one instance of a class. This single instance must have a single global point of access. That...
5
by: Frank Moyles | last post by:
I am a developer with many years (approx 10years) development experience using C++ for DESKTOP applications. I am writing a web application using C#, and I wanted to ask a question about...
3
by: stevewilliams2004 | last post by:
I am attempting to create a singleton, and was wondering if someone could give me a sanity check on the design - does it accomplish my constraints, and/or am I over complicating things. My design...
0
by: DolphinDB | last post by:
Tired of spending countless mintues downsampling your data? Look no further! In this article, you’ll learn how to efficiently downsample 6.48 billion high-frequency records to 61 million...
0
by: ryjfgjl | last post by:
ExcelToDatabase: batch import excel into database automatically...
1
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
0
by: Vimpel783 | last post by:
Hello! Guys, I found this code on the Internet, but I need to modify it a little. It works well, the problem is this: Data is sent from only one cell, in this case B5, but it is necessary that data...
0
by: jfyes | last post by:
As a hardware engineer, after seeing that CEIWEI recently released a new tool for Modbus RTU Over TCP/UDP filtering and monitoring, I actively went to its official website to take a look. It turned...
0
by: ArrayDB | last post by:
The error message I've encountered is; ERROR:root:Error generating model response: exception: access violation writing 0x0000000000005140, which seems to be indicative of an access violation...
1
by: CloudSolutions | last post by:
Introduction: For many beginners and individual users, requiring a credit card and email registration may pose a barrier when starting to use cloud servers. However, some cloud server providers now...
1
by: Defcon1945 | last post by:
I'm trying to learn Python using Pycharm but import shutil doesn't work
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 3 Apr 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome former...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.