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.

// EXAMPLE: If you use autoconf you can add the following here.
// #ifdef HAVE_CONFIG_H
// #include "config.h"
// #endif

// EXAMPLE: You could add stuff like this here too
// (Otherwise add -DCWDEBUG to your CFLAGS).
// #if defined(WANTSDEBUGGING) && defined(HAVE_LIBCWD_BLAHBLAH)
// #define CWDEBUG
// #endif

// The following is the libcwd related mandatory part.
// It must be included before any system header file is included!
#ifdef CWDEBUG
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <libcwd/sys.h>
#endif
«download»
debug.h example template
#ifndef DEBUG_H
#define DEBUG_H

#ifndef CWDEBUG

#include <iostream>     // std::cerr
#include <cstdlib>      // std::exit, EXIT_FAILURE

#define AllocTag1(p)
#define AllocTag2(p, desc)
#define AllocTag_dynamic_description(p, data)
#define AllocTag(p, data)
#define Debug(STATEMENT...)
#define Dout(cntrl, data)
#define DoutFatal(cntrl, data) LibcwDoutFatal(, , cntrl, data)
#define ForAllDebugChannels(STATEMENT...)
#define ForAllDebugObjects(STATEMENT...)
#define LibcwDebug(dc_namespace, STATEMENT...)
#define LibcwDout(dc_namespace, d, cntrl, data)
#define LibcwDoutFatal(dc_namespace, d, cntrl, data) do { ::std::cerr << data << ::std::endl; ::std::exit(EXIT_FAILURE); } while(1)
#define LibcwdForAllDebugChannels(dc_namespace, STATEMENT...)
#define LibcwdForAllDebugObjects(dc_namespace, STATEMENT...)
#define NEW(x) new x
#define CWDEBUG_ALLOC 0
#define CWDEBUG_MAGIC 0
#define CWDEBUG_LOCATION 0
#define CWDEBUG_LIBBFD 0
#define CWDEBUG_DEBUG 0
#define CWDEBUG_DEBUGOUTPUT 0
#define CWDEBUG_DEBUGM 0
#define CWDEBUG_DEBUGT 0
#define CWDEBUG_MARKER 0

#else // CWDEBUG

// This must be defined before <libcwd/debug.h> is included and must be the
// name of the namespace containing your `dc' (Debug Channels) namespace
// (see below).  You can use any namespace(s) you like, except existing
// namespaces (like ::, ::std and ::libcwd).
#define DEBUGCHANNELS ::myproject::debug::channels
#include <libcwd/debug.h>

namespace myproject {
  namespace debug {

    void init(void);            // Initialize debugging code from main().
    void init_thread(void);     // Initialize debugging code from new threads.

    namespace channels {        // This is the DEBUGCHANNELS namespace, see above.
      namespace dc {            // 'dc' is defined inside DEBUGCHANNELS.

        using namespace libcwd::channels::dc;
        using libcwd::channel_ct;

        // Add the declaration of new debug channels here
        // and add their definition in a custom debug.cc file.
        extern channel_ct custom;

      } // namespace dc
    } // namespace DEBUGCHANNELS
  }
}

#endif // CWDEBUG

#endif // DEBUG_H
«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: /cvsroot/libcwd/libcwd/debug.cc,v 1.143 2004/09/26 15:50:58 libcw Exp $
//
// 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
extern "C" char* getenv(char const* name);      // Needed before including 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 <libcwd/strerrno.h>
#include <libcwd/private_internal_stringbuf.h>
#include <libcwd/private_bufferstream.h>
#include "private_debug_stack.inl"
#include <libcwd/class_location.inl>

