Main Page   Reference Manual   Namespace List   Compound List   Namespace Members   Compound Members   File Members  

Preparation

Preparation

This page describes the preparations that you need to perform before starting to use libcwd in your applications source code.

Step 1: Installing libcwd

Binary distributions should be installed the usual way.

If you are installing libcwd from source then please read the INSTALL file that is included in the source distribution.
See also: Configuration Options And Macros

Step 2: Creating the custom header files

You need to add two custom header files to your application.
The recommended names are "debug.h" and "sys.h".

You can use the following templates for a quick start:

sys.h example template
// sys.h
//
// This header file is included at the top of every source file,
// before any other header file.
// It is intended to add defines that are needed globally and
// to work around Operating System dependend incompatibilities.
// The following includes the libcwd related mandatory part,
// which must be included before any system header file is included!
// Piggy back on cwds.
#include "cwds/sys.h"
// Add project specific parts here.
«download»
debug.h example template
// Piggyback on cwds
#pragma once
#define NAMESPACE_DEBUG myproject::debug
#define NAMESPACE_DEBUG_START namespace myproject { namespace debug {
#define NAMESPACE_DEBUG_END } }
#include "cwds/debug.h"
// Declare project-wide debug channels here, or in some other (more) appropriate header file.
NAMESPACE_DEBUG_CHANNELS_START
extern channel_ct custom;
NAMESPACE_DEBUG_CHANNELS_END
// And define them in a .cpp file like:
//NAMESPACE_DEBUG_CHANNELS_START
//channel_ct custom("CUSTOM");
//NAMESPACE_DEBUG_CHANNELS_END
«download»

This debug.h file is for applications; for more detailed information and for information for library developers who want to use libcwd, please also read The Custom debug.h File.

Step 3: Creating the custom source file

If you added one or more custom debug channels to your namespace DEBUGCHANNELS in your custom "debug.h", then of course you also need to add the definition somewhere.  You can do that anywhere, or you could add a special source file for it. 

