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:
@@ -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)
|
||||
|
||||
1
indra/cmake/Cwdebug.cmake
Normal file
1
indra/cmake/Cwdebug.cmake
Normal file
@@ -0,0 +1 @@
|
||||
set(CWDEBUG_LIBRARIES cwdebug)
|
||||
@@ -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")
|
||||
|
||||
@@ -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}
|
||||
|
||||
33
indra/cwdebug/CMakeLists.txt
Normal file
33
indra/cwdebug/CMakeLists.txt
Normal 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
9
indra/cwdebug/cwdebug.h
Normal 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
412
indra/cwdebug/debug.cc
Normal 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
256
indra/cwdebug/debug.h
Normal 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
|
||||
1
indra/cwdebug/debug_ostream_operators.h
Normal file
1
indra/cwdebug/debug_ostream_operators.h
Normal file
@@ -0,0 +1 @@
|
||||
// Empty so far...
|
||||
9
indra/cwdebug/sys.h
Normal file
9
indra/cwdebug/sys.h
Normal 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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
*/
|
||||
|
||||
// Precompiled header: almost always required for newview cpp files
|
||||
#include "../llcommon/linden_common.h"
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include "linden_common.h"
|
||||
#include <iostream>
|
||||
|
||||
#include "llsaleinfo.h"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// <edit>
|
||||
#include "llnetcanary.h"
|
||||
#include "linden_common.h"
|
||||
#include "llnetcanary.h"
|
||||
#include "llerror.h"
|
||||
#ifdef _MSC_VER
|
||||
#include <winsock2.h>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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(".");
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user