diff options
author | dai_y <dai_y@ae88bc3d-4319-0410-8dbf-d08b4c9d3795> | 2008-06-05 20:35:57 +0000 |
---|---|---|
committer | dai_y <dai_y@ae88bc3d-4319-0410-8dbf-d08b4c9d3795> | 2008-06-05 20:35:57 +0000 |
commit | b3c3f344deb77cac6aed23c0250919a77236a922 (patch) | |
tree | d1b56f0db66da5d12e58b8342734f86d766d0f0b | |
parent | 0bb5a2d6ce66991d03629fbf348b01210e333ac9 (diff) | |
download | ATCD-b3c3f344deb77cac6aed23c0250919a77236a922.tar.gz |
Thu Jun 5 20:32:00 UTC 2008 Yan Dai <dai_y@ociweb.com>
-rw-r--r-- | ACE/ChangeLog | 39 | ||||
-rw-r--r-- | ACE/NEWS | 5 | ||||
-rw-r--r-- | ACE/ace/Log_Msg.cpp | 17 | ||||
-rw-r--r-- | ACE/ace/Stack_Trace.cpp | 668 | ||||
-rw-r--r-- | ACE/ace/Stack_Trace.h | 72 | ||||
-rw-r--r-- | ACE/ace/ace.mpc | 1 | ||||
-rw-r--r-- | ACE/tests/Stack_Trace_Test.cpp | 86 | ||||
-rw-r--r-- | ACE/tests/run_test.lst | 1 | ||||
-rw-r--r-- | ACE/tests/tests.mpc | 7 |
9 files changed, 890 insertions, 6 deletions
diff --git a/ACE/ChangeLog b/ACE/ChangeLog index 66b8b766d4b..566c1414ad7 100644 --- a/ACE/ChangeLog +++ b/ACE/ChangeLog @@ -1,3 +1,30 @@ +Thu Jun 5 20:32:00 UTC 2008 Yan Dai <dai_y@ociweb.com> + + * ace/Stack_Trace.h: + * ace/Stack_Trace.cpp: + * tests/Stack_Trace_Test.cpp: + + Added new class that, on supported platforms, will take a + snapshot of the current stack when instantiated. + + * ace/Log_Msg.cpp (log): + + Added new conversion character, the question mark, which gets + replaced by a stack trace on supported platforms. + + * ace/ace.mpc: + + Added Stack_Trace in as source. + + * tests/tests.mpc: + * tests/run_test.lst: + + Added Stack_Trace_Test in. + + * NEWS: + + Add an entry for stack trace support. + Wed Jun 4 22:45:20 2008 Steve Huston <shuston@riverace.com> * ace/config-pharlap.h: Added ACE_LACKS_FILELLOCKS. My changes from @@ -50,9 +77,9 @@ Wed Jun 4 14:36:55 UTC 2008 Jeff Parsons <j.parsons@vanderbilt.edu> Tue Jun 3 16:14:39 UTC 2008 Ken Sedgwick <ken+5a4@bonsai.com> - * rpmbuild/ace-tao.spec: - * rpmbuild/ace-tao-unusedarg.patch: - * rpmbuild/ace-tao-strrecvfd.patch: + * rpmbuild/ace-tao.spec: + * rpmbuild/ace-tao-unusedarg.patch: + * rpmbuild/ace-tao-strrecvfd.patch: Added ace-tao-strrecvfd.patch (related to bug #3291). Changed make loop construct to abort when subcomponent fails. Removed PSS from TAO build list. @@ -67,9 +94,9 @@ Mon Jun 2 15:26:57 UTC 2008 Douglas C. Schmidt <schmidt@dre.vanderbilt.edu> Sun Jun 1 02:01:38 UTC 2008 Ken Sedgwick <ken+5a4@bonsai.com> - * rpmbuild/ace-tao.spec: - * rpmbuild/ace-tao-etc.tar.gz: - * rpmbuild/ace-tao-orbsvcs-daemon.patch: + * rpmbuild/ace-tao.spec: + * rpmbuild/ace-tao-etc.tar.gz: + * rpmbuild/ace-tao-orbsvcs-daemon.patch: Added ace-tao-orbsvcs-daemon.patch to workaround daemonization problems in rpm installed services. Fixed tao-cosconcurrency command line arguments. @@ -4,6 +4,11 @@ PLANNED MAJOR CHANGES "SOMETIME IN THE FUTURE" (i.e., exact beta not known) USER VISIBLE CHANGES BETWEEN ACE-5.6.5 and ACE-5.6.6 ==================================================== +. Added ACE_Stack_Trace class to allow users to obtain a stack trace + within their application on supported platforms. A new conversion + character, the question mark, was added to ACE_Log_Msg for stack + trace logging. + USER VISIBLE CHANGES BETWEEN ACE-5.6.4 and ACE-5.6.5 ==================================================== diff --git a/ACE/ace/Log_Msg.cpp b/ACE/ace/Log_Msg.cpp index 692b09af050..6acb1146c02 100644 --- a/ACE/ace/Log_Msg.cpp +++ b/ACE/ace/Log_Msg.cpp @@ -38,6 +38,7 @@ #include "ace/Log_Msg_UNIX_Syslog.h" #include "ace/Log_Record.h" #include "ace/Recursive_Thread_Mutex.h" +#include "ace/Stack_Trace.h" #if !defined (__ACE_INLINE__) #include "ace/Log_Msg.inl" @@ -1990,6 +1991,22 @@ ACE_Log_Msg::log (const ACE_TCHAR *format_str, ACE_UPDATE_COUNT (bspace, this_len); break; + case '?': + // Stack trace up to this point + { + ACE_Stack_Trace t(2); // skip the frame that we're currently in + ACE_OS::strcpy (fp, ACE_LIB_TEXT ("s")); + if (can_check) + this_len = ACE_OS::snprintf + (bp, bspace, format, t.c_str ()); + else + this_len = ACE_OS::sprintf + (bp, format, t.c_str ()); + ACE_UPDATE_COUNT (bspace, this_len); + break; + } + + default: // So, it's not a legit format specifier after all... // Copy from the original % to where we are now, then diff --git a/ACE/ace/Stack_Trace.cpp b/ACE/ace/Stack_Trace.cpp new file mode 100644 index 00000000000..3473b2b6e3f --- /dev/null +++ b/ACE/ace/Stack_Trace.cpp @@ -0,0 +1,668 @@ +//============================================================================= +/** + * @file Stack_Trace.cpp + * + * $Id$ + * + * @brief Encapsulate string representation of stack trace. + * + * Portions of the platform-specific code have been based on + * code found in various places on the internet e.g., google groups, + * VxWorks FAQ, etc., and adapted for use here. + */ +//============================================================================= + +#include "ace/Stack_Trace.h" +#include "ace/Min_Max.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_stdio.h" + +ACE_RCSID (ace, Stack_Trace, "$Id$") + +/* + This is ugly, simply because it's very platform-specific. +*/ + +const char ACE_Stack_Trace::UNSUPPORTED[] = "<stack traces unsupported platform>"; +const char ACE_Stack_Trace::UNABLE_TO_GET_TRACE[] = "<unable to get trace>"; + +ACE_Stack_Trace::ACE_Stack_Trace (ssize_t starting_frame_offset, size_t num_frames) + : buflen_(0) +{ + // cannot initialize arrays, so we must assign. + this->buf_[0] = '\0'; + this->generate_trace (starting_frame_offset, num_frames); +} + +const char* +ACE_Stack_Trace::c_str () const +{ + return &this->buf_[0]; +} + +static inline size_t +determine_starting_frame (ssize_t initial_frame, ssize_t offset) +{ + return ACE_MAX( initial_frame + offset, static_cast<ssize_t>(0)); +} + + +#if defined(__GLIBC__) || defined(ACE_HAS_EXECINFO_H) +// This is the code for glibc +# include <execinfo.h> + +void +ACE_Stack_Trace::generate_trace (ssize_t starting_frame_offset, size_t num_frames) +{ + const size_t MAX_FRAMES = 128; + const ssize_t INITIAL_FRAME = 3; + + void* stack[MAX_FRAMES]; + size_t stack_size = 0; + char** stack_syms; + + if (num_frames == 0) + num_frames = MAX_FRAMES; + + size_t starting_frame = + determine_starting_frame (INITIAL_FRAME, starting_frame_offset); + + stack_size = ::backtrace (&stack[0], sizeof(stack)/sizeof(stack[0])); + if (stack_size != 0) + { + stack_syms = ::backtrace_symbols (stack, stack_size); + + for (size_t i = starting_frame; + i < stack_size && num_frames > 0; + i++, num_frames--) + { + // this could be more efficient by remembering where we left off in buf_ + char *symp = &stack_syms[i][0]; + while (this->buflen_ < SYMBUFSIZ + && *symp != '\0') + { + this->buf_[this->buflen_++] = *symp++; + } + this->buf_[this->buflen_++] = '\n'; // put a newline at the end + } + this->buf_[this->buflen_+1] = '\0'; // zero terminate the string + + ::free (stack_syms); + } + else + { + ACE_OS::strcpy (&this->buf_[0], UNABLE_TO_GET_TRACE); + } +} +#elif defined(VXWORKS) && !defined(__RTP__) +# include <trcLib.h> +# include <taskLib.h> // hopefully this is enough to get all the necessary #defines. + +struct ACE_Stack_Trace_stackstate +{ + ACE_Stack_Trace_stackstate (char* b, size_t& bl, size_t nf, size_t sf) + : buf(b), buflen(bl), num_frames(nf), starting_frame(sf) + { } + + char* buf; + size_t& buflen; + size_t num_frames; + size_t starting_frame; +}; + +//@TODO: Replace with a TSS-based pointer to avoid problems in multithreaded environs, +// or use a mutex to serialize access to this. +static ACE_Stack_Trace_stackstate* ACE_Stack_Trace_stateptr = 0; + +static void +ACE_Stack_Trace_Add_Frame_To_Buf (INSTR *caller, + unsigned int func, + unsigned int nargs, + unsigned int *args) +{ + if (ACE_Stack_Trace_stateptr == 0) + return; + + ACE_Stack_Trace_stackstate *stackstate = ACE_Stack_Trace_stateptr; + + // Decrement the num_frames and starting_frame elements, + // then see if we're ready to start or ready to finish. + --stackstate->num_frames; + --stackstate->starting_frame; + + if (stackstate->num_frames == 0 || stackstate->starting_frame > 0) + return; + + // These are references so that the structure gets updated + // in the code below. + char*& buf = stackstate->buf; + unsigned int& len = stackstate->buflen; + + // At some point try using symFindByValue() to lookup func (and caller?) + // to print out symbols rather than simply addresses. + + len += ACE_OS::sprintf (&buf[len], "%#10x: %#10x (", (int)caller, func); + for (unsigned int i = 0; i < nargs; ++i) + { + if (i != 0) + len += ACE_OS::sprintf (&buf[len], ", "); + len += ACE_OS::sprintf(&buf [len], "%#x", args [i]); + } + + len += ACE_OS::sprintf(&buf[len], ")\n"); +} + +void +ACE_Stack_Trace::generate_trace (ssize_t starting_frame_offset, + size_t num_frames) +{ + const size_t MAX_FRAMES = 128; + const ssize_t INITIAL_FRAME = 3; + + if (num_frames == 0) + num_frames = MAX_FRAMES; + + size_t starting_frame = + determine_starting_frame (INITIAL_FRAME, starting_frame_offset); + + ACE_Stack_Trace_stackstate state (&this->buf_[0], this->buflen_, + num_frames, starting_frame); + + REG_SET regs; + + taskRegsGet ((int)taskIdSelf(), ®s); + // Maybe we should take a lock here to guard stateptr? + ACE_Stack_Trace_stateptr = &state; + trcStack (®s, (FUNCPTR)ACE_Stack_Trace_Add_Frame_To_Buf, taskIdSelf ()); +} + + +#elif defined(VXWORKS) && defined(__RTP__) +# include <setjmp.h> +# include <taskLib.h> +# include <private/trcLibP.h> + +// See memEdrLib.c in VxWorks RTP sources for an example of stack tracing. + +static STATUS ace_vx_rtp_pc_validate (INSTR *pc, TRC_OS_CTX *pOsCtx) +{ + return ALIGNED (pc, sizeof (INSTR)) ? OK : ERROR; +} + +void +ACE_Stack_Trace::generate_trace (ssize_t starting_frame_offset, + size_t num_frames) +{ + const size_t MAX_FRAMES = 128; + const ssize_t INITIAL_FRAME = 2; + + if (num_frames == 0) num_frames = MAX_FRAMES; + size_t starting_frame = + determine_starting_frame (INITIAL_FRAME, starting_frame_offset); + + jmp_buf regs; + setjmp (regs); + + TASK_DESC desc; + if (taskInfoGet (taskIdSelf (), &desc) == ERROR) return; + + TRC_OS_CTX osCtx; + osCtx.stackBase = desc.td_pStackBase; + osCtx.stackEnd = desc.td_pStackEnd; + osCtx.pcValidateRtn = reinterpret_cast<FUNCPTR> (ace_vx_rtp_pc_validate); + + char *fp = _WRS_FRAMEP_FROM_JMP_BUF (regs); + INSTR *pc = _WRS_RET_PC_FROM_JMP_BUF (regs); + + for (size_t depth = 0; depth < num_frames + starting_frame; ++depth) + { + char *prevFp; + INSTR *prevPc; + INSTR *prevFn; + + if (trcLibFuncs.lvlInfoGet (fp, pc, &osCtx, &prevFp, &prevPc, &prevFn) + == ERROR) + { + ACE_OS::strcpy (this->buf_, UNABLE_TO_GET_TRACE); + return; + } + + if(prevPc == 0 || prevFp == 0) break; + + if (depth >= starting_frame) + { + //Hopefully a future version of VxWorks will have a system call + //for an RTP to query its own symbols, but this is not possible now. + //An enhancement request has been filed under WIND00123307. + const char *fnName = "(no symbols)"; + + static const int N_ARGS = 12; + int buf[N_ARGS]; + int *pArgs = 0; + int numArgs = + trcLibFuncs.lvlArgsGet (prevPc, prevFn, prevFp, + buf, N_ARGS, &pArgs); + + size_t len = ACE_OS::strlen (this->buf_); + size_t space = SYMBUFSIZ - len - 1; + char *cursor = this->buf_ + len; + size_t written = ACE_OS::snprintf (cursor, space, "%x %s", + prevFn, fnName); + cursor += written; + space -= written; + + if (space < 1) return; //no point in logging when we're out of buffer + for (int arg = 0; numArgs != -1 && pArgs && arg < numArgs; ++arg) + { + if (arg == 0) *cursor++ = '(', --space; + written = ACE_OS::snprintf (cursor, space, + (arg < numArgs - 1) ? "%x, " : "%x", + pArgs[arg]); + cursor += written; + space -= written; + if (space && arg == numArgs - 1) *cursor++ = ')', --space; + } + if (space) *cursor++ = '\n', --space; + *cursor++ = 0; //we saved space for the null terminator + } + + fp = prevFp; + pc = prevPc; + } +} + +#elif defined(sun) +/* + * walks up call stack, printing library:routine+offset for each routine + */ + +# include <dlfcn.h> +# include <setjmp.h> +# include <sys/types.h> +# include <sys/reg.h> +# include <sys/frame.h> + +# if defined(sparc) || defined(__sparc) +# define ACE_STACK_TRACE_FLUSHWIN() asm("ta 3"); +# define ACE_STACK_TRACE_FRAME_PTR_INDEX 1 +# define ACE_STACK_TRACE_SKIP_FRAMES 0 +# endif + +# if defined(i386) || defined(__i386) +# define ACE_STACK_TRACE_FLUSHWIN() +# define ACE_STACK_TRACE_FRAME_PTR_INDEX 3 +# define ACE_STACK_TRACE_SKIP_FRAMES 0 +# endif + +# if defined(__amd64) || defined(__x86_64) +# define ACE_STACK_TRACE_FLUSHWIN() +# define ACE_STACK_TRACE_FRAME_PTR_INDEX 5 +# define ACE_STACK_TRACE_SKIP_FRAMES 0 +# endif + +# if defined(ppc) || defined(__ppc) +# define ACE_STACK_TRACE_FLUSHWIN() +# define ACE_STACK_TRACE_FRAME_PTR_INDEX 0 +# define ACE_STACK_TRACE_SKIP_FRAMES 2 +# endif + +/* + this function walks up call stack, calling user-supplied + function once for each stack frame, passing the pc and the user-supplied + usrarg as the argument. + */ + +static int +cs_operate(int (*func)(void *, void *), void * usrarg, + size_t starting_frame, size_t num_frames_arg) +{ + ACE_STACK_TRACE_FLUSHWIN(); + + jmp_buf env; + setjmp(env); + frame* sp = (frame*)env[ACE_STACK_TRACE_FRAME_PTR_INDEX]; + + // make a copy of num_frames_arg to eliminate the following warning on some + // solaris platforms: + // Stack_Trace.cpp:318: warning: argument `size_t num_frames' might be clobbered by `longjmp' or `vfork' + size_t num_frames = num_frames_arg; + + // I would like to use ACE_MAX below rather than ?:, but + // I get linker relocation errors such as the following when + // I use it: + // ld: fatal: relocation error: file: .shobj/Stack_Trace.o section: + // .rela.debug_line symbol: : relocation against a discarded symbol, + // symbol is part of discarded section: + // .text%const __type_0&ace_max<unsig\ned>(const __type_0&,const __type_0&) + // + const size_t starting_skip = starting_frame - 1; +#if ACE_STACK_TRACE_SKIP_FRAMES == 0 + size_t skip_frames = starting_skip; +#else + size_t skip_frames = + ACE_STACK_TRACE_SKIP_FRAMES > starting_skip ? + ACE_STACK_TRACE_SKIP_FRAMES : starting_skip; +#endif /* ACE_STACK_TRACE_SKIP_FRAMES == 0 */ + size_t i; + for (i = 0; i < skip_frames && sp; ++i) + { + sp = (frame*)sp->fr_savfp; + } + + i = 0; + + while ( sp + && sp->fr_savpc + && ++i + && --num_frames + && (*func)((void*)sp->fr_savpc, usrarg)) + { + sp = (frame*)sp->fr_savfp; + } + + return(i); +} + +static int +add_frame_to_buf (void* pc, void* usrarg) +{ + char* buf = (char*)usrarg; + Dl_info info; + const char* func = "??"; + const char* lib = "??"; + + if(dladdr(pc, & info) != 0) + { + lib = (const char *) info.dli_fname; + func = (const char *) info.dli_sname; + } + + (void) ACE_OS::snprintf(buf, + ACE_Stack_Trace::SYMBUFSIZ, + "%s%s:%s+0x%x\n", + buf, + lib, + func, + //@@ Should the arithmetic on the following + //line be done with two void* ptrs? The result + //would be ptrdiff_t, and what is the correct + //sprintf() conversion character for that? + (size_t)pc - (size_t)info.dli_saddr); + + return(1); +} + +void +ACE_Stack_Trace::generate_trace (ssize_t starting_frame_offset, + size_t num_frames) +{ + const size_t MAX_FRAMES = 128; + const ssize_t INITIAL_FRAME = 3; + + if (num_frames == 0) + num_frames = MAX_FRAMES; + + size_t starting_frame = + determine_starting_frame (INITIAL_FRAME, starting_frame_offset); + + cs_operate (&add_frame_to_buf, &this->buf_[0], starting_frame, num_frames); +} + +#elif defined(ACE_WIN64) && (_WIN32_WINNT <= _WIN32_WINNT_WIN2K) +# if defined(_MSC_VER) +# define STRING2(X) #X +# define STRING(X) STRING2(X) +# pragma message (__FILE__ "(" STRING(__LINE__) ") : warning: stack traces"\ + " can't be generated on 64-bit Windows when _WIN32_WINNT is less than "\ + "0x501.") +# undef STRING +# undef STRING2 +# endif /*_MSC_VER*/ +void +ACE_Stack_Trace::generate_trace (ssize_t, size_t) +{ + ACE_OS::strcpy (&this->buf_[0], "<stack traces unsupported on Win64 unless " + "ACE is built with _WIN32_WINNT set to 0x501 or above>"); +} + +#elif defined(ACE_WIN32) && !defined(ACE_HAS_WINCE) +# include <windows.h> +# include <Dbghelp.h> + +# define MAXTEXT 5000 +# define SYMSIZE 100 + +//@TODO: Test with WCHAR +//@TODO: Need a common CriticalSection since dbghelp is not thread-safe + +typedef struct _dbghelp_functions +{ + HMODULE hMod; //our handle to dbghelp.dll + + //these already have typedefs in DbgHelp.h + DWORD64 (WINAPI *SymGetModuleBase64) (HANDLE hProc, DWORD64 dwAddr); + PVOID (WINAPI *SymFunctionTableAccess64) (HANDLE hProc, DWORD64 AddrBase); + + typedef BOOL (WINAPI *SymFromAddr_t) + (HANDLE hProc, DWORD64 Addr, PDWORD64 Disp, PSYMBOL_INFO Symbol); + SymFromAddr_t SymFromAddr; + + typedef BOOL (WINAPI *SymGetLineFromAddr64_t) (HANDLE hProc, DWORD64 dwAddr, + PDWORD pdwDisplacement, + PIMAGEHLP_LINE64 Line); + SymGetLineFromAddr64_t SymGetLineFromAddr64; + + typedef DWORD (WINAPI *SymSetOptions_t) (DWORD SymOptions); + SymSetOptions_t SymSetOptions; + + typedef DWORD (WINAPI *SymGetOptions_t) (); + SymGetOptions_t SymGetOptions; + + typedef BOOL (WINAPI *SymInitialize_t) (HANDLE hProc, PCTSTR UserSearchPath, + BOOL invasive); + SymInitialize_t SymInitialize; + + typedef BOOL + (WINAPI *StackWalk64_t) (DWORD MachineType, HANDLE hPRoc, HANDLE hThr, + LPSTACKFRAME64 StackFrame, PVOID ContextRecord, + PREAD_PROCESS_MEMORY_ROUTINE64 RMRoutine, + PFUNCTION_TABLE_ACCESS_ROUTINE64 FTARoutine, + PGET_MODULE_BASE_ROUTINE64 GMBRoutine, + PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress); + StackWalk64_t StackWalk64; + + typedef BOOL (WINAPI *SymCleanup_t) (HANDLE hProc); + SymCleanup_t SymCleanup; +} dbghelp_functions; + + +static bool load_dbghelp_library_if_needed (dbghelp_functions *pDbg) +{ + //@TODO: See codeproject's StackWalker.cpp for the list of locations to + //search so we get the "enhanced" dbghelp if the user has it but it is not + //first on the path. + if (!(pDbg->hMod = LoadLibrary (TEXT ("Dbghelp")))) + return false; + + //@TODO: Cache this so we don't have to re-link every time. When to unload? + +# define LINK(TYPE, NAME) (pDbg->NAME = \ + (TYPE) GetProcAddress (pDbg->hMod, TEXT (#NAME))) +# define LINK_T(NAME) LINK (dbghelp_functions::NAME##_t, NAME) + return LINK (PGET_MODULE_BASE_ROUTINE64, SymGetModuleBase64) + && LINK (PFUNCTION_TABLE_ACCESS_ROUTINE64, SymFunctionTableAccess64) + && LINK_T (SymFromAddr) && LINK_T (SymGetLineFromAddr64) + && LINK_T (SymSetOptions)&& LINK_T (SymGetOptions) + && LINK_T (SymInitialize) && LINK_T (StackWalk64) && LINK_T (SymCleanup); +# undef LINK +# undef LINK_T +} + +struct frame_state { + STACKFRAME64 sf; + PSYMBOL_INFO pSym; + dbghelp_functions *pDbg; +}; + +static int +add_frame_to_buf (struct frame_state const *fs, void *usrarg) +{ + if (fs == 0 || usrarg == 0) + return -1; + + char *buf = static_cast<char *> (usrarg); + + DWORD64 disp; + DWORD64 dwModBase = fs->pDbg->SymGetModuleBase64 (GetCurrentProcess (), + fs->sf.AddrPC.Offset); + if (fs->pDbg->SymFromAddr (GetCurrentProcess (), + fs->sf.AddrPC.Offset, &disp, fs->pSym)) + { + IMAGEHLP_LINE64 line = {sizeof (IMAGEHLP_LINE64)}; + DWORD lineDisp; + if (fs->pDbg->SymGetLineFromAddr64 (GetCurrentProcess (), + fs->sf.AddrPC.Offset, + &lineDisp, &line)) + { + (void) ACE_OS::snprintf (buf, ACE_Stack_Trace::SYMBUFSIZ, + "%s%s() %s: %d + 0x%x\n", + buf, fs->pSym->Name, line.FileName, + line.LineNumber, lineDisp); + } + else + { + (void) ACE_OS::snprintf (buf, ACE_Stack_Trace::SYMBUFSIZ, + "%s%s()+0x%x [0x%x]\n", + buf, fs->pSym->Name, disp, + fs->sf.AddrPC.Offset - dwModBase); + } + } + else + { + (void) ACE_OS::snprintf (buf, ACE_Stack_Trace::SYMBUFSIZ, + "%s[0x%x]\n", + buf, fs->sf.AddrPC.Offset - dwModBase); + } + return 0; +} + +static void emptyStack () { } + +static int +cs_operate(int (*func)(struct frame_state const *, void *), void *usrarg, + size_t starting_frame, size_t num_frames) +{ + dbghelp_functions dbg; + if (!load_dbghelp_library_if_needed (&dbg)) + { + ACE_OS::strcpy (static_cast<char *> (usrarg), + "<error loading dbghelp.dll>"); + if (dbg.hMod) FreeLibrary (dbg.hMod); + return 1; + } + + frame_state fs; + ZeroMemory (&fs.sf, sizeof (fs.sf)); + fs.pDbg = &dbg; + emptyStack (); //Not sure what this should do, Chad? + + CONTEXT c; + ZeroMemory (&c, sizeof (CONTEXT)); + c.ContextFlags = CONTEXT_FULL; + +# if defined (_M_IX86) + DWORD machine = IMAGE_FILE_MACHINE_I386; + __asm { + call x + x: pop eax + mov c.Eip, eax + mov c.Ebp, ebp + mov c.Esp, esp + } + fs.sf.AddrPC.Offset = c.Eip; + fs.sf.AddrStack.Offset = c.Esp; + fs.sf.AddrFrame.Offset = c.Ebp; + fs.sf.AddrPC.Mode = AddrModeFlat; + fs.sf.AddrStack.Mode = AddrModeFlat; + fs.sf.AddrFrame.Mode = AddrModeFlat; +# elif defined (_M_X64) + DWORD machine = IMAGE_FILE_MACHINE_AMD64; + RtlCaptureContext (&c); + fs.sf.AddrPC.Offset = c.Rip; + fs.sf.AddrFrame.Offset = c.Rsp; //should be Rbp or Rdi instead? + fs.sf.AddrStack.Offset = c.Rsp; + fs.sf.AddrPC.Mode = AddrModeFlat; + fs.sf.AddrFrame.Mode = AddrModeFlat; + fs.sf.AddrStack.Mode = AddrModeFlat; +# elif defined (_M_IA64) + DWORD machine = IMAGE_FILE_MACHINE_IA64; + RtlCaptureContext (&c); + fs.sf.AddrPC.Offset = c.StIIP; + fs.sf.AddrFrame.Offset = c.RsBSP; + fs.sf.AddrBStore.Offset = c.RsBSP; + fs.sf.AddrStack.Offset = c.IntSp; + fs.sf.AddrPC.Mode = AddrModeFlat; + fs.sf.AddrFrame.Mode = AddrModeFlat; + fs.sf.AddrBStore.Mode = AddrModeFlat; + fs.sf.AddrStack.Mode = AddrModeFlat; +# endif + + fs.pSym = (PSYMBOL_INFO) GlobalAlloc (GMEM_FIXED, + sizeof (SYMBOL_INFO) + + sizeof (TCHAR) * (SYMSIZE - 1)); + fs.pSym->SizeOfStruct = sizeof (SYMBOL_INFO); + fs.pSym->MaxNameLen = SYMSIZE * sizeof (TCHAR); + dbg.SymSetOptions (SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES + | SYMOPT_FAIL_CRITICAL_ERRORS | dbg.SymGetOptions ()); + dbg.SymInitialize (GetCurrentProcess (), 0, true); + //What does the "true" parameter mean when tracing the current process? + + for (size_t current_frame = 0; current_frame < num_frames + starting_frame; + ++current_frame) + { + BOOL ok = dbg.StackWalk64 (machine, + GetCurrentProcess (), + GetCurrentThread (), + &fs.sf, &c, 0, + dbg.SymFunctionTableAccess64, + dbg.SymGetModuleBase64, 0); + if (!ok || fs.sf.AddrFrame.Offset == 0) + break; + + if (current_frame < starting_frame) + continue; + + func (&fs, usrarg); + } + + dbg.SymCleanup (GetCurrentProcess ()); + GlobalFree (fs.pSym); + FreeLibrary (dbg.hMod); + + return 0; +} + +void +ACE_Stack_Trace::generate_trace (ssize_t starting_frame_offset, + size_t num_frames) +{ + const size_t MAX_FRAMES = 128; + const ssize_t INITIAL_FRAME = 3; + + if (num_frames == 0) + num_frames = MAX_FRAMES; + + size_t starting_frame = + determine_starting_frame (INITIAL_FRAME, starting_frame_offset); + + cs_operate (&add_frame_to_buf, &this->buf_[0], starting_frame, num_frames); +} + +#else // Unsupported platform +void +ACE_Stack_Trace::generate_trace (ssize_t, size_t) +{ + ACE_OS::strcpy (&this->buf_[0], UNSUPPORTED); +} +#endif + diff --git a/ACE/ace/Stack_Trace.h b/ACE/ace/Stack_Trace.h new file mode 100644 index 00000000000..4b46f705129 --- /dev/null +++ b/ACE/ace/Stack_Trace.h @@ -0,0 +1,72 @@ +// -*- C++ -*- +//============================================================================= +/** + * @file Stack_Trace.h + * + * $Id$ + * + * @brief Encapsulate string representation of stack trace. + * + * @author Chris Cleeland <cleeland at ociweb dot com> + */ +//============================================================================= + +#ifndef ACE_STACK_TRACE_H +#define ACE_STACK_TRACE_H + +#include /**/ "ace/pre.h" + +#include "ace/ACE_export.h" +#include "ace/Basic_Types.h" + +# if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +# endif /* ACE_LACKS_PRAGMA_ONCE */ + +# ifndef ACE_STACK_TRACE_SYMBUFSIZ +# define ACE_STACK_TRACE_SYMBUFSIZ 4096 +# endif + +/** + * @class ACE_Stack_Trace + * + * @brief Encapsulate a string representation of a stack trace on supported platforms. + * Stack traces for code built with optimize=1 (or "Release" configs on Visual + * Studio) may be misleading (missng frames) due to inlining performed by the + * compiler, which is indepenent of the inline=0 / inline=1 build option and + * the __ACE_INLINE__ / ACE_NO_INLINE macros. + */ +class ACE_Export ACE_Stack_Trace +{ +public: + /** + * @brief Grab a snapshot of the current stack trace and hold it for later use. + * + * @param starting_frame_offset offset into the array of frames to start printing; 0 is the platform-specific offset for the first frame, positive numbers give less frames, negative give more frames + * @param num_frames the number of stack frames to include (0 indicates platform-specific maximum) + * + */ + explicit ACE_Stack_Trace (ssize_t starting_frame_offset = 0, size_t num_frames = 0); + + /** + * @brief Return buffer as a C-style string. + * @return C-style string with string representation of stack trace. + * @note Lifecycle of string follows lifecycle of ACE_Stack_Trace instance. + */ + const char* c_str() const; + + static const size_t SYMBUFSIZ = ACE_STACK_TRACE_SYMBUFSIZ; + +private: + char buf_[SYMBUFSIZ]; + size_t buflen_; + + static const char UNSUPPORTED[]; + static const char UNABLE_TO_GET_TRACE[]; + + void generate_trace (ssize_t starting_frame_offset, size_t num_frames); +}; + +#include "ace/post.h" +#endif /* ACE_STACK_TRACE_H */ + diff --git a/ACE/ace/ace.mpc b/ACE/ace/ace.mpc index cb6805b36a1..44b6b7fa2e5 100644 --- a/ACE/ace/ace.mpc +++ b/ACE/ace/ace.mpc @@ -239,6 +239,7 @@ project(ACE) : acedefaults, install, other, codecs, token, svcconf, uuid, fileca SPIPE_Connector.cpp SPIPE_Stream.cpp SString.cpp + Stack_Trace.cpp Stats.cpp String_Base_Const.cpp SUN_Proactor.cpp diff --git a/ACE/tests/Stack_Trace_Test.cpp b/ACE/tests/Stack_Trace_Test.cpp new file mode 100644 index 00000000000..a02b062fd43 --- /dev/null +++ b/ACE/tests/Stack_Trace_Test.cpp @@ -0,0 +1,86 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = DESCRIPTION +// This program exercises the ACE_Stack_Trace class. +// +// = AUTHOR +// Chris Cleeland <cleeland @ ociweb . com> +// +// ============================================================================ + +#include "ace/Stack_Trace.h" +#include "ace/OS_NS_string.h" +#include "test_config.h" + +ACE_RCSID(tests, Stack_Trace_Test, "$Id$") + +/* + * Ultra-basic test of stack trace. + * + * Other things to test: + * - throwing exceptions that contain the stack trace, and catching + * - capturing multiple traces + * - capture traces with different numbers of frames + * - capture traces with different numbers of skipped frames + */ + + +struct SomeException +{ + static const ssize_t SKIP = 1; //@TODO: #ifdef this for MACOSX which needs 2 + SomeException () : where_(SKIP), explanation_ ("") { } + SomeException (const char* explanation) + : where_(SKIP), explanation_(explanation) { } + ACE_Stack_Trace where_; + const char* explanation_; +}; + +void +func () +{ + throw SomeException(); +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Stack_Trace_Test")); + + ACE_Stack_Trace t1; + ACE_DEBUG ((LM_DEBUG, "t1 trace is %s\n", t1.c_str())); + + ACE_Stack_Trace t2(t1); + ACE_DEBUG ((LM_DEBUG, "t2 (copied) trace is %s\n", + (ACE_OS::strcmp (t1.c_str(), t2.c_str()) == 0) ? "same" : "different")); + + ACE_Stack_Trace t3; + ACE_DEBUG ((LM_DEBUG, "t3 trace before assignment is %s\n", + (ACE_OS::strcmp (t1.c_str(), t3.c_str()) == 0) ? "same" : "different")); + t3 = t1; + ACE_DEBUG ((LM_DEBUG, "t3 trace after assignment is %s\n", + (ACE_OS::strcmp (t1.c_str(), t3.c_str()) == 0) ? "same" : "different")); + + try { + func(); + } + catch (SomeException& e) { + ACE_DEBUG ((LM_DEBUG, + "SomeException caught at\n%?\n; thrown at\n%s", + e.where_.c_str())); + } + + ACE_Stack_Trace one_frame_only(0, 1); + ACE_DEBUG ((LM_DEBUG, + "stack trace of only one frame:\n%s", + one_frame_only.c_str())); + + ACE_DEBUG ((LM_DEBUG, "getting ready to end the test at %?\n")); + + ACE_END_TEST; + return 0; +} diff --git a/ACE/tests/run_test.lst b/ACE/tests/run_test.lst index 35a5191d6fc..a01be97fe28 100644 --- a/ACE/tests/run_test.lst +++ b/ACE/tests/run_test.lst @@ -151,6 +151,7 @@ SOCK_Send_Recv_Test: !NO_NETWORK SOCK_Test: !NO_NETWORK SPIPE_Test: !nsk !ACE_FOR_TAO SString_Test: !ACE_FOR_TAO +Stack_Trace_Test: SV_Shared_Memory_Test: !MSVC !Unicos !VxWorks !RH_7.1 !nsk !ACE_FOR_TAO Semaphore_Test: !ACE_FOR_TAO Service_Config_Test: !STATIC diff --git a/ACE/tests/tests.mpc b/ACE/tests/tests.mpc index b5f6b3eef5a..a6a2dce1ebd 100644 --- a/ACE/tests/tests.mpc +++ b/ACE/tests/tests.mpc @@ -128,6 +128,13 @@ project(Arg Shifter Test) : acetest { } } +project(Stack Trace Test) : acetest { + exename = Stack_Trace_Test + Source_Files { + Stack_Trace_Test.cpp + } +} + project(Array Map Test) : acetest { exename = Array_Map_Test Source_Files { |