// 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 #ifdef DEBUG_CURLIO #if LL_WINDOWS #define CWD_DLLEXPORT __declspec(dllexport) #define CWD_DLLIMPORT __declspec(dllimport) #elif LL_LINUX #define CWD_DLLEXPORT __attribute__ ((visibility("default"))) #define CWD_DLLIMPORT #else #define CWD_DLLEXPORT #define CWD_DLLIMPORT #endif // LL_WINDOWS #if LL_COMMON_LINK_SHARED #if defined(llcommon_EXPORTS) #define CWD_API CWD_DLLEXPORT #else // cwdebug_EXPORTS #define CWD_API CWD_DLLIMPORT #endif // cwdebug_EXPORTS #else // LL_COMMON_LINK_SHARED #error LL_COMMON_LINK_SHARED not defined #endif // LL_COMMON_LINK_SHARED #include "aithreadid.h" // If CWDEBUG is not defined, but DEBUG_CURLIO is, then replace // some of the cwd macro's with something that generates viewer // specific debug output. Note that this generates a LOT of // output and should not normally be defined. #include #if LL_WINDOWS #define CWD_API_TLS __declspec(thread) #define CWD_TLS __declspec(thread) #else #define CWD_API_TLS CWD_API __thread #define CWD_TLS __thread #endif namespace debug { namespace libcwd { struct buf2str { buf2str(char const* buf, int size) : mBuf(buf), mSize(size) { } char const* mBuf; int mSize; }; } // namespace libcwd inline void init() { } struct libcwd_do_type { void on() const { } }; extern CWD_API libcwd_do_type const libcw_do; struct Indent { int M_indent; static CWD_API_TLS int S_indentation; enum CWD_API print_nt { print }; CWD_API Indent(int indent) : M_indent(indent) { S_indentation += M_indent; } CWD_API ~Indent() { S_indentation -= M_indent; } }; extern CWD_API std::ostream& operator<<(std::ostream& os, libcwd::buf2str const& b2s); extern CWD_API std::ostream& operator<<(std::ostream& os, Indent::print_nt); namespace dc { struct fake_channel { int mOn; char const* mLabel; fake_channel(int on, char const* label) : mOn(on), mLabel(label) { } fake_channel(void) : mOn(0) { } bool is_on() const { return !!mOn; } bool is_off() const { return !mOn; } void on() const { } void off() const { } }; extern LL_COMMON_API fake_channel const warning; extern LL_COMMON_API fake_channel const curl; extern LL_COMMON_API fake_channel const curlio; extern LL_COMMON_API fake_channel const statemachine; extern LL_COMMON_API fake_channel const notice; } // namespace dc } // namespace debug #define Debug(x) do { using namespace debug; x; } while(0) #define Dout(a, b) do { using namespace debug; if ((a).mOn) { llinfos_nf << AIThreadID::DoutPrint << (a).mLabel << ": " << Indent::print << b << llendl; } } while(0) #define DoutEntering(a, b) \ int __slviewer_debug_indentation = 2; \ { \ using namespace debug; \ if ((a).mOn) \ llinfos_nf << AIThreadID::DoutPrint << (a).mLabel << ": " << Indent::print << "Entering " << b << llendl; \ else \ __slviewer_debug_indentation = 0; \ } \ debug::Indent __slviewer_debug_indent(__slviewer_debug_indentation); #else // !DEBUG_CURLIO #define Debug(x) #define Dout(a, b) #define DoutEntering(a, b) #endif // !DEBUG_CURLIO #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 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; extern CWD_API channel_ct statemachine; extern CWD_API channel_ct caps; extern CWD_API channel_ct curl; extern CWD_API channel_ct curlio; #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