diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index e81510b2c..f88aa1fa0 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -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) diff --git a/indra/cmake/Cwdebug.cmake b/indra/cmake/Cwdebug.cmake new file mode 100644 index 000000000..9568a625f --- /dev/null +++ b/indra/cmake/Cwdebug.cmake @@ -0,0 +1 @@ +set(CWDEBUG_LIBRARIES cwdebug) diff --git a/indra/cmake/LLAddBuildTest.cmake b/indra/cmake/LLAddBuildTest.cmake index f9b1ef96f..74f2e0cb1 100644 --- a/indra/cmake/LLAddBuildTest.cmake +++ b/indra/cmake/LLAddBuildTest.cmake @@ -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") diff --git a/indra/cmake/LLCommon.cmake b/indra/cmake/LLCommon.cmake index 2862ba7f0..efb3d004c 100644 --- a/indra/cmake/LLCommon.cmake +++ b/indra/cmake/LLCommon.cmake @@ -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} diff --git a/indra/cwdebug/CMakeLists.txt b/indra/cwdebug/CMakeLists.txt new file mode 100644 index 000000000..b291957e7 --- /dev/null +++ b/indra/cwdebug/CMakeLists.txt @@ -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}) diff --git a/indra/cwdebug/cwdebug.h b/indra/cwdebug/cwdebug.h new file mode 100644 index 000000000..37415df3f --- /dev/null +++ b/indra/cwdebug/cwdebug.h @@ -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" diff --git a/indra/cwdebug/debug.cc b/indra/cwdebug/debug.cc new file mode 100644 index 000000000..3066c2845 --- /dev/null +++ b/indra/cwdebug/debug.cc @@ -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 +// 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 . + +#ifndef USE_PCH +#include "sys.h" // Needed for platform-specific code +#endif + +#ifdef CWDEBUG + +#ifndef USE_PCH +#include // Needed for std::isprint +#include // Needed for setfill and setw +#include +#include +#include +#include +#include "debug.h" +#ifdef USE_LIBCW +#include // memleak_filter +#endif +#include +#include +#endif // USE_PCH + +#define BACKTRACE_AQUIRE_LOCK libcwd::_private_::mutex_tct::lock() +#define BACKTRACE_RELEASE_LOCK libcwd::_private_::mutex_tct::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, 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 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 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::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 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 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 (channel_ct::get_label) of an + * existing debug channel, this function returns \c true when the corresponding debug channel was + * on 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 > hide_list; + hide_list.push_back(std::pair("libdl.so.2", "_dlerror_run")); + hide_list.push_back(std::pair("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("libc.so.6", "dl_open_worker")); + memleak_filter().hide_functions_matching(hide_list); + } + { + std::vector 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 __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 diff --git a/indra/cwdebug/debug.h b/indra/cwdebug/debug.h new file mode 100644 index 000000000..66408ad72 --- /dev/null +++ b/indra/cwdebug/debug.h @@ -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 +// 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 . + +#ifndef DEBUG_H +#define DEBUG_H + +#ifndef CWDEBUG + +#ifndef DOXYGEN // No need to document this. See http://libcwd.sourceforge.net/ for more info. + +#include +#include // 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 +#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. +// +// Libcwd demands that this macro is defined +// before 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 +#include +#if CWDEBUG_LOCATION +#include // 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 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 const& buffer(void) const { return M_buffer; } +}; +extern std::vector 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 diff --git a/indra/cwdebug/debug_ostream_operators.h b/indra/cwdebug/debug_ostream_operators.h new file mode 100644 index 000000000..4459e9fab --- /dev/null +++ b/indra/cwdebug/debug_ostream_operators.h @@ -0,0 +1 @@ +// Empty so far... diff --git a/indra/cwdebug/sys.h b/indra/cwdebug/sys.h new file mode 100644 index 000000000..c18df1701 --- /dev/null +++ b/indra/cwdebug/sys.h @@ -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 +#endif + diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 7f352269b..f1325163c 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -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) + diff --git a/indra/llcommon/linden_common.h b/indra/llcommon/linden_common.h index 6e66e9916..d486359c7 100644 --- a/indra/llcommon/linden_common.h +++ b/indra/llcommon/linden_common.h @@ -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 diff --git a/indra/llcommon/llassettype.h b/indra/llcommon/llassettype.h index a769e165c..9912ecba2 100644 --- a/indra/llcommon/llassettype.h +++ b/indra/llcommon/llassettype.h @@ -195,4 +195,9 @@ private: static const char* mAssetTypeHumanNames[]; }; +#ifdef CWDEBUG +#include +inline std::ostream& operator<<(std::ostream& os, LLAssetType::EType type) { return os << LLAssetType::getDesc(type); } +#endif + #endif // LL_LLASSETTYPE diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index 4cdfe097d..a9c4380a1 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -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; diff --git a/indra/llcommon/llindraconfigfile.h b/indra/llcommon/llindraconfigfile.h index 661f79bd6..17eda906e 100644 --- a/indra/llcommon/llindraconfigfile.h +++ b/indra/llcommon/llindraconfigfile.h @@ -33,8 +33,8 @@ #ifndef LL_LLINDRACONFIGFILE_H #define LL_LLINDRACONFIGFILE_H -#include #include "linden_common.h" +#include #include "lllivefile.h" #include "llsd.h" diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp index 7ced30939..8bd341457 100644 --- a/indra/llcommon/llthread.cpp +++ b/indra/llcommon/llthread.cpp @@ -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 diff --git a/indra/llcrashlogger/llcrashlogger.cpp b/indra/llcrashlogger/llcrashlogger.cpp index 3dc41e897..f955ccdfd 100644 --- a/indra/llcrashlogger/llcrashlogger.cpp +++ b/indra/llcrashlogger/llcrashlogger.cpp @@ -29,13 +29,15 @@ * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ + +#include "linden_common.h" + #include #include #include #include #include "llcrashlogger.h" -#include "linden_common.h" #include "llstring.h" #include "indra_constants.h" // CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME #include "llerror.h" diff --git a/indra/llcrashlogger/llcrashlogger.h b/indra/llcrashlogger/llcrashlogger.h index f8abe2059..72c1f3fb8 100644 --- a/indra/llcrashlogger/llcrashlogger.h +++ b/indra/llcrashlogger/llcrashlogger.h @@ -32,10 +32,10 @@ #ifndef LLCRASHLOGGER_H #define LLCRASHLOGGER_H -#include - #include "linden_common.h" +#include + #include "llapp.h" #include "llsd.h" #include "llcontrol.h" diff --git a/indra/llimage/tests/llimageworker_test.cpp b/indra/llimage/tests/llimageworker_test.cpp index cc44696a4..2bca81434 100644 --- a/indra/llimage/tests/llimageworker_test.cpp +++ b/indra/llimage/tests/llimageworker_test.cpp @@ -32,6 +32,7 @@ */ // Precompiled header: almost always required for newview cpp files +#include "../llcommon/linden_common.h" #include #include #include diff --git a/indra/llinventory/llsaleinfo.cpp b/indra/llinventory/llsaleinfo.cpp index b7afb28ad..e51e35e6e 100644 --- a/indra/llinventory/llsaleinfo.cpp +++ b/indra/llinventory/llsaleinfo.cpp @@ -30,8 +30,8 @@ * $/LicenseInfo$ */ -#include #include "linden_common.h" +#include #include "llsaleinfo.h" diff --git a/indra/llmessage/llnetcanary.cpp b/indra/llmessage/llnetcanary.cpp index a714c60f2..57ee6d3a6 100644 --- a/indra/llmessage/llnetcanary.cpp +++ b/indra/llmessage/llnetcanary.cpp @@ -1,6 +1,6 @@ // -#include "llnetcanary.h" #include "linden_common.h" +#include "llnetcanary.h" #include "llerror.h" #ifdef _MSC_VER #include diff --git a/indra/llmessage/llsocks5.cpp b/indra/llmessage/llsocks5.cpp index 7326e806b..d21412f86 100644 --- a/indra/llmessage/llsocks5.cpp +++ b/indra/llmessage/llsocks5.cpp @@ -30,13 +30,12 @@ * $/LicenseInfo$ */ -#include - #include "linden_common.h" #include "net.h" #include "llhost.h" #include "message.h" #include "llsocks5.h" +#include // Static class variable instances diff --git a/indra/llplugin/slplugin/slplugin.cpp b/indra/llplugin/slplugin/slplugin.cpp index cca8ead8f..febcfea84 100644 --- a/indra/llplugin/slplugin/slplugin.cpp +++ b/indra/llplugin/slplugin/slplugin.cpp @@ -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("."); diff --git a/indra/newview/llappviewerlinux.cpp b/indra/newview/llappviewerlinux.cpp index a19ad5658..4e4bbcb43 100644 --- a/indra/newview/llappviewerlinux.cpp +++ b/indra/newview/llappviewerlinux.cpp @@ -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)