debug.cc example template
// $Header$
//
// Copyright (C) 2000 - 2004, by
//
// Carlo Wood, Run on IRC <carlo@alinoe.com>
// RSA-1024 0x624ACAD5 1997-01-26 Sign & Encrypt
// Fingerprint16 = 32 EC A7 B6 AC DB 65 A6 F6 F6 55 DD 1C DC FF 61
//
// This file may be distributed under the terms of the Q Public License
// version 1.0 as appearing in the file LICENSE.QPL included in the
// packaging of this file.
//
#include "sys.h"
#include <libcwd/config.h>
#if LIBCWD_THREAD_SAFE && CWDEBUG_ALLOC && __GNUC__ == 3
// This has to be very early (must not have been included elsewhere already).
#define private public // Ugly, I know.
#if (__GNUC__ == 3 && __GNUC_MINOR__ < 4)
#include <bits/stl_alloc.h>
#else
#include <cstdlib> // Needed for getenv, needed by ext/pool_allocator.h.
#include <ext/pool_allocator.h>
#endif
#undef private
#endif // LIBCWD_THREAD_SAFE && CWDEBUG_ALLOC
#include <cerrno>
#include <iostream>
#include <algorithm>
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/time.h> // Needed for setrlimit()
#include <sys/resource.h> // Needed for setrlimit()
#endif
#include <cstdlib> // Needed for _Exit() (C99)
#include <new>
#include "cwd_debug.h"
#include "macros.h"
#include "libcwd/debug.h"
#include "private_debug_stack.inl"
#include <libcwd/class_location.inl>
extern "C" int raise(int);
#if LIBCWD_THREAD_SAFE
using libcwd::_private_::rwlock_tct;
using libcwd::_private_::debug_objects_instance;
using libcwd::_private_::debug_channels_instance;
#define DEBUG_OBJECTS_ACQUIRE_WRITE_LOCK rwlock_tct<libcwd::_private_::debug_objects_instance>::wrlock()
#define DEBUG_OBJECTS_RELEASE_WRITE_LOCK rwlock_tct<libcwd::_private_::debug_objects_instance>::wrunlock()
#define DEBUG_OBJECTS_ACQUIRE_READ_LOCK rwlock_tct<libcwd::_private_::debug_objects_instance>::rdlock()
#define DEBUG_OBJECTS_RELEASE_READ_LOCK rwlock_tct<libcwd::_private_::debug_objects_instance>::rdunlock()
#define DEBUG_OBJECTS_ACQUIRE_READ2WRITE_LOCK rwlock_tct<libcwd::_private_::debug_objects_instance>::rd2wrlock()
#define DEBUG_OBJECTS_ACQUIRE_WRITE2READ_LOCK rwlock_tct<libcwd::_private_::debug_objects_instance>::wr2rdlock()
#define DEBUG_CHANNELS_ACQUIRE_WRITE_LOCK rwlock_tct<libcwd::_private_::debug_channels_instance>::wrlock()
#define DEBUG_CHANNELS_RELEASE_WRITE_LOCK rwlock_tct<libcwd::_private_::debug_channels_instance>::wrunlock()
#define DEBUG_CHANNELS_ACQUIRE_READ_LOCK rwlock_tct<libcwd::_private_::debug_channels_instance>::rdlock()
#define DEBUG_CHANNELS_RELEASE_READ_LOCK rwlock_tct<libcwd::_private_::debug_channels_instance>::rdunlock()
#define DEBUG_CHANNELS_ACQUIRE_READ2WRITE_LOCK rwlock_tct<libcwd::_private_::debug_channels_instance>::rd2wrlock()
#define DEBUG_CHANNELS_ACQUIRE_WRITE2READ_LOCK rwlock_tct<libcwd::_private_::debug_channels_instance>::wr2rdlock()
#define COMMA_IFTHREADS(x) ,x
#else // !LIBCWD_THREAD_SAFE
#define DEBUG_OBJECTS_ACQUIRE_WRITE_LOCK
#define DEBUG_OBJECTS_RELEASE_WRITE_LOCK
#define DEBUG_OBJECTS_ACQUIRE_READ_LOCK
#define DEBUG_OBJECTS_RELEASE_READ_LOCK
#define DEBUG_OBJECTS_ACQUIRE_READ2WRITE_LOCK
#define DEBUG_OBJECTS_ACQUIRE_WRITE2READ_LOCK
#define DEBUG_CHANNELS_ACQUIRE_WRITE_LOCK
#define DEBUG_CHANNELS_RELEASE_WRITE_LOCK
#define DEBUG_CHANNELS_ACQUIRE_READ_LOCK
#define DEBUG_CHANNELS_RELEASE_READ_LOCK
#define DEBUG_CHANNELS_ACQUIRE_READ2WRITE_LOCK
#define DEBUG_CHANNELS_ACQUIRE_WRITE2READ_LOCK
#define COMMA_IFTHREADS(x)
#endif // !LIBCWD_THREAD_SAFE
#define NEED_SUPRESSION_OF_MALLOC_AND_BFD (__GNUC__ == 3 && \
((__GNUC_MINOR__ == 2 && __GNUC_PATCHLEVEL__ == 0) || \
(__GNUC_MINOR__ == 1 && __GNUC_PATCHLEVEL__ == 1) || \
(__GNUC_MINOR__ == 0)))
namespace libcwd {
namespace _private_ {
// gcc 4.0 use mt_allocator.h as default, which doesn't use locks at all.
#if LIBCWD_THREAD_SAFE && CWDEBUG_ALLOC && __GNUC__ == 3
#if (__GNUC_MINOR__ == 4)
#if (__GNUC_PATCHLEVEL__ <= 1)
__gnu_cxx::_STL_mutex_lock* pool_allocator_lock_symbol_ptr;
#else
__gthread_mutex_t* pool_allocator_lock_symbol_ptr;
#endif
#endif
// The following tries to take the "node allocator" lock -- the lock of the
// default allocator for threaded applications.
inline
bool allocator_trylock()
{
#if (__GNUC_MINOR__ < 4)
#if !defined(__GTHREAD_MUTEX_INIT) && defined(__GTHREAD_MUTEX_INIT_FUNCTION)
if (!std::__default_alloc_template<true, 0>::_S_node_allocator_lock._M_init_flag)
std::__default_alloc_template<true, 0>::_S_node_allocator_lock._M_initialize();
#endif
return (__gthread_mutex_trylock(&std::__default_alloc_template<true, 0>::_S_node_allocator_lock._M_lock) == 0);
#else // __GNUC_MINOR__ == 4
if (!pool_allocator_lock_symbol_ptr)
return false;
#if !defined(__GTHREAD_MUTEX_INIT) && defined(__GTHREAD_MUTEX_INIT_FUNCTION)
#if (__GNUC_PATCHLEVEL__ <= 1)
if (!pool_allocator_lock_symbol_ptr->_M_init_flag)
pool_allocator_lock_symbol_ptr->_M_initialize();
#else
// If the lock is not initialized, it should be initialized here.
#error "Sorry, not implemented yet."
#endif
#endif
#if (__GNUC_PATCHLEVEL__ <= 1)
return (__gthread_mutex_trylock(&pool_allocator_lock_symbol_ptr->_M_lock) == 0);
#else
return (__gthread_mutex_trylock(pool_allocator_lock_symbol_ptr) == 0);
#endif
#endif // __GNUC_MINOR__ == 4
}
// The following unlocks the node allocator.
inline
void allocator_unlock()
{
#if (__GNUC_MINOR__ < 4)
#if !defined(__GTHREAD_MUTEX_INIT) && defined(__GTHREAD_MUTEX_INIT_FUNCTION)
if (!std::__default_alloc_template<true, 0>::_S_node_allocator_lock._M_init_flag)
std::__default_alloc_template<true, 0>::_S_node_allocator_lock._M_initialize();
#endif
__gthread_mutex_unlock(&std::__default_alloc_template<true, 0>::_S_node_allocator_lock._M_lock);
#else // __GNUC_MINOR__ == 4
#if (__GNUC_PATCHLEVEL__ <= 1)
__gthread_mutex_unlock(&pool_allocator_lock_symbol_ptr->_M_lock);
#else
__gthread_mutex_unlock(pool_allocator_lock_symbol_ptr);
#endif
#endif // __GNUC_MINOR__ == 4
}
#endif // LIBCWD_THREAD_SAFE && CWDEBUG_ALLOC && __GNUC__ == 3
} // namespace _private_
using _private_::set_alloc_checking_on;
using _private_::set_alloc_checking_off;
#if CWDEBUG_ALLOC
using _private_::debug_message_st;
#endif
class buffer_ct : public _private_::auto_internal_stringbuf {
private:
typedef pos_type streampos_t;
streampos_t position;
#if LIBCWD_THREAD_SAFE
// These two are protected by the ostream lock of a debug object.
bool unfinished_already_printed;
bool continued_needed;
#endif
public:
#if LIBCWD_THREAD_SAFE
buffer_ct() : unfinished_already_printed(false), continued_needed(false) { }
#endif
void writeto(std::ostream* os LIBCWD_COMMA_TSD_PARAM, debug_ct& debug_object,
bool request_unfinished, bool do_flush COMMA_IFTHREADS(bool ends_on_newline)
COMMA_IFTHREADS(bool possible_nonewline_cf));
void store_position() {
position = this->pubseekoff(0, std::ios_base::cur, std::ios_base::out);
}
void restore_position() {
this->pubseekpos(position, std::ios_base::out);
this->pubseekpos(0, std::ios_base::in);
#if LIBCWD_THREAD_SAFE
continued_needed = false;
#endif
}
void write_prefix_to(std::ostream* os)
{
streampos_t old_in_pos = this->pubseekoff(0, std::ios_base::cur, std::ios_base::in);
this->pubseekpos(0, std::ios_base::in);
os->put(this->sgetc());
int size = position - std::streampos(0);
for (int c = 1; c < size; ++c)
os->put(this->snextc());
this->pubseekpos(old_in_pos, std::ios_base::in);
}
};
void buffer_ct::writeto(std::ostream* os LIBCWD_COMMA_TSD_PARAM,
#if LIBCWD_THREAD_SAFE
debug_ct& debug_object,
#else
debug_ct&,
#endif
bool request_unfinished, bool do_flush COMMA_IFTHREADS(bool ends_on_newline)
COMMA_IFTHREADS(bool possible_nonewline_cf))
{
// os : The ostream that we need to write to.
// __libcwd_tsd : The Thread Specific context.
// debug_object : The debug object context.
// request_unfinished : When set, then this thread is writing output that is interrupting
// unfinished debug output of its own.
// do_flush : Flush the ostream after writing to it.
// ends_on_newline : This output ends on a newline.
// possible_nonewline_cf : When `ends_on_newline' is false, then that was caused by the use of nonewline_cf.
#if LIBCWD_THREAD_SAFE && CWDEBUG_ALLOC && __GNUC__ == 3
typedef debug_message_st* msgbuf_t;
msgbuf_t msgbuf;
// Queue the message when the default (STL) allocator is locked and it could be that we
// did that (because we got here via malloc or free). At least as important: if
// this is the last thread, just prior to exiting the application, and we CAN
// get the lock - then DON'T queue the message (and thus flush all possibly queued
// messages); this garantees that there will never messages be left in the queue
// when the application exits.
bool const queue_msg = __libcwd_tsd.inside_malloc_or_free && !_private_::allocator_trylock();
if (__libcwd_tsd.inside_malloc_or_free && !queue_msg)
_private_::allocator_unlock(); // Always immedeately release the lock again.
int const extra_size = sizeof(debug_message_st) - sizeof(msgbuf->buf);
#else
typedef char* msgbuf_t;
msgbuf_t msgbuf;
bool const queue_msg = false;
int const extra_size = 0;
#endif
int curlen;
curlen = this->pubseekoff(0, std::ios_base::cur, std::ios_base::out) - this->pubseekoff(0, std::ios_base::cur, std::ios_base::in);
bool free_msgbuf = false;
if (queue_msg)
msgbuf = (msgbuf_t)malloc(curlen + extra_size);
else if (curlen > 512 || !(msgbuf = (msgbuf_t)__builtin_alloca(curlen + extra_size)))
{
msgbuf = (msgbuf_t)malloc(curlen + extra_size);
free_msgbuf = true;
}
#if LIBCWD_THREAD_SAFE && CWDEBUG_ALLOC && __GNUC__ == 3
this->sgetn(msgbuf->buf, curlen);
#else
this->sgetn(msgbuf, curlen);
#endif
#if LIBCWD_THREAD_SAFE && CWDEBUG_ALLOC && __GNUC__ == 3
if (queue_msg) // Inside a call to malloc and possibly owning lock of std::LIBCWD_POOL_ALLOC<true, 0>?
{
// We don't write debug output to the final ostream when inside malloc and std::LIBCWD_POOL_ALLOC<true, 0> is locked.
// It is namely possible that this will again try to acquire the lock in std::LIBCWD_POOL_ALLOC<true, 0>, resulting
// in a deadlock. Append it to the queue instead.
msgbuf->curlen = curlen;
msgbuf->prev = NULL;
msgbuf->next = debug_object.queue;
if (debug_object.queue)
debug_object.queue->prev = msgbuf;
else
debug_object.queue_top = msgbuf;
debug_object.queue = msgbuf;
}
else
{
#endif
#if CWDEBUG_ALLOC
// Writing to the final std::ostream (ie std::cerr) must be non-internal!
// LIBCWD_DISABLE_CANCEL/LIBCWD_ENABLE_CANCEL must be done non-internal too.
int saved_internal = _private_::set_library_call_on(LIBCWD_TSD);
++LIBCWD_DO_TSD_MEMBER_OFF(libcw_do);
#endif
#if LIBCWD_THREAD_SAFE
LIBCWD_DISABLE_CANCEL; // We don't want Dout() to be a cancellation point.
_private_::mutex_tct<_private_::set_ostream_instance>::lock();
bool got_lock = debug_object.M_mutex;
if (got_lock)
{
debug_object.M_mutex->lock();
__libcwd_tsd.pthread_lock_interface_is_locked = true;
}
std::ostream* locked_os = os;
_private_::mutex_tct<_private_::set_ostream_instance>::unlock();
if (!got_lock && _private_::WST_multi_threaded)
{
static bool WST_second_time = false; // Break infinite loop.
if (!WST_second_time)
{
WST_second_time = true;
DoutFatal(dc::core, "When using multiple threads, you must provide a locking mechanism for the debug output stream. "
"You can pass a pointer to a mutex with `debug_ct::set_ostream' (see documentation/reference-manual/group__group__destination.html).");
}
}
#endif
#if LIBCWD_THREAD_SAFE
if (debug_object.newlineless_tsd && debug_object.newlineless_tsd != &__libcwd_tsd)
{
if (debug_object.unfinished_oss)
{
if (debug_object.unfinished_oss != this)
{
locked_os->write("<unfinished>\n", 13);
debug_object.unfinished_oss->unfinished_already_printed = true;
debug_object.unfinished_oss->continued_needed = true;
}
}
else
locked_os->write("<no newline>\n", 13);
}
if (continued_needed && curlen > 0)
{
continued_needed = false;
write_prefix_to(locked_os);
locked_os->write("<continued> ", 12);
}
#endif // LIBCWD_THREAD_SAFE
#if LIBCWD_THREAD_SAFE && CWDEBUG_ALLOC && __GNUC__ == 3
debug_message_st* message = debug_object.queue_top;
if (message)
{
// First empty the whole queue.
debug_message_st* next_message;
do
{
next_message = message->prev;
locked_os->write(message->buf, message->curlen);
__libcwd_tsd.internal = 1;
free(message);
__libcwd_tsd.internal = 0;
}
while ((message = next_message));
debug_object.queue_top = debug_object.queue = NULL;
}
// Then write the new message.
locked_os->write(msgbuf->buf, curlen);
#else // !(CWDEBUG_ALLOC && LIBCWD_THREAD_SAFE)
#if LIBCWD_THREAD_SAFE
locked_os->write(msgbuf, curlen);
#else // !LIBCWD_THREAD_SAFE
os->write(msgbuf, curlen);
#endif // !LIBCWD_THREAD_SAFE
#endif // !(CWDEBUG_ALLOC && LIBCWD_THREAD_SAFE)
#if LIBCWD_THREAD_SAFE
if (request_unfinished && !unfinished_already_printed)
locked_os->write("<unfinished>\n", 13);
#else
if (request_unfinished)
os->write("<unfinished>\n", 13);
#endif
if (do_flush)
#if LIBCWD_THREAD_SAFE
locked_os->flush();
#else
os->flush();
#endif
#if LIBCWD_THREAD_SAFE
unfinished_already_printed = ends_on_newline;
if (ends_on_newline)
{
debug_object.unfinished_oss = NULL;
debug_object.newlineless_tsd = NULL;
}
else if (curlen > 0)
{
debug_object.newlineless_tsd = &__libcwd_tsd;
if (possible_nonewline_cf)
debug_object.unfinished_oss = NULL;
else
debug_object.unfinished_oss = this;
}
if (got_lock)
{
__libcwd_tsd.pthread_lock_interface_is_locked = false;
debug_object.M_mutex->unlock();
}
LIBCWD_ENABLE_CANCEL;
#endif // !LIBCWD_THREAD_SAFE
#if CWDEBUG_ALLOC
--LIBCWD_DO_TSD_MEMBER_OFF(libcw_do);
_private_::set_library_call_off(saved_internal LIBCWD_COMMA_TSD);
#endif
#if LIBCWD_THREAD_SAFE && CWDEBUG_ALLOC && __GNUC__ == 3
}
#endif
if (free_msgbuf)
free(msgbuf);
}
// Configuration signature
unsigned long const config_signature_lib_c = config_signature_header_c;
// Return the configuration signature of the .so file.
unsigned long get_config_signature_lib_c()
{
return config_signature_lib_c;
}
// Put this here to decrease the code size of `check_configuration'
void conf_check_failed()
{
DoutFatal(dc::fatal, "check_configuration: This version of libcwd was compiled with a different configuration than is currently used in libcwd/config.h!");
}
void version_check_failed()
{
DoutFatal(dc::fatal, "check_configuration: This version of libcwd does not match the version of libcwd/config.h! Are your paths correct? Did you recently upgrade libcwd and forgot to recompile this application?");
}
debug_ct libcw_do;
namespace {
unsigned short int WST_max_len = 8; // The length of the longest label. Is adjusted automatically
// if a custom channel has a longer label.
} // namespace
namespace channels {
namespace dc {
channel_ct debug
#ifndef HIDE_FROM_DOXYGEN
("DEBUG")
#endif
;
channel_ct notice
#ifndef HIDE_FROM_DOXYGEN
("NOTICE")
#endif
;
channel_ct system
#ifndef HIDE_FROM_DOXYGEN
("SYSTEM")
#endif
;
channel_ct malloc
#ifndef HIDE_FROM_DOXYGEN
("MALLOC")
#endif
;
channel_ct warning
#ifndef HIDE_FROM_DOXYGEN
("WARNING")
#endif
;
always_channel_ct always;
continued_channel_ct continued
#ifndef HIDE_FROM_DOXYGEN
(continued_maskbit)
#endif
;
continued_channel_ct finish
#ifndef HIDE_FROM_DOXYGEN
(finish_maskbit)
#endif
;
fatal_channel_ct fatal
#ifndef HIDE_FROM_DOXYGEN
("FATAL", fatal_maskbit)
#endif
;
fatal_channel_ct core
#ifndef HIDE_FROM_DOXYGEN
("COREDUMP", coredump_maskbit)
#endif
;
} // namespace dc
} // namespace channels
channel_ct const channel_ct::off_channel
#ifndef HIDE_FROM_DOXYGEN
("!NEVER!", false)
#endif
;
#if CWDEBUG_LOCATION
namespace cwbfd {
extern bool ST_init(LIBCWD_TSD_PARAM);
} // namespace cwbfd
#endif
void ST_initialize_globals(LIBCWD_TSD_PARAM)
{
static bool ST_already_called;
if (ST_already_called)
return;
ST_already_called = true;
#if CWDEBUG_ALLOC
init_debugmalloc();
#endif
#if LIBCWD_THREAD_SAFE
_private_::initialize_global_mutexes();
#endif
_private_::process_environment_variables();
// Fatal channels need to be marked fatal, otherwise we get into an endless loop
// when they are used before they are created.
channels::dc::core.NS_initialize("COREDUMP", coredump_maskbit LIBCWD_COMMA_TSD);
channels::dc::fatal.NS_initialize("FATAL", fatal_maskbit LIBCWD_COMMA_TSD);
// Initialize other debug channels that might be used before we reach main().
channels::dc::debug.NS_initialize("DEBUG" LIBCWD_COMMA_TSD, true);
channels::dc::malloc.NS_initialize("MALLOC" LIBCWD_COMMA_TSD, true);
channels::dc::continued.NS_initialize(continued_maskbit);
channels::dc::finish.NS_initialize(finish_maskbit);
#if CWDEBUG_LOCATION
channels::dc::bfd.NS_initialize("BFD" LIBCWD_COMMA_TSD, true);
#endif
// What the heck, initialize all other debug channels too
channels::dc::warning.NS_initialize("WARNING" LIBCWD_COMMA_TSD, true);
channels::dc::notice.NS_initialize("NOTICE" LIBCWD_COMMA_TSD, true);
channels::dc::system.NS_initialize("SYSTEM" LIBCWD_COMMA_TSD, true);
if (!libcw_do.NS_init(LIBCWD_TSD)) // Initialize debug code.
DoutFatal(dc::core, "Calling debug_ct::NS_init recursively from ST_initialize_globals");
// Unlimit core size.
#ifdef RLIMIT_CORE
struct rlimit corelim;
if (getrlimit(RLIMIT_CORE, &corelim))
DoutFatal(dc::fatal|error_cf, "getrlimit(RLIMIT_CORE, &corelim)");
corelim.rlim_cur = corelim.rlim_max;
if (corelim.rlim_max != RLIM_INFINITY && !_private_::suppress_startup_msgs)
{
debug_ct::OnOffState state;
libcw_do.force_on(state);
// The cast is necessary on platforms where corelim.rlim_max is long long
// and libstdc++ was not compiled with support for long long.
Dout(dc::warning, "core size is limited (hard limit: " << (unsigned long)(corelim.rlim_max / 1024) << " kb). Core dumps might be truncated!");
libcw_do.restore(state);
}
if (setrlimit(RLIMIT_CORE, &corelim))
DoutFatal(dc::fatal|error_cf, "unlimit core size failed");
#else
if (!_private_::suppress_startup_msgs)
{
debug_ct::OnOffState state;
libcw_do.force_on(state);
Dout(dc::warning, "Please unlimit core size manually");
libcw_do.restore(state);
}
#endif
#if CWDEBUG_LOCATION
cwbfd::ST_init(LIBCWD_TSD); // Initialize BFD code.
#endif
#if CWDEBUG_DEBUG && !CWDEBUG_DEBUGOUTPUT
// Force allocation of a __cxa_eh_globals struct in libsupc++.
(void)std::uncaught_exceptions(); // Leaks memory.
#endif
}
namespace _private_ {
#if !LIBCWD_THREAD_SAFE
TSD_st __libcwd_tsd;
#endif
#if LIBCWD_THREAD_SAFE
extern bool WST_is_NPTL;
#endif
debug_channels_ct debug_channels; // List with all channel_ct objects.
debug_objects_ct debug_objects; // List with all debug devices.
// _private_::
void debug_channels_ct::init(LIBCWD_TSD_PARAM)
{
#if LIBCWD_THREAD_SAFE
_private_::rwlock_tct<libcwd::_private_::debug_channels_instance>::initialize();
#endif
DEBUG_CHANNELS_ACQUIRE_READ_LOCK;
if (!WNS_debug_channels) // MT: `WNS_debug_channels' is only false when this object is still Non_Shared.
{
DEBUG_CHANNELS_ACQUIRE_READ2WRITE_LOCK;
set_alloc_checking_off(LIBCWD_TSD);
WNS_debug_channels = new debug_channels_ct::container_type; // LEAK2
set_alloc_checking_on(LIBCWD_TSD);
DEBUG_CHANNELS_RELEASE_WRITE_LOCK;
}
#if LIBCWD_THREAD_SAFE
else
DEBUG_CHANNELS_RELEASE_READ_LOCK;
#endif
}
#if LIBCWD_THREAD_SAFE
// _private_::
void debug_channels_ct::init_and_rdlock()
{
_private_::rwlock_tct<libcwd::_private_::debug_channels_instance>::initialize();
DEBUG_CHANNELS_ACQUIRE_READ_LOCK;
if (!WNS_debug_channels) // MT: `WNS_debug_channels' is only false when this object is still Non_Shared.
{
LIBCWD_TSD_DECLARATION;
set_alloc_checking_off(LIBCWD_TSD);
DEBUG_CHANNELS_ACQUIRE_READ2WRITE_LOCK;
WNS_debug_channels = new debug_channels_ct::container_type; // LEAK3
DEBUG_CHANNELS_ACQUIRE_WRITE2READ_LOCK;
set_alloc_checking_on(LIBCWD_TSD);
}
}
#endif
// _private_::
void debug_objects_ct::init(LIBCWD_TSD_PARAM)
{
#if LIBCWD_THREAD_SAFE
_private_::rwlock_tct<libcwd::_private_::debug_objects_instance>::initialize();
#endif
DEBUG_OBJECTS_ACQUIRE_READ_LOCK;
if (!WNS_debug_objects) // MT: `WNS_debug_objects' is only false when this object is still Non_Shared.
{
DEBUGDEBUG_CERR( (char const*)"_debug_objects == NULL; initializing it" );
#if CWDEBUG_ALLOC
// It is possible that malloc is not initialized yet.
init_debugmalloc();
#endif
DEBUG_OBJECTS_ACQUIRE_READ2WRITE_LOCK;
set_alloc_checking_off(LIBCWD_TSD);
WNS_debug_objects = new debug_objects_ct::container_type;
set_alloc_checking_on(LIBCWD_TSD);
DEBUG_OBJECTS_RELEASE_WRITE_LOCK;
}
#if LIBCWD_THREAD_SAFE
else
DEBUG_OBJECTS_RELEASE_READ_LOCK;
#endif
}
#if LIBCWD_THREAD_SAFE
// _private_::
void debug_objects_ct::init_and_rdlock()
{
_private_::rwlock_tct<libcwd::_private_::debug_objects_instance>::initialize();
DEBUG_OBJECTS_ACQUIRE_READ_LOCK;
if (!WNS_debug_objects) // MT: `WNS_debug_objects' is only false when this object is still Non_Shared.
{
DEBUGDEBUG_CERR( "_debug_objects == NULL; initializing it" );
#if CWDEBUG_ALLOC
// It is possible that malloc is not initialized yet.
init_debugmalloc();
#endif
LIBCWD_TSD_DECLARATION;
set_alloc_checking_off(LIBCWD_TSD);
DEBUG_OBJECTS_ACQUIRE_READ2WRITE_LOCK;
WNS_debug_objects = new debug_objects_ct::container_type;
DEBUG_OBJECTS_ACQUIRE_WRITE2READ_LOCK;
set_alloc_checking_on(LIBCWD_TSD);
}
}
#endif
// _private_::
void debug_objects_ct::ST_uninit()
{
if (WNS_debug_objects)
{
LIBCWD_TSD_DECLARATION;
set_alloc_checking_off(LIBCWD_TSD);
delete WNS_debug_objects;
set_alloc_checking_on(LIBCWD_TSD);
WNS_debug_objects = NULL;
}
}
} // namespace _private_
#if CWDEBUG_DEBUG
static long WST_debug_object_init_magic = 0;
static void init_debug_object_init_magic()
{
struct timeval rn;
gettimeofday(&rn, NULL);
WST_debug_object_init_magic = rn.tv_usec;
if (!WST_debug_object_init_magic)
WST_debug_object_init_magic = 1;
DEBUGDEBUG_CERR( "Set WST_debug_object_init_magic to " << WST_debug_object_init_magic );
}
#endif
class laf_ct {
public:
buffer_ct buffer;
// The temporary output buffer.
_private_::bufferstream_ct bufferstream;
// Our 'stringstream'.
// The previous control bits.
char const* label;
// The previous label.
int err;
// The current errno.
public:
laf_ct(control_flag_t m, char const* l, int e) : bufferstream(&buffer), mask(m), label(l), err(e) { }
};
static inline void write_whitespace_to(std::ostream& os, unsigned int size)
{
for (unsigned int i = size; i > 0; --i)
os.put(' ');
}
namespace _private_ {
static char WST_dummy_laf[sizeof(laf_ct)] __attribute__((__aligned__));
} // namespace _private_
void core_dump()
{
#if LIBCWD_THREAD_SAFE
// Are we the first thread that tries to generate a core?
#if CWDEBUG_DEBUGT || CWDEBUG_ALLOC
LIBCWD_TSD_DECLARATION;
#endif
LIBCWD_DISABLE_CANCEL;
if (!_private_::mutex_tct<_private_::kill_threads_instance>::try_lock())
{
#if CWDEBUG_ALLOC
__libcwd_tsd.internal = 0; // Dunno if this is needed, but it looks consistant.
++__libcwd_tsd.library_call;; // So our sanity checks allow us to call free() again in
// pthread_exit when we get here from malloc et al.
#endif
// Another thread is already trying to generate a core dump.
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
pthread_exit(PTHREAD_CANCELED);
}
// Leave cancelation disabled because otherwise it might be that another thread is generating the core.
#endif
#if LIBCWD_THREAD_SAFE && CWDEBUG_DEBUG && defined(__linux)
if (!_private_::WST_is_NPTL && pthread_self() == (pthread_t)2049)
{
::write(1, "WARNING: Thread manager core dumped. Going into infinite loop. Please detach process with gdb.\n", 97);
while(1) ;
}
#endif
raise(6);
#if LIBCWD_THREAD_SAFE
LIBCWD_ENABLE_CANCEL;
#endif
_Exit(6); // Never reached.
}
size_t debug_string_ct::calculate_capacity(size_t size)
{
size_t capacity_plus_one = M_default_capacity + 1; // For the terminating zero.
while(size >= capacity_plus_one)
capacity_plus_one *= 2;
return capacity_plus_one - 1;
}
void debug_string_ct::NS_internal_init(char const* str, size_t len)
{
M_default_capacity = min_capacity_c;
M_str = (char*)malloc((M_default_capacity = M_capacity = calculate_capacity(len)) + 1); // Add one for the terminating zero. LEAK46
strncpy(M_str, str, len);
M_size = len;
M_str[M_size] = 0;
}
// This is called with alloc checking off.
void debug_string_ct::deinitialize()
{
free(M_str);
M_str = NULL;
}
// This is called with alloc checking on (or off).
debug_string_ct::~debug_string_ct()
{
#if CWDEBUG_DEBUG && LIBCWD_THREAD_SAFE
LIBCWD_ASSERT(M_str == NULL); // Need to call debug_string_ct::deinitialize() before destructor.
// But not in the non-threaded case, see debug_tsd_st::~debug_tsd_st.
#endif
}
void debug_string_ct::internal_assign(char const* str, size_t len)
{
if (len > M_capacity || (M_capacity > M_default_capacity && len < M_default_capacity))
M_str = (char*)realloc(M_str, (M_capacity = calculate_capacity(len)) + 1);
strncpy(M_str, str, len);
M_size = len;
M_str[M_size] = 0;
}
void debug_string_ct::internal_append(char const* str, size_t len)
{
if (M_size + len > M_capacity || (M_capacity > M_default_capacity && M_size + len < M_default_capacity))
M_str = (char*)realloc(M_str, (M_capacity = calculate_capacity(M_size + len)) + 1);
strncpy(M_str + M_size, str, len);
M_size += len;
M_str[M_size] = 0;
}
void debug_string_ct::internal_prepend(char const* str, size_t len)
{
if (M_size + len > M_capacity || (M_capacity > M_default_capacity && M_size + len < M_default_capacity))
M_str = (char*)realloc(M_str, (M_capacity = calculate_capacity(M_size + len)) + 1);
memmove(M_str + len, M_str, M_size + 1);
strncpy(M_str, str, len);
M_size += len;
}
void debug_string_ct::reserve(size_t size)
{
if (size < M_size)
return;
LIBCWD_TSD_DECLARATION;
set_alloc_checking_off(LIBCWD_TSD);
M_default_capacity = min_capacity_c;
M_str = (char*)realloc(M_str, (M_default_capacity = M_capacity = calculate_capacity(size)) + 1);
set_alloc_checking_on(LIBCWD_TSD);
}
void debug_string_ct::internal_swallow(debug_string_ct const& ds)
{
// `this' and `ds' are both initialized. `internal' is set.
free(M_str);
M_str = ds.M_str;
M_size = ds.M_size;
M_capacity = ds.M_capacity;
M_default_capacity = ds.M_default_capacity;
}
{
LIBCWD_TSD_DECLARATION;
debug_string_stack_element_ct* current_margin_stack = LIBCWD_TSD_MEMBER(M_margin_stack);
set_alloc_checking_off(LIBCWD_TSD);
void* new_debug_string = malloc(sizeof(debug_string_stack_element_ct));
LIBCWD_TSD_MEMBER(M_margin_stack) = new (new_debug_string) debug_string_stack_element_ct(LIBCWD_TSD_MEMBER(margin));
set_alloc_checking_on(LIBCWD_TSD);
LIBCWD_TSD_MEMBER(M_margin_stack)->next = current_margin_stack;
}
{
LIBCWD_TSD_DECLARATION;
if (!LIBCWD_TSD_MEMBER(M_margin_stack))
DoutFatal(dc::core, "Calling `debug_ct::pop_margin' more often than `debug_ct::push_margin'.");
debug_string_stack_element_ct* next = LIBCWD_TSD_MEMBER(M_margin_stack)->next;
set_alloc_checking_off(LIBCWD_TSD);
LIBCWD_TSD_MEMBER(margin).internal_swallow(LIBCWD_TSD_MEMBER(M_margin_stack)->debug_string);
free(LIBCWD_TSD_MEMBER(M_margin_stack));
set_alloc_checking_on(LIBCWD_TSD);
LIBCWD_TSD_MEMBER(M_margin_stack) = next;
}
{
LIBCWD_TSD_DECLARATION;
debug_string_stack_element_ct* current_marker_stack = LIBCWD_TSD_MEMBER(M_marker_stack);
set_alloc_checking_off(LIBCWD_TSD);
void* new_debug_string = malloc(sizeof(debug_string_stack_element_ct));
LIBCWD_TSD_MEMBER(M_marker_stack) = new (new_debug_string) debug_string_stack_element_ct(LIBCWD_TSD_MEMBER(marker));
set_alloc_checking_on(LIBCWD_TSD);
LIBCWD_TSD_MEMBER(M_marker_stack)->next = current_marker_stack;
}
{
LIBCWD_TSD_DECLARATION;
if (!LIBCWD_TSD_MEMBER(M_marker_stack))
DoutFatal(dc::core, "Calling `debug_ct::pop_marker' more often than `debug_ct::push_marker'.");
debug_string_stack_element_ct* next = LIBCWD_TSD_MEMBER(M_marker_stack)->next;
set_alloc_checking_off(LIBCWD_TSD);
LIBCWD_TSD_MEMBER(marker).internal_swallow(LIBCWD_TSD_MEMBER(M_marker_stack)->debug_string);
free(LIBCWD_TSD_MEMBER(M_marker_stack));
set_alloc_checking_on(LIBCWD_TSD);
LIBCWD_TSD_MEMBER(M_marker_stack) = next;
}
void debug_tsd_st::start(debug_ct& debug_object, channel_set_data_st& channel_set LIBCWD_COMMA_TSD_PARAM)
{
#if CWDEBUG_DEBUG || CWDEBUG_DEBUGT
#if LIBCWD_THREAD_SAFE
// Initialisation of the TSD part should be done from LIBCWD_TSD_DECLARATION inside Dout et al.
LIBCWD_ASSERT( tsd_initialized );
#else
if (!tsd_initialized)
init();
#endif
#endif
if (NEED_SUPRESSION_OF_MALLOC_AND_BFD)
{
#if CWDEBUG_LOCATION
#endif
}
// Skip `start()' for a `continued' debug output.
// Generating the "prefix: <continued>" is already taken care
// of while generating the "<unfinished>" (see next `if' block).
if ((channel_set.mask & (continued_maskbit|finish_maskbit)))
{
current->err = errno; // Always keep the last errno as set at the start of LibcwDout()
if (!(current->mask & continued_expected_maskbit))
{
std::ostream* target_os = (channel_set.mask & cerr_cf) ? &std::cerr : debug_object.real_os;
#if LIBCWD_THREAD_SAFE
// Try to get the lock, but don't try too long...
int res;
struct timespec const t = { 0, 5000000 };
int count = 0;
do
{
if (!(res = debug_object.M_mutex->try_lock()))
break;
nanosleep(&t, NULL);
}
while(++count < 40);
#endif
target_os->put('\n');
#if LIBCWD_THREAD_SAFE
if (res == 0)
debug_object.M_mutex->unlock();
#endif
char const* channame = (channel_set.mask & finish_maskbit) ? "finish" : "continued";
#if CWDEBUG_LOCATION
DoutFatal(dc::core, "Using `dc::" << channame << "' in " <<
location_ct((char*)__builtin_return_address(0) + builtin_return_address_offset) <<
" without (first using) a matching `continued_cf'.");
#else
DoutFatal(dc::core, "Using `dc::" << channame <<
"' without (first using) a matching `continued_cf'.");
#endif
}
#if CWDEBUG_DEBUG
// MT: current != _private_::WST_dummy_laf, otherwise we didn't pass the previous if.
LIBCWD_ASSERT( current != reinterpret_cast<laf_ct*>(_private_::WST_dummy_laf) );
#endif
current->mask = channel_set.mask; // New bits might have been added
if ((current->mask & finish_maskbit))
current->mask &= ~continued_expected_maskbit;
return;
}
set_alloc_checking_off(LIBCWD_TSD);
++LIBCWD_DO_TSD_MEMBER_OFF(debug_object);
DEBUGDEBUG_CERR( "Entering debug_ct::start(), _off became " << LIBCWD_DO_TSD_MEMBER_OFF(debug_object) );
// Is this an interrupting debug output (in the middle of a continued debug output)?
if ((current->mask & continued_cf_maskbit) && unfinished_expected)
{
#if CWDEBUG_DEBUG
// MT: if current == _private_::WST_dummy_laf then
// (current->mask & continued_cf_maskbit) is false and this if is skipped.
LIBCWD_ASSERT( current != reinterpret_cast<laf_ct*>(_private_::WST_dummy_laf) );
#endif
int saved_errno = errno; // The writeto below changes errno.
// And write out what is in the buffer till now.
std::ostream* target_os = (channel_set.mask & cerr_cf) ? &std::cerr : debug_object.real_os;
current->buffer.writeto(target_os LIBCWD_COMMA_TSD, debug_object,
true, // This thread requests an <unfinished> because of previous, unfinished 'continued' output.
false // Don't flush.
COMMA_IFTHREADS(true) // This output ends on a newline by itself.
COMMA_IFTHREADS(false)); // The newline is not missing as a result of nonewline_cf.
// Truncate the buffer to its prefix and append "<continued>" to it already.
current->buffer.restore_position();
current_bufferstream->write("<continued> ", 12); // therefore we repeat the space here.
errno = saved_errno;
}
// Is this a nested debug output (the first of a series in the middle of another debug output)?
// MT: if current == _private_::WST_dummy_laf then
// start_expected is true and this if is skipped.
if (!start_expected)
{
// Put current stringstream on the stack.
laf_stack.push(current);
// Indent nested debug output with 4 extra spaces.
indent += 4;
// If the previous target was written to cerr, then
// write this interrupting output to cerr too.
channel_set.mask |= (current->mask & cerr_cf);
}
// Create a new laf.
DEBUGDEBUG_CERR( "creating new laf_ct" );
int saved_internal = _private_::set_library_call_on(LIBCWD_TSD);
_private_::set_invisible_on(LIBCWD_TSD);
current = new laf_ct(channel_set.mask, channel_set.label, errno); // LEAK5: This allocation + location_ct.
_private_::set_invisible_off(LIBCWD_TSD);
_private_::set_library_call_off(saved_internal LIBCWD_COMMA_TSD);
DEBUGDEBUG_CERR( "current = " << (void*)current );
current_bufferstream = &current->bufferstream;
DEBUGDEBUG_CERR( "laf_ct created" );
// Without a new nested Dout() call, we expect to see a finish() call: The finish belonging to *this* Dout() call.
start_expected = false;
// If this is a `continued' debug output, then we want to print "<unfinished>" if next we see a start().
unfinished_expected = true;
// Print prefix if requested.
// Handle most common case first: no special flags set
{
current_bufferstream->write(margin.c_str(), margin.size());
current_bufferstream->write(channel_set.label, WST_max_len);
current_bufferstream->write(marker.c_str(), marker.size());
write_whitespace_to(*current_bufferstream, indent);
}
else if (!(channel_set.mask & noprefix_cf))
{
if ((channel_set.mask & blank_margin_cf))
write_whitespace_to(*current_bufferstream, margin.size());
else
current_bufferstream->write(margin.c_str(), margin.size());
#if !CWDEBUG_DEBUGOUTPUT
if (!(channel_set.mask & nolabel_cf))
#endif
{
if ((channel_set.mask & blank_label_cf))
write_whitespace_to(*current_bufferstream, WST_max_len);
else
current_bufferstream->write(channel_set.label, WST_max_len);
if ((channel_set.mask & blank_marker_cf))
write_whitespace_to(*current_bufferstream, marker.size());
else
current_bufferstream->write(marker.c_str(), marker.size());
write_whitespace_to(*current_bufferstream, indent);
}
}
if ((channel_set.mask & continued_cf_maskbit))
{
// If this is continued debug output, then it makes sense to remember the prefix length,
// just in case we need indeed to output <continued> data.
current->buffer.store_position();
}
--LIBCWD_DO_TSD_MEMBER_OFF(debug_object);
DEBUGDEBUG_CERR( "Leaving debug_ct::start(), _off became " << LIBCWD_DO_TSD_MEMBER_OFF(debug_object) );
set_alloc_checking_on(LIBCWD_TSD);
}
void debug_tsd_st::finish(debug_ct& debug_object, channel_set_data_st& /*UNUSED, */ LIBCWD_COMMA_TSD_PARAM)
{
#if CWDEBUG_DEBUG
LIBCWD_ASSERT( current != reinterpret_cast<laf_ct*>(_private_::WST_dummy_laf) );
#endif
std::ostream* target_os = (current->mask & cerr_cf) ? &std::cerr : debug_object.real_os;
set_alloc_checking_off(LIBCWD_TSD);
if (NEED_SUPRESSION_OF_MALLOC_AND_BFD)
{
#if CWDEBUG_LOCATION
#endif
}
// Skip `finish()' for a `continued' debug output.
if ((current->mask & continued_cf_maskbit) && !(current->mask & finish_maskbit))
{
// Allow a subsequent Dout(dc::continued (or dc::finish), ...).
current->mask |= continued_expected_maskbit;
if ((current->mask & continued_maskbit))
unfinished_expected = true;
// If the `flush_cf' control flag is set, flush the ostream at every `finish()' though.
if ((current->mask & flush_cf))
{
// Write buffer to ostream.
// Flush ostream. Note that in the case of nested debug output this `os' can be an stringstream,
// in that case, no actual flushing is done until the debug output to the real ostream has
// finished.
current->buffer.writeto(target_os LIBCWD_COMMA_TSD, debug_object,
false, // This thread requests <unfinished> because of previous, unfinished 'continued' output.
true // Flush ostream after printing this.
COMMA_IFTHREADS(false) // This output does not end on a newline.
COMMA_IFTHREADS(false)); // The newline is not missing as a result of nonewline_cf.
}
set_alloc_checking_on(LIBCWD_TSD);
return;
}
++LIBCWD_DO_TSD_MEMBER_OFF(debug_object);
DEBUGDEBUG_CERR( "Entering debug_ct::finish(), _off became " << LIBCWD_DO_TSD_MEMBER_OFF(debug_object) );
// Handle control flags, if any:
if ((current->mask & error_cf))
{
// strerror[_r] can call malloc (in gettext()).
#if CWDEBUG_ALLOC
int saved_internal = _private_::set_library_call_on(LIBCWD_TSD);
#endif
#if !LIBCWD_THREAD_SAFE
char const* error_text = strerror(current->err);
#else // LIBCWD_THREAD_SAFE
char error_text_buf[512];
char const* error_text;
#ifdef _GNU_SOURCE
error_text = strerror_r(current->err, error_text_buf, sizeof(error_text_buf));
#else // POSIX
if (strerror_r(current->err, error_text_buf, sizeof(error_text_buf)) == -1)
{
if (errno == ERANGE)
error_text = "<libcwd: Oops, error text longer than 512 characters>";
else
error_text = "Unknown Error";
}
else
error_text = error_text_buf;
#endif
#endif
#if CWDEBUG_ALLOC
_private_::set_library_call_off(saved_internal LIBCWD_COMMA_TSD);
#endif
*current_bufferstream << ": " << strerrno(current->err) << " (" << error_text << ')';
}
if (!(current->mask & nonewline_cf))
current_bufferstream->put('\n');
// Handle control flags, if any:
if (current->mask != 0)
{
if ((current->mask & (coredump_maskbit|fatal_maskbit)))
{
current->buffer.writeto(target_os LIBCWD_COMMA_TSD, debug_object,
false, // This thread requests <unfinished> because of previous, unfinished 'continued' output.
!__libcwd_tsd.recursive_fatal // Flush ostream after printing this when there is no recursive loop yet.
COMMA_IFTHREADS(!(current->mask & nonewline_cf)) // Whether or not this output ends on a newline.
COMMA_IFTHREADS(true)); // If the newline is missing, then it is missing because of the use of nonewline_cf.
__libcwd_tsd.recursive_fatal = true;
if ((current->mask & coredump_maskbit))
DEBUGDEBUG_CERR( "Deleting `current' " << (void*)current );
int saved_internal = _private_::set_library_call_on(LIBCWD_TSD);
_private_::set_invisible_on(LIBCWD_TSD);
delete current;
_private_::set_invisible_off(LIBCWD_TSD);
_private_::set_library_call_off(saved_internal LIBCWD_COMMA_TSD);
DEBUGDEBUG_CERR( "Done deleting `current'" );
set_alloc_checking_on(LIBCWD_TSD);
#if CWDEBUG_ALLOC
if (__libcwd_tsd.internal) // Still internal? Are we ever calling DoutFatal while internal?
_private_::set_library_call_on(LIBCWD_TSD); // Indefinetely turn library call on before terminating threads.
#endif
#if LIBCWD_THREAD_SAFE
LIBCWD_DISABLE_CANCEL;
if (!_private_::mutex_tct<_private_::kill_threads_instance>::try_lock())
{
// Another thread is already trying to generate a core dump.
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
pthread_exit(PTHREAD_CANCELED);
}
_private_::rwlock_tct<_private_::threadlist_instance>::rdlock(true);
// Terminate all threads that I know of, so that no locks will remain.
for(_private_::threadlist_t::iterator thread_iter = _private_::threadlist->begin(); thread_iter != _private_::threadlist->end(); ++thread_iter)
if (!pthread_equal((*thread_iter).tid, pthread_self())
#ifdef __linux
&& (_private_::WST_is_NPTL || (*thread_iter).tid != (pthread_t)1024)
#endif
)
pthread_cancel((*thread_iter).tid);
_private_::rwlock_tct<_private_::threadlist_instance>::rdunlock();
LIBCWD_ENABLE_CANCEL;
#endif
_Exit(254); // Exit without calling global destructors.
}
if ((current->mask & wait_cf))
{
current->buffer.writeto(target_os LIBCWD_COMMA_TSD, debug_object,
false, debug_object.interactive COMMA_IFTHREADS(!(current->mask & nonewline_cf))
COMMA_IFTHREADS(true));
#if LIBCWD_THREAD_SAFE
debug_object.M_mutex->lock();
#endif
*target_os << "(type return)";
if (debug_object.interactive)
{
*target_os << std::flush;
while(std::cin.get() != '\n') ;
}
#if LIBCWD_THREAD_SAFE
debug_object.M_mutex->unlock();
#endif
}
else
current->buffer.writeto(target_os LIBCWD_COMMA_TSD, debug_object,
false, (current->mask & flush_cf) COMMA_IFTHREADS(!(current->mask & nonewline_cf))
COMMA_IFTHREADS(true));
}
else
current->buffer.writeto(target_os LIBCWD_COMMA_TSD, debug_object,
false, false COMMA_IFTHREADS(!(current->mask & nonewline_cf)) COMMA_IFTHREADS(true));
DEBUGDEBUG_CERR( "Deleting `current' " << (void*)current );
int saved_internal = _private_::set_library_call_on(LIBCWD_TSD);
_private_::set_invisible_on(LIBCWD_TSD);
control_flag_t mask = current->mask; // Keep this.
delete current;
_private_::set_invisible_off(LIBCWD_TSD);
_private_::set_library_call_off(saved_internal LIBCWD_COMMA_TSD);
DEBUGDEBUG_CERR( "Done deleting `current'" );
if (start_expected)
{
// Ok, we're done with the last buffer.
indent -= 4;
laf_stack.pop();
}
// Restore previous buffer as being the current one, if any.
if (laf_stack.size())
{
current = laf_stack.top();
DEBUGDEBUG_CERR( "current = " << (void*)current );
current_bufferstream = &current->bufferstream;
if ((mask & flush_cf))
current->mask |= flush_cf; // Propagate flush to real ostream.
}
else
{
current = reinterpret_cast<laf_ct*>(_private_::WST_dummy_laf); // Used (MT: read-only!) in next debug_ct::start().
DEBUGDEBUG_CERR( "current = " << (void*)current );
current_bufferstream = NULL;
}
start_expected = true;
unfinished_expected = false;
--LIBCWD_DO_TSD_MEMBER_OFF(debug_object);
DEBUGDEBUG_CERR( "Leaving debug_ct::finish(), _off became " << LIBCWD_DO_TSD_MEMBER_OFF(debug_object) );
set_alloc_checking_on(LIBCWD_TSD);
}
PRAGMA_DIAGNOSTIC_PUSH_IGNORE_infinite_recursion
void debug_tsd_st::fatal_finish(debug_ct& debug_object, channel_set_data_st& channel_set LIBCWD_COMMA_TSD_PARAM)
{
finish(debug_object, channel_set LIBCWD_COMMA_TSD);
DoutFatal( dc::core, "Don't use `DoutFatal' together with `continued_cf', use `Dout' instead. (This message can also occur when using DoutFatal correctly but from the constructor of a global object)." );
_Exit(1); // This is never reached, but g++ 3.3.x -O2 thinks it is.
}
PRAGMA_DIAGNOSTIC_POP
#if LIBCWD_THREAD_SAFE
int debug_ct::S_index_count = 0;
#endif
bool debug_ct::NS_init(LIBCWD_TSD_PARAM)
{
if (NS_being_initialized)
return false;
ST_initialize_globals(LIBCWD_TSD);// Because all allocations for global objects are internal these days, we use
// the constructor of debug_ct to initiate the global initialization of libcwd
// instead of relying on malloc().
if (WNS_initialized)
return true;
NS_being_initialized = true;
#if LIBCWD_THREAD_SAFE
M_mutex = NULL;
unfinished_oss = NULL;
#endif
#if CWDEBUG_DEBUG
if (!WST_debug_object_init_magic)
init_debug_object_init_magic();
init_magic = WST_debug_object_init_magic;
DEBUGDEBUG_CERR( "Set init_magic to " << init_magic );
DEBUGDEBUG_CERR( "Setting WNS_initialized to true" );
#endif
LIBCWD_DEFER_CANCEL;
_private_::debug_objects.init(LIBCWD_TSD);
set_alloc_checking_off(LIBCWD_TSD); // debug_objects is internal.
DEBUG_OBJECTS_ACQUIRE_WRITE_LOCK;
if (find(_private_::debug_objects.write_locked().begin(),
_private_::debug_objects.write_locked().end(), this)
== _private_::debug_objects.write_locked().end()) // Not added before?
_private_::debug_objects.write_locked().push_back(this);
DEBUG_OBJECTS_RELEASE_WRITE_LOCK;
#if LIBCWD_THREAD_SAFE
set_alloc_checking_on(LIBCWD_TSD);
LIBCWD_RESTORE_CANCEL;
set_alloc_checking_off(LIBCWD_TSD); // debug_objects is internal.
#endif
int saved_internal = _private_::set_library_call_on(LIBCWD_TSD);
_private_::set_invisible_on(LIBCWD_TSD);
new (_private_::WST_dummy_laf) laf_ct(0, channels::dc::debug.get_label(), 0); // Leaks 24 bytes of memory
_private_::set_invisible_off(LIBCWD_TSD);
_private_::set_library_call_off(saved_internal LIBCWD_COMMA_TSD);
#if LIBCWD_THREAD_SAFE
WNS_index = S_index_count++;
#if CWDEBUG_DEBUGT
LIBCWD_ASSERT( !_private_::WST_multi_threaded ); // Only the first thread should be initializing debug_ct objects.
#endif
LIBCWD_ASSERT( __libcwd_tsd.do_array[WNS_index] == NULL );
debug_tsd_st& tsd(*(__libcwd_tsd.do_array[WNS_index] = new debug_tsd_st));
#endif
tsd.init();
set_alloc_checking_on(LIBCWD_TSD);
#if CWDEBUG_DEBUGOUTPUT
LIBCWD_TSD_MEMBER_OFF = -1; // Print as much debug output as possible right away.
#else
LIBCWD_TSD_MEMBER_OFF = 0; // Don't print debug output till the REAL initialization of the debug system
// has been performed (ie, the _application_ start (don't confuse that with
// the constructor - which does nothing)).
#endif
DEBUGDEBUG_CERR( "debug_ct::NS_init(), _off set to " << LIBCWD_TSD_MEMBER_OFF );
set_ostream(&std::cerr); // Write to std::cerr by default.
interactive = true; // and thus we're interactive.
NS_being_initialized = false;
WNS_initialized = true;
return true; // Success.
}
void debug_tsd_st::init()
{
DEBUGDEBUG_CERR( "Entering debug_tsd_st::init (this == " << (void*)this << ")");
#if CWDEBUG_DEBUGM
LIBCWD_TSD_DECLARATION;
LIBCWD_ASSERT( __libcwd_tsd.internal );
#endif
start_expected = true; // Of course, we start with expecting the beginning of a debug output.
unfinished_expected = false;
// `current' needs to be non-zero (saving us a check in start()) and
// current.mask needs to be 0 to avoid a crash in start():
current = reinterpret_cast<laf_ct*>(_private_::WST_dummy_laf);
DEBUGDEBUG_CERR( "current = " << (void*)current );
current_bufferstream = NULL;
laf_stack.init();
continued_stack.init();
margin.NS_internal_init("", 0);
marker.NS_internal_init(": ", 2); // LEAK7
#if CWDEBUG_DEBUGOUTPUT
first_time = true;
#endif
off_count = 0;
M_margin_stack = NULL;
M_marker_stack = NULL;
indent = 0;
tsd_initialized = true;
DEBUGDEBUG_CERR( "Leaving debug_tsd_st::init (this == " << (void*)this <<
"); &tsd_initialized == " << (void*)&tsd_initialized );
}
#if LIBCWD_THREAD_SAFE
namespace _private_ {
#if CWDEBUG_DEBUGT
void debug_tsd_init(LIBCWD_TSD_PARAM_UNUSED)
#else
void debug_tsd_init(LIBCWD_TSD_PARAM)
#endif
{
set_alloc_checking_off(LIBCWD_TSD);
LIBCWD_ASSERT( __libcwd_tsd.do_array[(debugObject).WNS_index] == NULL );
debug_tsd_st& tsd(*(__libcwd_tsd.do_array[(debugObject).WNS_index] = new debug_tsd_st));
tsd.init();
set_alloc_checking_on(LIBCWD_TSD);
LIBCWD_DO_TSD_MEMBER_OFF(debugObject) = 0;
);
}
} // namespace _private_
#endif
debug_tsd_st::~debug_tsd_st()
{
#if LIBCWD_THREAD_SAFE
// In the non-threaded case we do not want to deinitialize these because they
// might still be needed if dc::malloc (and/or dc::bfd) is turned on and
// the destructor of some global object that is destructed *after* libcw_do
// is deleting (or allocating) memory.
// In the threaded case, we are called with `internal' set.
margin.deinitialize();
marker.deinitialize();
#endif
if (!tsd_initialized) // Skip the rest when it wasn't initialized.
return;
// Sanity checks:
if (continued_stack.size())
DoutFatal( dc::core|cerr_cf, "Destructing debug_tsd_st with a non-empty continued_stack (missing dc::finish?)" );
if (laf_stack.size())
DoutFatal( dc::core|cerr_cf, "Destructing debug_tsd_st with a non-empty laf_stack" );
}
channel_ct* find_channel(char const* label)
{
channel_ct* tmp = NULL;
LIBCWD_TSD_DECLARATION;
LIBCWD_DEFER_CANCEL;
_private_::debug_channels.init(LIBCWD_TSD);
DEBUG_CHANNELS_ACQUIRE_READ_LOCK;
for(_private_::debug_channels_ct::container_type::const_iterator i(_private_::debug_channels.read_locked().begin());
i != _private_::debug_channels.read_locked().end(); ++i)
{
if (!strncasecmp(label, (*i)->get_label(), strlen(label)))
tmp = (*i);
}
DEBUG_CHANNELS_RELEASE_READ_LOCK;
LIBCWD_RESTORE_CANCEL;
return tmp;
}
void list_channels_on(debug_ct& debug_object)
{
LIBCWD_TSD_DECLARATION;
if (LIBCWD_DO_TSD_MEMBER_OFF(debug_object) < 0)
{
LIBCWD_DEFER_CANCEL;
_private_::debug_channels.init(LIBCWD_TSD);
LIBCWD_RESTORE_CANCEL;
LIBCWD_DEFER_CLEANUP_PUSH(&rwlock_tct<libcwd::_private_::debug_channels_instance>::cleanup, NULL);
DEBUG_CHANNELS_ACQUIRE_READ_LOCK;
for(_private_::debug_channels_ct::container_type::const_iterator i(_private_::debug_channels.read_locked().begin());
i != _private_::debug_channels.read_locked().end(); ++i)
{
LibcwDoutScopeBegin(LIBCWD_DEBUGCHANNELS, debug_object, dc::always|noprefix_cf);
if (NEED_SUPRESSION_OF_MALLOC_AND_BFD)
{
#if CWDEBUG_LOCATION
#endif
}
LibcwDoutStream.write(LIBCWD_DO_TSD_MEMBER(debug_object, margin).c_str(), LIBCWD_DO_TSD_MEMBER(debug_object, margin).size());
LibcwDoutStream.write((*i)->get_label(), WST_max_len);
if ((*i)->is_on(LIBCWD_TSD))
LibcwDoutStream.write(": Enabled", 9);
else
LibcwDoutStream.write(": Disabled", 10);
if (NEED_SUPRESSION_OF_MALLOC_AND_BFD)
{
#if CWDEBUG_LOCATION
#endif
}
LibcwDoutScopeEnd;
}
DEBUG_CHANNELS_RELEASE_READ_LOCK;
LIBCWD_CLEANUP_POP_RESTORE(false);
}
}
void channel_ct::NS_initialize(char const* label LIBCWD_COMMA_TSD_PARAM, bool add_to_channel_list)
{
// This is pretty much identical to fatal_channel_ct::fatal_channel_ct().
if (WNS_initialized)
return; // Already initialized.
DEBUGDEBUG_CERR( "Entering `channel_ct::NS_initialize(\"" << label << "\")'" );
size_t label_len = strlen(label);
if (label_len > max_label_len_c) // Only happens for customized channels
DoutFatal( dc::core, "strlen(\"" << label << "\") > " << max_label_len_c );
LIBCWD_DEFER_CANCEL;
_private_::debug_channels.init(LIBCWD_TSD);
static _private_::debug_channels_ct hidden_channels;
hidden_channels.init(LIBCWD_TSD);
DEBUG_CHANNELS_ACQUIRE_WRITE_LOCK;
set_alloc_checking_off(LIBCWD_TSD); // debug_channels is internal.
const_cast<char*>(channels::dc::core.get_label())[WST_max_len] = ' ';
const_cast<char*>(channels::dc::fatal.get_label())[WST_max_len] = ' ';
_private_::debug_channels_ct::container_type& channels(_private_::debug_channels.write_locked());
for(_private_::debug_channels_ct::container_type::iterator i(channels.begin()); i != channels.end(); ++i)
const_cast<char*>((*i)->get_label())[WST_max_len] = ' ';
_private_::debug_channels_ct::container_type& channels_not_listed(hidden_channels.write_locked());
for(_private_::debug_channels_ct::container_type::iterator i(channels_not_listed.begin());
i != channels_not_listed.end(); ++i)
const_cast<char*>((*i)->get_label())[WST_max_len] = ' ';
// MT: This is not strict thread safe because it is possible that after threads are already
// running, a new shared library is dlopen-ed with a new global channel_ct with a larger
// label. However, it makes no sense to lock the *reading* of WST_max_len for the other
// threads because this assignment is an atomic operation. The lock above is only needed
// to prefend two such simultaneously loaded libraries from causing WST_max_len to end up
// not maximal.
// When this thread is cancelled later, there is no need to take action: the debug channel
// is global and can be used by any thread. We want to keep the maximum label length as
// set by this channel. Even when this debug channel is part of shared library that is
// being loaded by dlopen() then it won't get removed when this thread is cancelled.
// (Actually, a downwards update of WST_max_len should be done by dlclose if at all).
if (label_len > WST_max_len)
WST_max_len = label_len;
const_cast<char*>(channels::dc::core.get_label())[WST_max_len] = '\0';
const_cast<char*>(channels::dc::fatal.get_label())[WST_max_len] = '\0';
for(_private_::debug_channels_ct::container_type::iterator i(channels.begin()); i != channels.end(); ++i)
const_cast<char*>((*i)->get_label())[WST_max_len] = '\0';
for(_private_::debug_channels_ct::container_type::iterator i(channels_not_listed.begin());
i != channels_not_listed.end(); ++i)
const_cast<char*>((*i)->get_label())[WST_max_len] = '\0';
set_alloc_checking_on(LIBCWD_TSD);
#if LIBCWD_THREAD_SAFE
// MT: Take advantage of the `libcwd::_private_::debug_channels_instance' lock to prevent simultaneous access
// to `next_index' in the case of simultaneously dlopen-loaded libraries.
static int next_index;
WNS_index = ++next_index; // Don't use index 0, it is used to make sure that uninitialized channels appear to be off.
__libcwd_tsd.off_cnt_array[WNS_index] = 0;
#else
off_cnt = 0;
#endif
PRAGMA_DIAGNOSTIC_PUSH_IGNORE_stringop_overflow
strncpy(WNS_label, label, label_len);
PRAGMA_DIAGNOSTIC_POP
std::memset(WNS_label + label_len, ' ', max_label_len_c - label_len);
WNS_label[WST_max_len] = '\0';
set_alloc_checking_off(LIBCWD_TSD); // debug_channels is internal.
if (add_to_channel_list)
{
// We store debug channels in some organized order, so that the
// order in which they appear in the ForAllDebugChannels is not
// dependent on the order in which these global objects are
// initialized.
_private_::debug_channels_ct::container_type::iterator i(channels.begin());
for(; i != channels.end(); ++i)
if (strncmp((*i)->get_label(), WNS_label, WST_max_len) > 0)
break;
channels.insert(i, this);
}
else
channels_not_listed.push_back(this);
set_alloc_checking_on(LIBCWD_TSD);
DEBUG_CHANNELS_RELEASE_WRITE_LOCK;
LIBCWD_RESTORE_CANCEL;
// Turn debug channel "WARNING" on by default.
if (strncmp(WNS_label, "WARNING", label_len) == 0)
#if LIBCWD_THREAD_SAFE
__libcwd_tsd.off_cnt_array[WNS_index] = -1;
#else
off_cnt = -1;
#endif
DEBUGDEBUG_CERR( "Leaving `channel_ct::NS_initialize(\"" << label << "\")" );
WNS_initialized = true;
}
void fatal_channel_ct::NS_initialize(char const* label, control_flag_t maskbit LIBCWD_COMMA_TSD_PARAM)
{
// This is pretty much identical to channel_ct::NS_initialize().
if (WNS_maskbit)
return; // Already initialized.
WNS_maskbit = maskbit;
DEBUGDEBUG_CERR( "Entering `fatal_channel_ct::NS_initialize(\"" << label << "\")'" );
size_t label_len = strlen(label);
if (label_len > max_label_len_c) // Only happens for customized channels
DoutFatal( dc::core, "strlen(\"" << label << "\") > " << max_label_len_c );
LIBCWD_DEFER_CANCEL;
_private_::debug_channels.init(LIBCWD_TSD);
DEBUG_CHANNELS_ACQUIRE_WRITE_LOCK;
set_alloc_checking_off(LIBCWD_TSD); // debug_channels is internal.
_private_::debug_channels_ct::container_type& channels(_private_::debug_channels.write_locked());
for(_private_::debug_channels_ct::container_type::iterator i(channels.begin()); i != channels.end(); ++i)
const_cast<char*>((*i)->get_label())[WST_max_len] = ' ';
// MT: See comments in channel_ct::NS_initialize above.
if (label_len > WST_max_len)
WST_max_len = label_len;
for(_private_::debug_channels_ct::container_type::iterator i(channels.begin()); i != channels.end(); ++i)
const_cast<char*>((*i)->get_label())[WST_max_len] = '\0';
set_alloc_checking_on(LIBCWD_TSD);
PRAGMA_DIAGNOSTIC_PUSH_IGNORE_stringop_overflow
strncpy(WNS_label, label, label_len);
PRAGMA_DIAGNOSTIC_POP
std::memset(WNS_label + label_len, ' ', max_label_len_c - label_len);
WNS_label[WST_max_len] = '\0';
DEBUG_CHANNELS_RELEASE_WRITE_LOCK;
LIBCWD_RESTORE_CANCEL;
DEBUGDEBUG_CERR( "Leaving `fatal_channel_ct::NS_initialize(\"" << label << "\")" );
}
void continued_channel_ct::NS_initialize(control_flag_t maskbit)
{
if (!WNS_maskbit)
WNS_maskbit = maskbit;
}
char const always_channel_ct::label[max_label_len_c + 1] = { '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', 0 };
void channel_ct::off()
{
#if LIBCWD_THREAD_SAFE
LIBCWD_TSD_DECLARATION;
__libcwd_tsd.off_cnt_array[WNS_index] += 1;
#else
++off_cnt;
#endif
}
void channel_ct::on()
{
#if LIBCWD_THREAD_SAFE
LIBCWD_TSD_DECLARATION;
if (__libcwd_tsd.off_cnt_array[WNS_index] == -1)
#else
if (off_cnt == -1)
#endif
DoutFatal( dc::core, "Calling channel_ct::on() more often than channel_ct::off()" );
#if LIBCWD_THREAD_SAFE
__libcwd_tsd.off_cnt_array[WNS_index] -= 1;
#else
--off_cnt;
#endif
}
//----------------------------------------------------------------------------------------
// Handle continued channel flags and update `off_count' and `continued_stack'.
//
namespace _private_ {
void print_pop_error() {
DoutFatal(dc::core, "Using \"dc::finish\" without corresponding \"continued_cf\" or "
"calling the Dout(dc::finish, ...) more often than its corresponding "
"Dout(dc::channel|continued_cf, ...). Note that the wrong \"dc::finish\" doesn't "
"have to be the one that we core dumped on, if two or more are nested.");
}
}
continued_channel_set_st& channel_set_st::operator|(continued_cf_nt)
{
#if CWDEBUG_DEBUG
DEBUGDEBUG_CERR( "continued_cf detected" );
if (!do_tsd_ptr || !do_tsd_ptr->tsd_initialized)
{
if (do_tsd_ptr)
DEBUGDEBUG_CERR( "&do_tsd_ptr->tsd_initialized == " << (void*)&do_tsd_ptr->tsd_initialized );
FATALDEBUGDEBUG_CERR( "Don't use DoutFatal together with continued_cf, use Dout instead." );
}
#endif
mask |= continued_cf_maskbit;
if (!on)
{
++(do_tsd_ptr->off_count);
DEBUGDEBUG_CERR( "Channel is switched off. Increased off_count to " << do_tsd_ptr->off_count );
}
else
{
do_tsd_ptr->continued_stack.push(do_tsd_ptr->off_count);
DEBUGDEBUG_CERR( "Channel is switched on. Pushed off_count (" << do_tsd_ptr->off_count << ") to stack (size now " <<
do_tsd_ptr->continued_stack.size() << ") and set off_count to 0" );
do_tsd_ptr->off_count = 0;
}
return *(reinterpret_cast<continued_channel_set_st*>(this));
}
continued_channel_set_st& channel_set_bootstrap_st::operator|(continued_channel_ct const& cdc)
{
#if CWDEBUG_DEBUG
if ((cdc.get_maskbit() & continued_maskbit))
DEBUGDEBUG_CERR( "dc::continued detected" );
else
DEBUGDEBUG_CERR( "dc::finish detected" );
#endif
if ((on = !do_tsd_ptr->off_count))
{
DEBUGDEBUG_CERR( "Channel is switched on (off_count is 0)" );
do_tsd_ptr->current->mask |= cdc.get_maskbit(); // We continue with the current channel
mask = do_tsd_ptr->current->mask;
label = do_tsd_ptr->current->label;
if (cdc.get_maskbit() == finish_maskbit)
{
do_tsd_ptr->off_count = do_tsd_ptr->continued_stack.top();
do_tsd_ptr->continued_stack.pop();
DEBUGDEBUG_CERR( "Restoring off_count to " << do_tsd_ptr->off_count << ". Stack size now " << do_tsd_ptr->continued_stack.size() );
}
}
else
{
DEBUGDEBUG_CERR( "Channel is switched off (off_count is " << do_tsd_ptr->off_count << ')' );
if (cdc.get_maskbit() == finish_maskbit)
{
DEBUGDEBUG_CERR( "` decrementing off_count with 1" );
--(do_tsd_ptr->off_count);
}
}
return *reinterpret_cast<continued_channel_set_st*>(this);
}
namespace _private_ {
void assert_fail(char const* expr, char const* file, int line, char const* function)
{
#if CWDEBUG_DEBUG
LIBCWD_TSD_DECLARATION;
if (__libcwd_tsd.recursive_assert
#if CWDEBUG_DEBUGM
|| __libcwd_tsd.inside_malloc_or_free
#endif
)
{
if (!__libcwd_tsd.recursive_assert
#if CWDEBUG_ALLOC
&& __libcwd_tsd.library_call < 6
#endif
)
{
#if CWDEBUG_ALLOC
int saved_internal = 0; // To avoid 'may be used uninitialized' compiler warning.
bool is_internal = __libcwd_tsd.internal;
if (is_internal)
saved_internal = _private_::set_library_call_on(LIBCWD_TSD); // flush is a library call.
else
++__libcwd_tsd.library_call;
#endif
Debug( libcw_do.get_ostream()->flush() );
#if CWDEBUG_ALLOC
if (is_internal)
_private_::set_library_call_off(saved_internal LIBCWD_COMMA_TSD);
else
--__libcwd_tsd.library_call;
#endif
}
set_alloc_checking_off(LIBCWD_TSD);
FATALDEBUGDEBUG_CERR(file << ':' << line << ": " << function << ": Assertion `" << expr << "' failed.\n");
set_alloc_checking_on(LIBCWD_TSD);
}
__libcwd_tsd.recursive_assert = true;
#if CWDEBUG_DEBUGT
__libcwd_tsd.internal_debugging_code = true;
#endif
#endif
DoutFatal(dc::core, file << ':' << line << ": " << function << ": Assertion `" << expr << "' failed.\n");
}
} // namespace _private_
void debug_ct::force_on(debug_ct::OnOffState& state)
{
LIBCWD_TSD_DECLARATION;
#if CWDEBUG_DEBUG
if (!NS_init(LIBCWD_TSD))
DoutFatal(dc::core, "Calling debug_ct::NS_init recursively from debug_ct::force_on");
#else
(void)NS_init(LIBCWD_TSD);
#endif
state._off = LIBCWD_TSD_MEMBER_OFF;
#if CWDEBUG_DEBUGOUTPUT
state.first_time = LIBCWD_TSD_MEMBER(first_time);
#endif
LIBCWD_TSD_MEMBER_OFF = -1; // Turn object on.
}
void debug_ct::restore(debug_ct::OnOffState const& state)
{
LIBCWD_TSD_DECLARATION;
#if CWDEBUG_DEBUGOUTPUT
if (state.first_time != LIBCWD_TSD_MEMBER(first_time)) // state.first_time && !first_time.
core_dump(); // on() was called without first a call to off().
#endif
if (LIBCWD_TSD_MEMBER_OFF != -1)
core_dump(); // off() and on() where called and not in equal pairs.
LIBCWD_TSD_MEMBER_OFF = state._off; // Restore.
}
void channel_ct::force_on(channel_ct::OnOffState& state, char const* label)
{
LIBCWD_TSD_DECLARATION;
NS_initialize(label LIBCWD_COMMA_TSD, true);
#if LIBCWD_THREAD_SAFE
int& off_cnt(__libcwd_tsd.off_cnt_array[WNS_index]);
#endif
state.off_cnt = off_cnt;
off_cnt = -1; // Turn channel on.
}
void channel_ct::restore(channel_ct::OnOffState const& state)
{
#if LIBCWD_THREAD_SAFE
LIBCWD_TSD_DECLARATION;
int& off_cnt(__libcwd_tsd.off_cnt_array[WNS_index]);
#endif
if (off_cnt != -1)
core_dump(); // off() and on() where called and not in equal pairs.
off_cnt = state.off_cnt; // Restore.
}
#if LIBCWD_THREAD_SAFE
// Specialization
template<>
void debug_ct::set_ostream(std::ostream* os, pthread_mutex_t* mutex)
{
LIBCWD_TSD_DECLARATION;
_private_::set_alloc_checking_off(LIBCWD_TSD);
_private_::lock_interface_base_ct* new_mutex = new _private_::pthread_lock_interface_ct(mutex); // A single LEAK of 20 bytes per debug object from here is ok.
_private_::set_alloc_checking_on(LIBCWD_TSD);
LIBCWD_DEFER_CANCEL;
_private_::mutex_tct<_private_::set_ostream_instance>::lock();
_private_::lock_interface_base_ct* old_mutex = M_mutex;
if (old_mutex)
old_mutex->lock(); // Make sure all other threads left this critical area.
M_mutex = new_mutex;
if (old_mutex)
{
old_mutex->unlock();
_private_::set_alloc_checking_off(LIBCWD_TSD);
delete old_mutex;
_private_::set_alloc_checking_on(LIBCWD_TSD);
}
private_set_ostream(os);
_private_::mutex_tct<_private_::set_ostream_instance>::unlock();
LIBCWD_RESTORE_CANCEL;
}
#endif // LIBCWD_THREAD_SAFE
void debug_ct::set_ostream(std::ostream* os)
{
#if LIBCWD_THREAD_SAFE
if (_private_::WST_multi_threaded)
#if CWDEBUG_LOCATION
Dout(dc::warning, location_ct((char*)__builtin_return_address(0) + builtin_return_address_offset) << ": You should passing a locking mechanism to `set_ostream' for the ostream (see documentation/reference-manual/group__group__destination.html)");
#else
DoutFatal(dc::core, "You must pass a locking mechanism to `set_ostream' for the ostream (see documentation/reference-manual/group__group__destination.html)");
#endif
#if CWDEBUG_DEBUGT
LIBCWD_TSD_DECLARATION;
#endif
LIBCWD_DEFER_CANCEL;
_private_::mutex_tct<_private_::set_ostream_instance>::lock();
#endif
private_set_ostream(os);
#if LIBCWD_THREAD_SAFE
_private_::mutex_tct<_private_::set_ostream_instance>::unlock();
LIBCWD_RESTORE_CANCEL;
#endif
}
} // namespace libcwd
// This can be used in configure to see if libcwd exists.
extern "C" char const* const __libcwd_version = VERSION;
// The following functions can be invoked from gdb directly.
namespace libcwd {
namespace _private_ {
extern void demangle_symbol(char const* in, _private_::internal_string& out);
} // namespace _private_
} // namespace libcwd
void off()
Turn this channel off.
Definition: debug.cc:1780
void on()
Cancel one call to ‘off()’.
Definition: debug.cc:1795
size_t size() const
The size of the string.
Definition: class_debug_string.inl:89
void reserve(size_t)
Reserve memory for the string in advance.
Definition: debug.cc:905
This is the main header file of libcwd.
#define Dout(cntrl, data)
Macro for writing debug output.
Definition: debug.h:154
#define Debug(STATEMENTS...)
Encapsulation macro for general debugging code.
Definition: debug.h:124
void core_dump()
Dump core of current thread.
Definition: debug.cc:806
control_flag_t const blank_marker_cf
Replace marker by white space.
Definition: control_flag.h:50
control_flag_t const noprefix_cf
Omit margin, label, marker and indentation.
Definition: control_flag.h:38
control_flag_t const nolabel_cf
Omit label, marker and indentation.
Definition: control_flag.h:41
unsigned int control_flag_t
Definition: control_flag.h:31
control_flag_t const nonewline_cf
Omit the default new line at the end.
Definition: control_flag.h:35
control_flag_t const blank_margin_cf
Replace margin by white space.
Definition: control_flag.h:44
continued_cf_nt
continued_cf has its own type for overloading purposes.
Definition: control_flag.h:75
control_flag_t const error_cf
Append error string according to errno.
Definition: control_flag.h:62
control_flag_t const cerr_cf
Force output to be written to cerr.
Definition: control_flag.h:53
control_flag_t const blank_label_cf
Replace label by white space.
Definition: control_flag.h:47
control_flag_t const wait_cf
If interactive, wait till return is pressed.
Definition: control_flag.h:59
control_flag_t const flush_cf
Flush ostream after writing this output.
Definition: control_flag.h:56
channel_ct system
Definition: debug.cc:467
channel_ct malloc
Definition: debug.cc:474
channel_ct bfd
Definition: bfd.cc:92
continued_channel_ct finish
Definition: debug.cc:517
fatal_channel_ct fatal
Definition: debug.cc:527
channel_ct notice
Definition: debug.cc:460
continued_channel_ct continued
Definition: debug.cc:506
channel_ct debug
Definition: debug.cc:453
always_channel_ct always
Definition: debug.cc:495
fatal_channel_ct core
Definition: debug.cc:537
channel_ct warning
Definition: debug.cc:485
void demangle_symbol(char const *input, std::string &output)
Demangle mangled symbol name input and write the result to string output.
Definition: demangle3.cc:825
std::ostream * get_ostream() const
Get the ostream device as set with set_ostream().
Definition: class_debug.inl:128
#define DoutFatal(cntrl, data)
Macro for writing fatal debug output to the default debug object libcw_do .
Definition: debug.h:164
void pop_margin()
Pop margin from the stack.
Definition: debug.cc:946
void push_marker()
Push the current marker on a stack.
Definition: debug.cc:962
void pop_marker()
Pop marker from the stack.
Definition: debug.cc:976
void push_margin()
Push the current margin on a stack.
Definition: debug.cc:932
debug_string_ct & margin()
The margin.
Definition: class_debug.inl:43
debug_string_ct & marker()
The marker.
Definition: class_debug.inl:59
channel_ct * find_channel(char const *label)
Find debug channel with label label.
Definition: debug.cc:1528
#define ForAllDebugObjects(STATEMENT...)
Looping over all debug objects.
Definition: debug.h:209
void list_channels_on(debug_ct &debug_object)
List all debug channels to a given debug object.
Definition: debug.cc:1575
namespace for libcwd.
Definition: debug.cc:87
debug_ct libcw_do
The default debug object.
Definition: debug.cc:429
int const builtin_return_address_offset
Offset to __builtin_return_address() needed to get the correct line number from location_ct.
Definition: sys.h:39
unsigned short const max_label_len_c
The maximum number of characters that are allowed in a debug channel label.
Definition: max_label_len.h:24
«download»

The above is just an example of course.  But if you'd use it, then you would initialize libcwd by including the following at the top of main:

int main()
{
#ifdef CWDEBUG
myproject::debug::init();
#endif
// ...

And every thread would call myproject::debug::init_thread(); when started.

Copyright © 2001 - 2004 Carlo Wood.  All rights reserved.