extern "C" int raise(int);
extern "C" void _exit(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(void)
{
#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(void)
{
#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(void) : 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(void) {
        position = this->pubseekoff(0, std::ios_base::cur, std::ios_base::out);
      }
      void restore_position(void) {
        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, debug_ct& debug_object,
        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(void)
    {
      return config_signature_lib_c;
    }

    // Put this here to decrease the code size of `check_configuration'
    void conf_check_failed(void)
    {
      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(void)
    {
      DoutFatal(dc::fatal, "check_configuration: This version of libcwd does not match the version of libcwd/config.h!  Are your paths correct?");
    }

    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

#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_exception();          // 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;
          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(void)
      {
        _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;
          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( "_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(void)
      {
        _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(void)
      {
        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(void)
    {
      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'.

      control_flag_t mask;
        // 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(void)
    {
#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>::trylock())
      {
#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.
#ifdef HAVE_PTHREAD_KILL_OTHER_THREADS_NP
      // For the same reason, kill other threads prior to generating the core.
      //pthread_kill_other_threads_np(); // Only causes a deadlock.
#endif
#endif
#if LIBCWD_THREAD_SAFE && CWDEBUG_DEBUG
      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.
      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(void)
    {
      free(M_str);
      M_str = NULL;
    }

    // This is called with alloc checking on (or off).
    debug_string_ct::~debug_string_ct(void)
    {
#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;
    }

    /* \{ */

    void debug_ct::push_margin(void)
    {
      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;
    }

    void debug_ct::pop_margin(void)
    {
      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;
    }

    void debug_ct::push_marker(void)
    {
      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;
    }

    void debug_ct::pop_marker(void)
    {
      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)
      {
        channels::dc::malloc.off();
#if CWDEBUG_LOCATION
        channels::dc::bfd.off();
#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->trylock()))
              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);
      _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
      if (!(channel_set.mask & (noprefix_cf|nolabel_cf|blank_margin_cf|blank_label_cf|blank_marker_cf)))
      {
        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& channel_set 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)
      {
        channels::dc::malloc.on();
#if CWDEBUG_LOCATION
        channels::dc::bfd.on();
#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 = strerror_r(current->err, error_text_buf, sizeof(error_text_buf));
#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))
            core_dump();
          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>::trylock())
          {
            // 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()) && (_private_::WST_is_NPTL || (*thread_iter).tid != 1024))
              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);
    }

    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.
    }

#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(void)
    {
      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);
#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_ {
      void debug_tsd_init(LIBCWD_TSD_PARAM)
      {
        ForAllDebugObjects(
          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)
          {
            channels::dc::malloc.on();
#if CWDEBUG_LOCATION
            channels::dc::bfd.on();
#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)
          {
            channels::dc::malloc.off();
#if CWDEBUG_LOCATION
            channels::dc::bfd.off();
#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.
      _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;

      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 prefend 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

      strncpy(WNS_label, label, label_len);
      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);

      strncpy(WNS_label, label, label_len);
      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(void)
    {
#if LIBCWD_THREAD_SAFE
      LIBCWD_TSD_DECLARATION;
      __libcwd_tsd.off_cnt_array[WNS_index] += 1;
#else
      ++off_cnt;
#endif
    }

    void channel_ct::on(void)
    {
#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(void) {
        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." );
        core_dump();
      }
#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);
    }

    channel_set_st& channel_set_bootstrap_st::operator|(fatal_channel_ct const&)
    {
#if CWDEBUG_LOCATION
      DoutFatal(dc::fatal, location_ct((char*)__builtin_return_address(0) + libcwd::builtin_return_address_offset) <<
          " : Don't use Dout together with dc::core or dc::fatal!  Use DoutFatal instead.");
#else
      DoutFatal(dc::core,
          "Don't use Dout together with dc::core or dc::fatal!  Use DoutFatal instead.");
#endif
    }

    channel_set_st& channel_set_bootstrap_st::operator&(channel_ct const&)
    {
#if CWDEBUG_LOCATION
      DoutFatal(dc::fatal, location_ct((char*)__builtin_return_address(0) + libcwd::builtin_return_address_offset) <<
          " : Use dc::core or dc::fatal together with DoutFatal.");
#else
      DoutFatal(dc::core,
          "Use dc::core or dc::fatal together with DoutFatal.");
#endif
    }

    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;
            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);
          core_dump();
        }
        __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
    Dout(dc::core, "You must passing 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
«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.