Document return values
[ACE_TAO.git] / ACE / ace / Stack_Trace.cpp
blob52e994cf471769ff2b1ea04f2c4a961615bc101a
1 //=============================================================================
2 /**
3 * @file Stack_Trace.cpp
5 * @brief Encapsulate string representation of stack trace.
7 * Some platform-specific areas of this code have been adapted from
8 * examples found elsewhere. Specifically,
9 * - the GLIBC stack generation uses the documented "backtrace" API
10 * and is adapted from examples shown in relevant documentation
11 * and repeated elsewhere, e.g.,
12 * http://www.linuxselfhelp.com/gnu/glibc/html_chapter/libc_33.html
13 * - VxWorks kernel-mode stack tracing is adapted from a code example
14 * in the VxWorks FAQ at http://www.xs4all.nl/~borkhuis/vxworks/vxw_pt5.html
15 * although the undocumented functions it uses are also mentioned in
16 * various documents available on the WindRiver support website.
18 * If you add support for a new platform, please add a bullet to the
19 * above list with durable references to the origins of your code.
21 //=============================================================================
23 #include "ace/Stack_Trace.h"
24 #include "ace/Min_Max.h"
25 #include "ace/OS_NS_string.h"
26 #include "ace/OS_NS_stdio.h"
28 ACE_BEGIN_VERSIONED_NAMESPACE_DECL
31 This is ugly, simply because it's very platform-specific.
34 const char ACE_Stack_Trace::UNSUPPORTED[] = "<stack traces unsupported platform>";
35 const char ACE_Stack_Trace::UNABLE_TO_GET_TRACE[] = "<unable to get trace>";
37 ACE_Stack_Trace::ACE_Stack_Trace (ssize_t starting_frame_offset, size_t num_frames)
38 : buflen_(0)
40 // cannot initialize arrays, so we must assign.
41 this->buf_[0] = '\0';
42 this->generate_trace (starting_frame_offset, num_frames);
45 const char*
46 ACE_Stack_Trace::c_str () const
48 return &this->buf_[0];
51 static inline size_t
52 determine_starting_frame (ssize_t initial_frame, ssize_t offset)
54 return ACE_MAX( initial_frame + offset, static_cast<ssize_t>(0));
57 #if defined(ACE_FACE_SAFETY_BASE) && !defined(ACE_FACE_DEV)
58 void
59 ACE_Stack_Trace::generate_trace (ssize_t starting_frame_offset, size_t num_frames)
61 ACE_UNUSED_ARG (starting_frame_offset);
62 ACE_UNUSED_ARG (num_frames);
63 ACE_OS::strcpy (&this->buf_[0], UNABLE_TO_GET_TRACE);
66 #elif (defined(__GLIBC__) || defined(ACE_HAS_EXECINFO_H))
67 // This is the code for glibc
68 # include <execinfo.h>
70 void
71 ACE_Stack_Trace::generate_trace (ssize_t starting_frame_offset, size_t num_frames)
73 const size_t MAX_FRAMES = 128;
74 const ssize_t INITIAL_FRAME = 3;
76 void* stack[MAX_FRAMES];
77 size_t stack_size = 0;
78 char** stack_syms;
80 if (num_frames == 0)
81 num_frames = MAX_FRAMES;
83 size_t starting_frame =
84 determine_starting_frame (INITIAL_FRAME, starting_frame_offset);
86 stack_size = ::backtrace (&stack[0], sizeof(stack)/sizeof(stack[0]));
87 if (stack_size != 0)
89 stack_syms = ::backtrace_symbols (stack, stack_size);
91 for (size_t i = starting_frame;
92 i < stack_size && num_frames > 0;
93 i++, num_frames--)
95 // this could be more efficient by remembering where we left off in buf_
96 char *symp = &stack_syms[i][0];
97 while (this->buflen_ < SYMBUFSIZ - 2 && *symp != '\0')
99 this->buf_[this->buflen_++] = *symp++;
101 this->buf_[this->buflen_++] = '\n'; // put a newline at the end
103 this->buf_[this->buflen_] = '\0'; // zero terminate the string
105 ::free (stack_syms);
107 else
109 ACE_OS::strcpy (&this->buf_[0], UNABLE_TO_GET_TRACE);
112 #elif defined(VXWORKS) && !defined(__RTP__)
113 # include <trcLib.h>
114 # include <taskLib.h> // hopefully this is enough to get all the necessary #defines.
116 struct ACE_Stack_Trace_stackstate
118 ACE_Stack_Trace_stackstate (char* b, size_t& bl, size_t nf, size_t sf)
119 : buf(b), buflen(bl), num_frames(nf), starting_frame(sf)
122 char* buf;
123 size_t& buflen;
124 size_t num_frames;
125 size_t starting_frame;
128 //@TODO: Replace with a TSS-based pointer to avoid problems in multithreaded environs,
129 // or use a mutex to serialize access to this.
130 static ACE_Stack_Trace_stackstate* ACE_Stack_Trace_stateptr = 0;
132 static void
133 ACE_Stack_Trace_Add_Frame_To_Buf (INSTR *caller,
134 INSTR *func,
135 int nargs,
136 ACE_VX_USR_ARG_T *args)
138 if (ACE_Stack_Trace_stateptr == 0)
139 return;
141 ACE_Stack_Trace_stackstate *stackstate = ACE_Stack_Trace_stateptr;
143 // Decrement the num_frames and starting_frame elements,
144 // then see if we're ready to start or ready to finish.
145 --stackstate->num_frames;
146 --stackstate->starting_frame;
148 if (stackstate->num_frames == 0 || stackstate->starting_frame > 0)
149 return;
151 // These are references so that the structure gets updated
152 // in the code below.
153 char*& buf = stackstate->buf;
154 size_t& len = stackstate->buflen;
156 // At some point try using symFindByValue() to lookup func (and caller?)
157 // to print out symbols rather than simply addresses.
159 // VxWorks can pass -1 for "nargs" if there was an error
160 if (nargs == -1)
161 nargs = 0;
163 len += ACE_OS::sprintf (&buf[len], "%p: %p (", caller, func);
164 for (int i = 0; i < nargs; ++i)
166 if (i != 0)
167 len += ACE_OS::sprintf (&buf[len], ", ");
168 len += ACE_OS::sprintf(&buf[len], "0x" ACE_VX_ARG_FORMAT, args[i]);
171 len += ACE_OS::sprintf(&buf[len], ")\n");
174 void
175 ACE_Stack_Trace::generate_trace (ssize_t starting_frame_offset,
176 size_t num_frames)
178 const size_t MAX_FRAMES = 128;
179 const ssize_t INITIAL_FRAME = 3;
181 if (num_frames == 0)
182 num_frames = MAX_FRAMES;
184 size_t starting_frame =
185 determine_starting_frame (INITIAL_FRAME, starting_frame_offset);
187 ACE_Stack_Trace_stackstate state (&this->buf_[0], this->buflen_,
188 num_frames, starting_frame);
190 REG_SET regs;
192 taskRegsGet (taskIdSelf(), &regs);
193 // Maybe we should take a lock here to guard stateptr?
194 ACE_Stack_Trace_stateptr = &state;
195 trcStack (&regs, (FUNCPTR)ACE_Stack_Trace_Add_Frame_To_Buf, taskIdSelf ());
199 #elif defined(VXWORKS) && defined(__RTP__)
200 # include <setjmp.h>
201 # include <taskLib.h>
202 # include <private/trcLibP.h>
204 // See memEdrLib.c in VxWorks RTP sources for an example of stack tracing.
206 static STATUS ace_vx_rtp_pc_validate (INSTR *pc, TRC_OS_CTX *)
208 return ALIGNED (pc, sizeof (INSTR)) ? OK : ERROR;
211 void
212 ACE_Stack_Trace::generate_trace (ssize_t starting_frame_offset,
213 size_t num_frames)
215 const size_t MAX_FRAMES = 128;
216 const ssize_t INITIAL_FRAME = 2;
218 if (num_frames == 0) num_frames = MAX_FRAMES;
219 size_t starting_frame =
220 determine_starting_frame (INITIAL_FRAME, starting_frame_offset);
222 jmp_buf regs;
223 setjmp (regs);
225 TASK_DESC desc;
226 if (taskInfoGet (taskIdSelf (), &desc) == ERROR) return;
228 TRC_OS_CTX osCtx;
229 osCtx.stackBase = desc.td_pStackBase;
230 osCtx.stackEnd = desc.td_pStackEnd;
231 #if (ACE_VXWORKS < 0x690)
232 osCtx.pcValidateRtn = reinterpret_cast<FUNCPTR> (ace_vx_rtp_pc_validate);
233 #else
234 // reinterpret_cast causes an error
235 osCtx.pcValidateRtn = ace_vx_rtp_pc_validate;
236 #endif
238 char *fp = _WRS_FRAMEP_FROM_JMP_BUF (regs);
239 INSTR *pc = _WRS_RET_PC_FROM_JMP_BUF (regs);
241 for (size_t depth = 0; depth < num_frames + starting_frame; ++depth)
243 char *prevFp;
244 INSTR *prevPc;
245 INSTR *prevFn;
247 if (trcLibFuncs.lvlInfoGet (fp, pc, &osCtx, &prevFp, &prevPc, &prevFn)
248 == ERROR)
250 ACE_OS::strcpy (this->buf_, UNABLE_TO_GET_TRACE);
251 return;
254 if(prevPc == 0 || prevFp == 0) break;
256 if (depth >= starting_frame)
258 //Hopefully a future version of VxWorks will have a system call
259 //for an RTP to query its own symbols, but this is not possible now.
260 //An enhancement request has been filed under WIND00123307.
261 const char *fnName = "(no symbols)";
263 static const int N_ARGS = 12;
264 ACE_VX_USR_ARG_T buf[N_ARGS];
265 ACE_VX_USR_ARG_T *pArgs = 0;
266 int numArgs =
267 trcLibFuncs.lvlArgsGet (prevPc, prevFn, prevFp,
268 buf, N_ARGS, &pArgs);
270 // VxWorks can return -1 for "numArgs" if there was an error
271 if (numArgs == -1) numArgs = 0;
273 size_t len = ACE_OS::strlen (this->buf_);
274 size_t space = SYMBUFSIZ - len - 1;
275 char *cursor = this->buf_ + len;
276 size_t written = ACE_OS::snprintf (cursor, space, "%p %s",
277 prevFn, fnName);
278 cursor += written;
279 space -= written;
281 if (space < 1) return; //no point in logging when we're out of buffer
282 for (int arg = 0; numArgs != -1 && pArgs && arg < numArgs; ++arg)
284 if (arg == 0) *cursor++ = '(', --space;
285 written = ACE_OS::snprintf (cursor, space,
286 (arg < numArgs - 1) ?
287 ACE_VX_ARG_FORMAT ", " :
288 ACE_VX_ARG_FORMAT,
289 pArgs[arg]);
290 cursor += written;
291 space -= written;
292 if (space && arg == numArgs - 1) *cursor++ = ')', --space;
294 if (space) *cursor++ = '\n', --space;
295 *cursor++ = 0; //we saved space for the null terminator
298 fp = prevFp;
299 pc = prevPc;
302 #elif defined(ACE_WIN64) && (_WIN32_WINNT <= _WIN32_WINNT_WIN2K)
303 # if defined(_MSC_VER)
304 # define STRING2(X) #X
305 # define STRING(X) STRING2(X)
306 # pragma message (__FILE__ "(" STRING(__LINE__) ") : warning: stack traces"\
307 " can't be generated on 64-bit Windows when _WIN32_WINNT is less than "\
308 "0x501.")
309 # undef STRING
310 # undef STRING2
311 # endif /*_MSC_VER*/
312 void
313 ACE_Stack_Trace::generate_trace (ssize_t, size_t)
315 ACE_OS::strcpy (&this->buf_[0], "<stack traces unsupported on Win64 unless "
316 "ACE is built with _WIN32_WINNT set to 0x501 or above>");
319 #elif defined(ACE_WIN32) && !defined (__MINGW32__) && !defined(__BORLANDC__)
320 # include <windows.h>
321 # include <Dbghelp.h>
323 # define MAXTEXT 5000
324 # define SYMSIZE 100
326 //@TODO: Test with WCHAR
327 //@TODO: Need a common CriticalSection since dbghelp is not thread-safe
329 typedef struct _dbghelp_functions
331 HMODULE hMod; //our handle to dbghelp.dll
333 //these already have typedefs in DbgHelp.h
334 DWORD64 (WINAPI *SymGetModuleBase64) (HANDLE hProc, DWORD64 dwAddr);
335 PVOID (WINAPI *SymFunctionTableAccess64) (HANDLE hProc, DWORD64 AddrBase);
337 typedef BOOL (WINAPI *SymFromAddr_t)
338 (HANDLE hProc, DWORD64 Addr, PDWORD64 Disp, PSYMBOL_INFO Symbol);
339 SymFromAddr_t SymFromAddr;
341 typedef BOOL (WINAPI *SymGetLineFromAddr64_t) (HANDLE hProc, DWORD64 dwAddr,
342 PDWORD pdwDisplacement,
343 PIMAGEHLP_LINE64 Line);
344 SymGetLineFromAddr64_t SymGetLineFromAddr64;
346 typedef DWORD (WINAPI *SymSetOptions_t) (DWORD SymOptions);
347 SymSetOptions_t SymSetOptions;
349 typedef DWORD (WINAPI *SymGetOptions_t) ();
350 SymGetOptions_t SymGetOptions;
352 typedef BOOL (WINAPI *SymInitialize_t) (HANDLE hProc, PCTSTR UserSearchPath,
353 BOOL invasive);
354 SymInitialize_t SymInitialize;
356 typedef BOOL
357 (WINAPI *StackWalk64_t) (DWORD MachineType, HANDLE hPRoc, HANDLE hThr,
358 LPSTACKFRAME64 StackFrame, PVOID ContextRecord,
359 PREAD_PROCESS_MEMORY_ROUTINE64 RMRoutine,
360 PFUNCTION_TABLE_ACCESS_ROUTINE64 FTARoutine,
361 PGET_MODULE_BASE_ROUTINE64 GMBRoutine,
362 PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress);
363 StackWalk64_t StackWalk64;
365 typedef BOOL (WINAPI *SymCleanup_t) (HANDLE hProc);
366 SymCleanup_t SymCleanup;
367 } dbghelp_functions;
370 # pragma warning (push)
371 # pragma warning (disable:4706)
372 static bool load_dbghelp_library_if_needed (dbghelp_functions *pDbg)
374 //@TODO: See codeproject's StackWalker.cpp for the list of locations to
375 //search so we get the "enhanced" dbghelp if the user has it but it is not
376 //first on the path.
377 if (!(pDbg->hMod = ACE_TEXT_LoadLibrary (ACE_TEXT ("Dbghelp"))))
378 return false;
380 //@TODO: Cache this so we don't have to re-link every time. When to unload?
382 # define LINK(TYPE, NAME) (pDbg->NAME = \
383 (TYPE) GetProcAddress (pDbg->hMod, #NAME))
384 # define LINK_T(NAME) LINK (dbghelp_functions::NAME##_t, NAME)
385 return LINK (PGET_MODULE_BASE_ROUTINE64, SymGetModuleBase64)
386 && LINK (PFUNCTION_TABLE_ACCESS_ROUTINE64, SymFunctionTableAccess64)
387 && LINK_T (SymFromAddr) && LINK_T (SymGetLineFromAddr64)
388 && LINK_T (SymSetOptions)&& LINK_T (SymGetOptions)
389 && LINK_T (SymInitialize) && LINK_T (StackWalk64) && LINK_T (SymCleanup);
390 # undef LINK
391 # undef LINK_T
393 # pragma warning (pop)
396 struct frame_state {
397 STACKFRAME64 sf;
398 PSYMBOL_INFO pSym;
399 dbghelp_functions *pDbg;
402 static int
403 add_frame_to_buf (struct frame_state const *fs, void *usrarg)
405 if (fs == 0 || usrarg == 0)
406 return -1;
408 char *buf = static_cast<char *> (usrarg);
410 DWORD64 disp;
411 DWORD64 dwModBase = fs->pDbg->SymGetModuleBase64 (GetCurrentProcess (),
412 fs->sf.AddrPC.Offset);
413 if (fs->pDbg->SymFromAddr (GetCurrentProcess (),
414 fs->sf.AddrPC.Offset, &disp, fs->pSym))
416 IMAGEHLP_LINE64 line = {sizeof (IMAGEHLP_LINE64)};
417 DWORD lineDisp;
418 if (fs->pDbg->SymGetLineFromAddr64 (GetCurrentProcess (),
419 fs->sf.AddrPC.Offset,
420 &lineDisp, &line))
422 (void) ACE_OS::snprintf (buf, ACE_Stack_Trace::SYMBUFSIZ,
423 "%s%s() %s: %d + 0x%x\n",
424 buf, fs->pSym->Name, line.FileName,
425 line.LineNumber, lineDisp);
427 else
429 (void) ACE_OS::snprintf (buf, ACE_Stack_Trace::SYMBUFSIZ,
430 "%s%s()+0x%x [0x%x]\n",
431 buf, fs->pSym->Name, disp,
432 fs->sf.AddrPC.Offset - dwModBase);
435 else
437 (void) ACE_OS::snprintf (buf, ACE_Stack_Trace::SYMBUFSIZ,
438 "%s[0x%x]\n",
439 buf, fs->sf.AddrPC.Offset - dwModBase);
441 return 0;
444 static void emptyStack () { }
446 #if defined (_MSC_VER)
447 # pragma warning(push)
448 // Suppress warning 4748 "/GS can not protect parameters and local
449 // variables from local buffer overrun because optimizations are
450 // disabled in function"
451 # pragma warning(disable: 4748)
452 #endif /* _MSC_VER */
454 static int
455 cs_operate(int (*func)(struct frame_state const *, void *), void *usrarg,
456 size_t starting_frame, size_t num_frames)
458 dbghelp_functions dbg;
459 if (!load_dbghelp_library_if_needed (&dbg))
461 ACE_OS::strcpy (static_cast<char *> (usrarg),
462 "<error loading dbghelp.dll>");
463 if (dbg.hMod) FreeLibrary (dbg.hMod);
464 return 1;
467 frame_state fs;
468 ZeroMemory (&fs.sf, sizeof (fs.sf));
469 fs.pDbg = &dbg;
470 emptyStack (); //Not sure what this should do, Chad?
472 CONTEXT c;
473 ZeroMemory (&c, sizeof (CONTEXT));
474 c.ContextFlags = CONTEXT_FULL;
476 # if defined (_M_IX86)
477 DWORD machine = IMAGE_FILE_MACHINE_I386;
478 __asm {
479 call x
480 x: pop eax
481 mov c.Eip, eax
482 mov c.Ebp, ebp
483 mov c.Esp, esp
485 fs.sf.AddrPC.Offset = c.Eip;
486 fs.sf.AddrStack.Offset = c.Esp;
487 fs.sf.AddrFrame.Offset = c.Ebp;
488 fs.sf.AddrPC.Mode = AddrModeFlat;
489 fs.sf.AddrStack.Mode = AddrModeFlat;
490 fs.sf.AddrFrame.Mode = AddrModeFlat;
491 # elif defined (_M_X64)
492 DWORD machine = IMAGE_FILE_MACHINE_AMD64;
493 RtlCaptureContext (&c);
494 fs.sf.AddrPC.Offset = c.Rip;
495 fs.sf.AddrFrame.Offset = c.Rsp; //should be Rbp or Rdi instead?
496 fs.sf.AddrStack.Offset = c.Rsp;
497 fs.sf.AddrPC.Mode = AddrModeFlat;
498 fs.sf.AddrFrame.Mode = AddrModeFlat;
499 fs.sf.AddrStack.Mode = AddrModeFlat;
500 # elif defined (_M_IA64)
501 DWORD machine = IMAGE_FILE_MACHINE_IA64;
502 RtlCaptureContext (&c);
503 fs.sf.AddrPC.Offset = c.StIIP;
504 fs.sf.AddrFrame.Offset = c.RsBSP;
505 fs.sf.AddrBStore.Offset = c.RsBSP;
506 fs.sf.AddrStack.Offset = c.IntSp;
507 fs.sf.AddrPC.Mode = AddrModeFlat;
508 fs.sf.AddrFrame.Mode = AddrModeFlat;
509 fs.sf.AddrBStore.Mode = AddrModeFlat;
510 fs.sf.AddrStack.Mode = AddrModeFlat;
511 # elif defined (_M_ARM)
512 DWORD machine = IMAGE_FILE_MACHINE_ARM;
513 fs.sf.AddrPC.Offset = c.Pc;
514 fs.sf.AddrFrame.Offset = c.R11;
515 fs.sf.AddrStack.Offset = c.Sp;
516 fs.sf.AddrPC.Mode = AddrModeFlat;
517 fs.sf.AddrFrame.Mode = AddrModeFlat;
518 fs.sf.AddrStack.Mode = AddrModeFlat;
519 # elif defined (_M_ARM64)
520 DWORD machine = IMAGE_FILE_MACHINE_ARM64;
521 fs.sf.AddrPC.Offset = c.Pc;
522 fs.sf.AddrFrame.Offset = c.Fp;
523 fs.sf.AddrStack.Offset = c.Sp;
524 fs.sf.AddrPC.Mode = AddrModeFlat;
525 fs.sf.AddrFrame.Mode = AddrModeFlat;
526 fs.sf.AddrStack.Mode = AddrModeFlat;
527 # endif
529 fs.pSym = (PSYMBOL_INFO) GlobalAlloc (GMEM_FIXED,
530 sizeof (SYMBOL_INFO) +
531 sizeof (ACE_TCHAR) * (SYMSIZE - 1));
532 fs.pSym->SizeOfStruct = sizeof (SYMBOL_INFO);
533 fs.pSym->MaxNameLen = SYMSIZE * sizeof (ACE_TCHAR);
534 dbg.SymSetOptions (SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES
535 | SYMOPT_FAIL_CRITICAL_ERRORS | dbg.SymGetOptions ());
536 dbg.SymInitialize (GetCurrentProcess (), 0, true);
537 //What does the "true" parameter mean when tracing the current process?
539 for (size_t current_frame = 0; current_frame < num_frames + starting_frame;
540 ++current_frame)
542 BOOL ok = dbg.StackWalk64 (machine,
543 GetCurrentProcess (),
544 GetCurrentThread (),
545 &fs.sf, &c, 0,
546 dbg.SymFunctionTableAccess64,
547 dbg.SymGetModuleBase64, 0);
548 if (!ok || fs.sf.AddrFrame.Offset == 0)
549 break;
551 if (current_frame < starting_frame)
552 continue;
554 func (&fs, usrarg);
557 dbg.SymCleanup (GetCurrentProcess ());
558 GlobalFree (fs.pSym);
559 FreeLibrary (dbg.hMod);
561 return 0;
564 #if defined (_MSC_VER)
565 // Restore the warning state to what it was before entry.
566 # pragma warning(pop)
567 #endif /* _MSC_VER */
569 void
570 ACE_Stack_Trace::generate_trace (ssize_t starting_frame_offset,
571 size_t num_frames)
573 const size_t MAX_FRAMES = 128;
574 const ssize_t INITIAL_FRAME = 3;
576 if (num_frames == 0)
577 num_frames = MAX_FRAMES;
579 size_t starting_frame =
580 determine_starting_frame (INITIAL_FRAME, starting_frame_offset);
582 cs_operate (&add_frame_to_buf, &this->buf_[0], starting_frame, num_frames);
585 #else // Unsupported platform
586 void
587 ACE_Stack_Trace::generate_trace (ssize_t, size_t)
589 ACE_OS::strcpy (&this->buf_[0], UNSUPPORTED);
591 #endif
593 ACE_END_VERSIONED_NAMESPACE_DECL