summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordai_y <dai_y@ae88bc3d-4319-0410-8dbf-d08b4c9d3795>2008-06-05 20:35:57 +0000
committerdai_y <dai_y@ae88bc3d-4319-0410-8dbf-d08b4c9d3795>2008-06-05 20:35:57 +0000
commitb3c3f344deb77cac6aed23c0250919a77236a922 (patch)
treed1b56f0db66da5d12e58b8342734f86d766d0f0b
parent0bb5a2d6ce66991d03629fbf348b01210e333ac9 (diff)
downloadATCD-b3c3f344deb77cac6aed23c0250919a77236a922.tar.gz
Thu Jun 5 20:32:00 UTC 2008 Yan Dai <dai_y@ociweb.com>
-rw-r--r--ACE/ChangeLog39
-rw-r--r--ACE/NEWS5
-rw-r--r--ACE/ace/Log_Msg.cpp17
-rw-r--r--ACE/ace/Stack_Trace.cpp668
-rw-r--r--ACE/ace/Stack_Trace.h72
-rw-r--r--ACE/ace/ace.mpc1
-rw-r--r--ACE/tests/Stack_Trace_Test.cpp86
-rw-r--r--ACE/tests/run_test.lst1
-rw-r--r--ACE/tests/tests.mpc7
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.
diff --git a/ACE/NEWS b/ACE/NEWS
index 9b8d44e1900..59abc806049 100644
--- a/ACE/NEWS
+++ b/ACE/NEWS
@@ -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(), &regs);
+ // Maybe we should take a lock here to guard stateptr?
+ ACE_Stack_Trace_stateptr = &state;
+ trcStack (&regs, (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 {