Add support for libcwd.

This patch has no influence if you don't have libcwd installed.
Note that libcwd (http://libcwd.sourceforge.net/) is only
available for linux.

A default compile of libcwd does memory allocation tracking,
which is too slow for everyday usage of the viewer (usable,
but notably slower) and we don't need that. Configure your
libcwd as follows:

./configure --prefix=/sl/usr --disable-alloc --enable-optimize

Or whatever prefix you prefer (add --enable-maintainer-mode
if you're compiling it from the SVN repository), add
--disable-nonthreading to compile twice as fast.

If you have it installed you can activate it's use by setting a
few environment variables:

CXXFLAGS="$(pkg-config --cflags libcwd_r)"
LDFLAGS="$(pkg-config --libs libcwd_r) -lpthread"

and then reconfiguring the viewer.

The -lpthread is needed when using ld.gold, however, if you
leave it out you might get an LDFLAGS that ends on trailing
whitespaces, which doesn't work for some reason.

Also, if you installed it in a non-standard place (/sl/usr
above) then you need this to run the viewer (and tests):

export LD_LIBRARY_PATH="/sl/usr/lib"
This commit is contained in:
Aleric Inglewood
2011-05-08 16:16:11 +02:00
parent 3a1d753344
commit 474acdbff9
24 changed files with 772 additions and 8 deletions

View File

@@ -44,6 +44,7 @@ endif(NOT STANDALONE)
add_custom_target(prepare DEPENDS ${prepare_depends})
add_subdirectory(cmake)
add_subdirectory(${LIBS_OPEN_PREFIX}cwdebug)
add_subdirectory(${LIBS_OPEN_PREFIX}llaudio)
add_subdirectory(${LIBS_OPEN_PREFIX}llcharacter)
add_subdirectory(${LIBS_OPEN_PREFIX}llcommon)

View File

@@ -0,0 +1 @@
set(CWDEBUG_LIBRARIES cwdebug)

View File

@@ -3,6 +3,7 @@
INCLUDE(APR)
INCLUDE(LLMath)
INCLUDE(Tut)
INCLUDE(Cwdebug)
MACRO(ADD_BUILD_TEST_NO_COMMON name parent)
# MESSAGE("${CMAKE_CURRENT_SOURCE_DIR}/tests/${name}_test.cpp")

View File

@@ -6,6 +6,7 @@ include(EXPAT)
include(ZLIB)
set(LLCOMMON_INCLUDE_DIRS
${LIBS_OPEN_DIR}/cwdebug
${LIBS_OPEN_DIR}/llcommon
${APR_INCLUDE_DIR}
${Boost_INCLUDE_DIRS}

View File

@@ -0,0 +1,33 @@
# -*- cmake -*-
project(cwdebug)
include(00-Common)
include(LLCommon)
include(LLMath)
include(LLMessage)
include(LLVFS)
include_directories (${CMAKE_CURRENT_SOURCE_DIR})
set(cwdebug_SOURCE_FILES
debug.cc
)
set(cwdebug_HEADER_FILES
CMakeLists.txt
cwdebug.h
sys.h
debug.h
debug_ostream_operators.h
)
set_source_files_properties(${cwdebug_HEADER_FILES}
PROPERTIES HEADER_FILE_ONLY TRUE)
add_definitions(-fPIC)
list(APPEND cwdebug_SOURCE_FILES ${cwdebug_HEADER_FILES})
add_library (cwdebug ${cwdebug_SOURCE_FILES})

9
indra/cwdebug/cwdebug.h Normal file
View File

@@ -0,0 +1,9 @@
// We support compiling C++ source files that are not
// thread-safe, but in that case we assume that they
// will not be linked with libcwd_r.
#if !defined(_REENTRANT) || !defined(__linux__)
#undef CWDEBUG
#endif
#include "sys.h"
#include "debug.h"

412
indra/cwdebug/debug.cc Normal file
View File

@@ -0,0 +1,412 @@
// slviewer -- Second Life Viewer Source Code
//
//! @file debug.cc
//! @brief This file contains the definitions of debug related objects and functions.
//
// Copyright (C) 2008, 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 program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef USE_PCH
#include "sys.h" // Needed for platform-specific code
#endif
#ifdef CWDEBUG
#ifndef USE_PCH
#include <cctype> // Needed for std::isprint
#include <iomanip> // Needed for setfill and setw
#include <map>
#include <string>
#include <sstream>
#include <fstream>
#include "debug.h"
#ifdef USE_LIBCW
#include <libcw/memleak.h> // memleak_filter
#endif
#include <sys/types.h>
#include <unistd.h>
#endif // USE_PCH
#define BACKTRACE_AQUIRE_LOCK libcwd::_private_::mutex_tct<libcwd::_private_::backtrace_instance>::lock()
#define BACKTRACE_RELEASE_LOCK libcwd::_private_::mutex_tct<libcwd::_private_::backtrace_instance>::unlock()
namespace debug {
#if CWDEBUG_LOCATION
void BackTrace::dump_backtrace(void) const
{
for (int frame = 0; frame < frames(); ++frame)
{
libcwd::location_ct loc((char*)buffer()[frame] + libcwd::builtin_return_address_offset);
Dout(dc::notice|continued_cf, '#' << std::left << std::setw(3) << frame <<
std::left << std::setw(16) << buffer()[frame] << ' ' << loc << "\n in ");
char const* mangled_function_name = loc.mangled_function_name();
if (mangled_function_name != libcwd::unknown_function_c)
{
std::string demangled_function_name;
libcwd::demangle_symbol(mangled_function_name, demangled_function_name);
Dout(dc::finish, demangled_function_name);
}
else
Dout(dc::finish, mangled_function_name);
}
}
#endif // CWDEBUG_LOCATION
#if CWDEBUG_ALLOC && CWDEBUG_LOCATION
typedef std::map<BackTrace, int, std::less<BackTrace>, libcwd::_private_::internal_allocator> backtrace_map_t;
backtrace_map_t* backtrace_map;
static int total_calls = 0;
static int number_of_stack_traces = 0;
void my_backtrace_hook(void** buffer, int frames LIBCWD_COMMA_TSD_PARAM)
{
++total_calls;
backtrace_map_t::iterator iter;
set_alloc_checking_off(__libcwd_tsd);
{
BackTrace backtrace(buffer, frames);
std::pair<backtrace_map_t::iterator, bool> res = backtrace_map->insert(backtrace_map_t::value_type(backtrace, 0));
if (res.second)
++number_of_stack_traces;
++res.first->second;
iter = res.first;
}
set_alloc_checking_on(__libcwd_tsd);
#if 0
// Dump the stack trace.
iter->first.dump_backtrace();
#endif
}
void start_recording_backtraces(void)
{
BACKTRACE_AQUIRE_LOCK;
libcwd::backtrace_hook = my_backtrace_hook;
BACKTRACE_RELEASE_LOCK;
//Debug(dc::malloc.on());
LIBCWD_TSD_DECLARATION;
set_alloc_checking_off(__libcwd_tsd);
backtrace_map = new backtrace_map_t;
set_alloc_checking_on(__libcwd_tsd);
}
struct Compare {
bool operator()(backtrace_map_t::const_iterator const& iter1, backtrace_map_t::const_iterator const& iter2)
{
return iter1->second > iter2->second;
}
};
void stop_recording_backtraces(void)
{
//Debug(dc::malloc.off());
BACKTRACE_AQUIRE_LOCK;
libcwd::backtrace_hook = NULL;
if (!backtrace_map)
{
Dout(dc::notice, "Not recording; call cwdebug_start() first.");
return;
}
Dout(dc::notice, "Total number of calls: " << total_calls);
Dout(dc::notice, "Number of different stack traces: " << number_of_stack_traces);
Dout(dc::notice, "First 10 stack traces:");
std::list<backtrace_map_t::const_iterator> entries;
for (backtrace_map_t::const_iterator iter = backtrace_map->begin(); iter != backtrace_map->end(); ++iter)
entries.push_back(iter);
entries.sort(Compare());
int count = 0;
for (std::list<backtrace_map_t::const_iterator>::iterator iter = entries.begin(); iter != entries.end(); ++iter, ++count)
{
Dout(dc::notice, "Used: " << (*iter)->second);
// Dump the stack trace.
(*iter)->first.dump_backtrace();
if (count == 10)
break;
}
// Clear all data.
LIBCWD_TSD_DECLARATION;
set_alloc_checking_off(__libcwd_tsd);
delete backtrace_map;
set_alloc_checking_on(__libcwd_tsd);
backtrace_map = NULL;
total_calls = 0;
number_of_stack_traces = 0;
BACKTRACE_RELEASE_LOCK;
}
#endif // CWDEBUG_ALLOC && CWDEBUG_LOCATION
namespace channels { // namespace DEBUGCHANNELS
namespace dc {
#ifndef DOXYGEN
#define DDCN(x) (x)
#endif
// Add new debug channels here.
channel_ct viewer DDCN("VIEWER"); //!< This debug channel is used for the normal debugging out of the viewer.
channel_ct primbackup DDCN("PRIMBACKUP"); //!< This debug channel is used for output related to primbackup.
channel_ct gtk DDCN("GTK"); //!< This debug channel is used for output related to gtk.
channel_ct sdl DDCN("SDL"); //!< This debug channel is used for output related to sdl locking.
channel_ct backtrace DDCN("BACKTRACE"); //!< This debug channel is used for backtraces.
} // namespace dc
} // namespace DEBUGCHANNELS
// Anonymous namespace, this map and its initialization functions are private to this file
// for Thead-safeness reasons.
namespace {
/*! @brief The type of rcfile_dc_states.
* @internal
*/
typedef std::map<std::string, bool> rcfile_dc_states_type;
/*! @brief Map containing the default debug channel states used at the start of each new thread.
* @internal
*
* The first thread calls main, which calls debug::init which will initialize this
* map with all debug channel labels and whether or not they were turned on in the
* rcfile or not.
*/
rcfile_dc_states_type rcfile_dc_states;
/*! @brief Set the default state of debug channel \a dc_label.
* @internal
*
* This function is called once for each debug channel.
*/
void set_state(char const* dc_label, bool is_on)
{
std::pair<rcfile_dc_states_type::iterator, bool> res =
rcfile_dc_states.insert(rcfile_dc_states_type::value_type(std::string(dc_label), is_on));
if (!res.second)
Dout(dc::warning, "Calling set_state() more than once for the same label!");
return;
}
/*! @brief Save debug channel states.
* @internal
*
* One time initialization function of rcfile_dc_state.
* This must be called from debug::init after reading the rcfile.
*/
void save_dc_states(void)
{
// We may only call this function once: it reflects the states as stored
// in the rcfile and that won't change. Therefore it is not needed to
// lock `rcfile_dc_states', it is only written to by the first thread
// (once, via main -> init) when there are no other threads yet.
static bool second_time = false;
if (second_time)
{
Dout(dc::warning, "Calling save_dc_states() more than once!");
return;
}
second_time = true;
ForAllDebugChannels( set_state(debugChannel.get_label(), debugChannel.is_on()) );
}
} // anonymous namespace
/*! @brief Returns the the original state of a debug channel.
* @internal
*
* For a given \a dc_label, which must be the exact name (<tt>channel_ct::get_label</tt>) of an
* existing debug channel, this function returns \c true when the corresponding debug channel was
* <em>on</em> at the startup of the application, directly after reading the libcwd runtime
* configuration file (.libcwdrc).
*
* If the label/channel did not exist at the start of the application, it will return \c false
* (note that libcwd disallows adding debug channels to modules - so this would probably
* a bug).
*/
bool is_on_in_rcfile(char const* dc_label)
{
rcfile_dc_states_type::const_iterator iter = rcfile_dc_states.find(std::string(dc_label));
if (iter == rcfile_dc_states.end())
{
Dout(dc::warning, "is_on_in_rcfile(\"" << dc_label << "\"): \"" << dc_label << "\" is an unknown label!");
return false;
}
return iter->second;
}
#if LIBCWD_THREAD_SAFE
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
// I can cause I'm the maintainer of libcwd ;).
libcwd::_private_::pthread_lock_interface_ct cout_mutex(&mutex);
libcwd::_private_::lock_interface_base_ct* cout_mutex_ptr(&cout_mutex);
#endif
/*! @brief Initialize debugging code from new threads.
*
* This function needs to be called at the start of each new thread,
* because a new thread starts in a completely reset state.
*
* The function turns on all debug channels that were turned on
* after reading the rcfile at the start of the application.
* Furthermore it initializes the debug ostream, its mutex and the
* margin of the default debug object (Dout).
*/
void init_thread(void)
{
// Turn on all debug channels that are turned on as per rcfile configuration.
ForAllDebugChannels(
if (!debugChannel.is_on() && is_on_in_rcfile(debugChannel.get_label()))
debugChannel.on();
);
// Turn on debug output.
Debug( libcw_do.on() );
static std::ofstream debug_file;
static bool debug_out_opened = false;
if (!debug_out_opened)
{
debug_out_opened = true; // Thread-safe, cause the main thread calls this first while it's still alone.
std::ostringstream os;
os << "debug.out." << getpid();
debug_file.open(os.str().c_str());
}
static debug::TeeStream debug_stream(std::cout, debug_file);
#if LIBCWD_THREAD_SAFE
Debug( libcw_do.set_ostream(&debug_stream, cout_mutex_ptr) );
#else
Debug( libcw_do.set_ostream(&debug_stream) );
#endif
static bool first_thread = true;
if (!first_thread) // So far, the application has only one thread. So don't add a thread id.
{
// Set the thread id in the margin.
char margin[18];
sprintf(margin, "0x%-14lx ", pthread_self());
Debug( libcw_do.margin().assign(margin, 17) );
}
first_thread = false;
}
/*! @brief Initialize debugging code from main.
*
* This function initializes the debug code.
*/
void init(void)
{
#if CWDEBUG_ALLOC && defined(USE_LIBCW)
// Tell the memory leak detector which parts of the code are
// expected to leak so that we won't get an alarm for those.
{
std::vector<std::pair<std::string, std::string> > hide_list;
hide_list.push_back(std::pair<std::string, std::string>("libdl.so.2", "_dlerror_run"));
hide_list.push_back(std::pair<std::string, std::string>("libstdc++.so.6", "__cxa_get_globals"));
// The following is actually necessary because of a bug in glibc
// (see http://sources.redhat.com/bugzilla/show_bug.cgi?id=311).
hide_list.push_back(std::pair<std::string, std::string>("libc.so.6", "dl_open_worker"));
memleak_filter().hide_functions_matching(hide_list);
}
{
std::vector<std::string> hide_list;
// Also because of http://sources.redhat.com/bugzilla/show_bug.cgi?id=311
hide_list.push_back(std::string("ld-linux.so.2"));
memleak_filter().hide_objectfiles_matching(hide_list);
}
memleak_filter().set_flags(libcwd::show_objectfile|libcwd::show_function);
#endif
// The following call allocated the filebuf's of cin, cout, cerr, wcin, wcout and wcerr.
// Because this causes a memory leak being reported, make them invisible.
Debug(set_invisible_on());
// You want this, unless you mix streams output with C output.
// Read http://gcc.gnu.org/onlinedocs/libstdc++/27_io/howto.html#8 for an explanation.
//std::ios::sync_with_stdio(false);
// Cancel previous call to set_invisible_on.
Debug(set_invisible_off());
// This will warn you when you are using header files that do not belong to the
// shared libcwd object that you linked with.
Debug( check_configuration() );
Debug(
libcw_do.on(); // Show which rcfile we are reading!
ForAllDebugChannels(
while (debugChannel.is_on())
debugChannel.off() // Print as little as possible though.
);
read_rcfile(); // Put 'silent = on' in the rcfile to suppress most of the output here.
libcw_do.off()
);
save_dc_states();
init_thread();
}
#if CWDEBUG_LOCATION
/*! @brief Return call location.
*
* @param return_addr The return address of the call.
*/
std::string call_location(void const* return_addr)
{
libcwd::location_ct loc((char*)return_addr + libcwd::builtin_return_address_offset);
std::ostringstream convert;
convert << loc;
return convert.str();
}
std::vector<BackTrace> __attribute__ ((visibility("default"))) backtraces;
pthread_mutex_t __attribute__ ((visibility("default"))) backtrace_mutex = PTHREAD_MUTEX_INITIALIZER;
#endif
} // namespace debug
#if CWDEBUG_ALLOC
// These can be called from gdb.
void cwdebug_start()
{
debug::start_recording_backtraces();
}
void cwdebug_stop()
{
debug::stop_recording_backtraces();
}
#endif
#if CWDEBUG_LOCATION
void cwdebug_backtrace(int n)
{
if (0 < n && n <= debug::backtraces.size())
{
Dout(dc::backtrace, "Backtrace #" << n << ":");
debug::backtraces[n - 1].dump_backtrace();
}
else
std::cout << "No such backtrace nr. Max is " << debug::backtraces.size() << std::endl;
}
#endif
#endif // CWDEBUG

256
indra/cwdebug/debug.h Normal file
View File

@@ -0,0 +1,256 @@
// slviewer -- Second Life Viewer Source Code
//
//! @file debug.h
//! @brief This file contains the declaration of debug related macros, objects and functions.
//
// Copyright (C) 2008, 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 program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef DEBUG_H
#define DEBUG_H
#ifndef CWDEBUG
#ifndef DOXYGEN // No need to document this. See http://libcwd.sourceforge.net/ for more info.
#include <iostream>
#include <cstdlib> // std::exit, EXIT_FAILURE
#define AllocTag1(p)
#define AllocTag2(p, desc)
#define AllocTag_dynamic_description(p, x)
#define AllocTag(p, x)
#define Debug(x)
#define Dout(a, b)
#define DoutEntering(a, b)
#define DoutFatal(a, b) LibcwDoutFatal(::std, , a, b)
#define ForAllDebugChannels(STATEMENT)
#define ForAllDebugObjects(STATEMENT)
#define LibcwDebug(dc_namespace, x)
#define LibcwDout(a, b, c, d)
#define LibcwDoutFatal(a, b, c, d) do { ::std::cerr << d << ::std::endl; ::std::exit(EXIT_FAILURE); } while (1)
#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
#define BACKTRACE do { } while(0)
#endif // !DOXYGEN
#include <cassert>
#ifdef DEBUG
#define ASSERT(x) assert(x)
#else
#define ASSERT(x)
#endif
#else // CWDEBUG
//! Assert \a x, if debugging is turned on.
#define ASSERT(x) LIBCWD_ASSERT(x)
#define builtin_return_address(addr) ((char*)__builtin_return_address(addr) + libcwd::builtin_return_address_offset)
#ifndef DEBUGCHANNELS
//! @brief The namespace in which the \c dc namespace is declared.
//
// <A HREF="http://libcwd.sourceforge.net/">Libcwd</A> demands that this macro is defined
// before <libcwd/debug.h> is included and must be the name of the namespace containing
// the \c dc (Debug Channels) namespace.
//
// @sa debug::channels::dc
#define DEBUGCHANNELS ::debug::channels
#endif
#include <libcwd/debug.h>
#include <boost/shared_array.hpp>
#if CWDEBUG_LOCATION
#include <execinfo.h> // Needed for 'backtrace'.
#endif
#define CWD_API __attribute__ ((visibility("default")))
//! Debug specific code.
namespace debug {
void CWD_API init(void); // Initialize debugging code, called once from main.
void CWD_API init_thread(void); // Initialize debugging code, called once for each thread.
//! @brief Debug Channels (dc) namespace.
//
// @sa debug::channels::dc
namespace channels { // namespace DEBUGCHANNELS
//! The namespace containing the actual debug channels.
namespace dc {
using namespace libcwd::channels::dc;
using libcwd::channel_ct;
#ifndef DOXYGEN // Doxygen bug causes a warning here.
// Add the declaration of new debug channels here
// and add their definition in a custom debug.cc file.
extern CWD_API channel_ct viewer; // The normal logging output of the viewer (normally to stderr).
extern CWD_API channel_ct primbackup;
extern CWD_API channel_ct gtk;
extern CWD_API channel_ct sdl;
extern CWD_API channel_ct backtrace;
#endif
} // namespace dc
} // namespace DEBUGCHANNELS
#if CWDEBUG_LOCATION
std::string call_location(void const* return_addr);
#endif
//! @brief Interface for marking scopes of invisible memory allocations.
//
// Creation of the object does nothing, you have to explicitly call
// InvisibleAllocations::on. Destruction of the object automatically
// cancels any call to \c on of this object. This makes it exception-
// (stack unwinding) and recursive-safe.
struct InvisibleAllocations {
int M_on; //!< The number of times that InvisibleAllocations::on() was called.
//! Constructor.
InvisibleAllocations() : M_on(0) { }
//! Destructor.
~InvisibleAllocations() { while (M_on > 0) off(); }
//! Set invisible allocations on. Can be called recursively.
void on(void) { libcwd::set_invisible_on(); ++M_on; }
//! Cancel one call to on().
void off(void) { assert(M_on > 0); --M_on; libcwd::set_invisible_off(); }
};
//! @brief Interface for marking scopes with indented debug output.
//
// Creation of the object increments the debug indentation. Destruction
// of the object automatically decrements the indentation again.
struct Indent {
int M_indent; //!< The extra number of spaces that were added to the indentation.
//! Construct an Indent object.
Indent(int indent) : M_indent(indent) { if (M_indent > 0) libcwd::libcw_do.inc_indent(M_indent); }
//! Destructor.
~Indent() { if (M_indent > 0) libcwd::libcw_do.dec_indent(M_indent); }
};
// A class streambuf that splits what is written to two streambufs.
class TeeBuf : public std::streambuf {
private:
std::streambuf* M_sb1;
std::streambuf* M_sb2;
public:
TeeBuf(std::streambuf* sb1, std::streambuf* sb2) : M_sb1(sb1), M_sb2(sb2) { }
protected:
virtual int sync(void) { M_sb2->pubsync(); return M_sb1->pubsync(); }
virtual std::streamsize xsputn(char_type const* p, std::streamsize n) { M_sb2->sputn(p, n); return M_sb1->sputn(p, n); }
virtual int_type overflow(int_type c = traits_type::eof()) { M_sb2->sputc(c); return M_sb1->sputc(c); }
};
// An ostream that passes what is written to it on to two other ostreams.
class TeeStream : public std::ostream {
private:
TeeBuf M_teebuf;
public:
TeeStream(std::ostream& os1, std::ostream& os2) : std::ostream(&M_teebuf), M_teebuf(os1.rdbuf(), os2.rdbuf()) { }
};
#if CWDEBUG_LOCATION
class BackTrace {
private:
boost::shared_array<void*> M_buffer;
int M_frames;
public:
BackTrace(void** buffer, int frames) : M_buffer(new void* [frames]), M_frames(frames) { std::memcpy(M_buffer.get(), buffer, sizeof(void*) * frames); }
friend bool operator<(BackTrace const& bt1, BackTrace const& bt2)
{
if (bt1.M_frames != bt2.M_frames)
return bt1.M_frames < bt2.M_frames;
for (int frame = 0; frame < bt1.M_frames; ++frame)
if (bt1.M_buffer[frame] < bt2.M_buffer[frame])
return true;
else if (bt1.M_buffer[frame] > bt2.M_buffer[frame])
return true;
return false;
}
void dump_backtrace(void) const;
int frames(void) const { return M_frames; }
boost::shared_array<void*> const& buffer(void) const { return M_buffer; }
};
extern std::vector<BackTrace> backtraces;
extern pthread_mutex_t backtrace_mutex;
#define BACKTRACE do { \
using namespace debug; \
void* buffer[32]; \
int frames = backtrace(buffer, 32); \
size_t size; \
{ \
pthread_mutex_lock(&backtrace_mutex); \
backtraces.push_back(BackTrace(buffer, frames)); \
size = backtraces.size(); \
pthread_mutex_unlock(&backtrace_mutex); \
} \
Dout(dc::backtrace, "Stored backtrace #" << size); \
} while(0)
#else
#define BACKTRACE do { } while(0)
#endif // CWDEBUG_LOCATION
} // namespace debug
//! Debugging macro.
//
// Print "Entering " << \a data to channel \a cntrl and increment
// debugging output indentation until the end of the current scope.
#define DoutEntering(cntrl, data) \
int __slviewer_debug_indentation = 2; \
{ \
LIBCWD_TSD_DECLARATION; \
if (LIBCWD_DO_TSD_MEMBER_OFF(::libcwd::libcw_do) < 0) \
{ \
::libcwd::channel_set_bootstrap_st __libcwd_channel_set(LIBCWD_DO_TSD(::libcwd::libcw_do) LIBCWD_COMMA_TSD); \
bool on; \
{ \
using namespace LIBCWD_DEBUGCHANNELS; \
on = (__libcwd_channel_set|cntrl).on; \
} \
if (on) \
Dout(cntrl, "Entering " << data); \
else \
__slviewer_debug_indentation = 0; \
} \
} \
debug::Indent __slviewer_debug_indent(__slviewer_debug_indentation);
#endif // CWDEBUG
#include "debug_ostream_operators.h"
#endif // DEBUG_H

View File

@@ -0,0 +1 @@
// Empty so far...

9
indra/cwdebug/sys.h Normal file
View File

@@ -0,0 +1,9 @@
// 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

View File

@@ -2,6 +2,7 @@
project(llcommon)
include(Cwdebug)
include(00-Common)
include(LLCommon)
include(Linking)
@@ -214,5 +215,11 @@ target_link_libraries(
${EXPAT_LIBRARIES}
${ZLIB_LIBRARIES}
${WINDOWS_LIBRARIES}
${CWDEBUG_LIBRARIES}
)
if (LINUX)
# When linking with llcommon later, we do not want to link with cwdebug.a again.
set_property(TARGET llcommon PROPERTY LINK_INTERFACE_LIBRARIES "-lapr-1 -laprutil-1 -lz")
endif (LINUX)

View File

@@ -33,6 +33,8 @@
#ifndef LL_LINDEN_COMMON_H
#define LL_LINDEN_COMMON_H
#include "cwdebug.h"
#if defined(LL_WINDOWS) && defined(_DEBUG)
# if _MSC_VER >= 1400 // Visual C++ 2005 or later
# define _CRTDBG_MAP_ALLOC

View File

@@ -195,4 +195,9 @@ private:
static const char* mAssetTypeHumanNames[];
};
#ifdef CWDEBUG
#include <iosfwd>
inline std::ostream& operator<<(std::ostream& os, LLAssetType::EType type) { return os << LLAssetType::getDesc(type); }
#endif
#endif // LL_LLASSETTYPE

View File

@@ -162,7 +162,15 @@ namespace {
break;
}
}
#ifdef CWDEBUG
// Include normal logging in libcwd's message processing.
// This takes care of prefixing with thread ID's, locking
// and allows us to (temporarily) turn off normal logging
// output.
Dout(dc::viewer, message);
#else
fprintf(stderr, "%s\n", message.c_str());
#endif
#if LL_WINDOWS
fflush(stderr); //Now using a buffer. flush is required.
#endif
@@ -1218,6 +1226,8 @@ namespace LLError
#endif
void crashAndLoop(const std::string& message)
{
DoutFatal(dc::core, message);
// Now, we go kaboom!
int* make_me_crash = NULL;

View File

@@ -33,8 +33,8 @@
#ifndef LL_LLINDRACONFIGFILE_H
#define LL_LLINDRACONFIGFILE_H
#include <string>
#include "linden_common.h"
#include <string>
#include "lllivefile.h"
#include "llsd.h"

View File

@@ -67,6 +67,10 @@
//
void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap)
{
#ifdef CWDEBUG
debug::init_thread();
#endif
LLThread *threadp = (LLThread *)datap;
// Set thread state to running

View File

@@ -29,13 +29,15 @@
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#include "linden_common.h"
#include <cstdio>
#include <cstdlib>
#include <sstream>
#include <map>
#include "llcrashlogger.h"
#include "linden_common.h"
#include "llstring.h"
#include "indra_constants.h" // CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME
#include "llerror.h"

View File

@@ -32,10 +32,10 @@
#ifndef LLCRASHLOGGER_H
#define LLCRASHLOGGER_H
#include <vector>
#include "linden_common.h"
#include <vector>
#include "llapp.h"
#include "llsd.h"
#include "llcontrol.h"

View File

@@ -32,6 +32,7 @@
*/
// Precompiled header: almost always required for newview cpp files
#include "../llcommon/linden_common.h"
#include <list>
#include <map>
#include <algorithm>

View File

@@ -30,8 +30,8 @@
* $/LicenseInfo$
*/
#include <iostream>
#include "linden_common.h"
#include <iostream>
#include "llsaleinfo.h"

View File

@@ -1,6 +1,6 @@
// <edit>
#include "llnetcanary.h"
#include "linden_common.h"
#include "llnetcanary.h"
#include "llerror.h"
#ifdef _MSC_VER
#include <winsock2.h>

View File

@@ -30,13 +30,12 @@
* $/LicenseInfo$
*/
#include <string>
#include "linden_common.h"
#include "net.h"
#include "llhost.h"
#include "message.h"
#include "llsocks5.h"
#include <string>
// Static class variable instances

View File

@@ -183,6 +183,13 @@ int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdL
int main(int argc, char **argv)
#endif
{
#ifdef CWDEBUG
Debug( libcw_do.margin().assign("SLPlugin ", 9) );
Debug(debug::init());
// Uncomment this to automatically open a terminal with gdb. Requires SNOW-173.
//Debug(attach_gdb());
#endif
// Set up llerror logging
{
LLError::initForApplication(".");

View File

@@ -98,6 +98,9 @@ static void exceptionTerminateHandler()
int main( int argc, char **argv )
{
Debug(debug::init());
Debug(libcw_do.on());
LLMemType mt1(LLMemType::MTYPE_STARTUP);
#if LL_SOLARIS && defined(__sparc)