00001 #include "stacktrace.h"
00002 #include "systrace.h"
00003
00004 #ifdef TRACE_SOURCE_FILES
00005 #ifdef LIN_STACK_TRACE
00006 #include "libtrace.h"
00007 static char Init_bfd = 1;
00008 #endif
00009 #endif
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 void set_prgname(const char *fname)
00023 {
00024 #if defined(LIN_STACK_TRACE) && defined(TRACE_SOURCE_FILES)
00025 set_program_name(fname);
00026 #endif
00027 }
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041 void stack_trace(FILE *out, long level)
00042 {
00043 #ifdef LIN_STACK_TRACE
00044 lin_stack_trace(out, level);
00045 #endif
00046 #ifdef WIN_STACK_TRACE
00047 win_stack_trace(out, level);
00048 #endif
00049 }
00050
00051
00052
00053 #ifdef LIN_STACK_TRACE
00054 #ifndef _GNU_SOURCE
00055 # define _GNU_SOURCE
00056 #endif
00057
00058 #include <stdlib.h>
00059 #include <dlfcn.h>
00060 #include <execinfo.h>
00061 #include <string.h>
00062 #include <ucontext.h>
00063 #ifndef NO_CPP_DEMANGLE
00064 #include <cxxabi.h>
00065 #endif
00066
00067 #if defined(REG_RIP)
00068 # define STACK_IA64
00069 #elif defined(REG_EIP)
00070 # define STACK_X86
00071 #else
00072 # define STACK_GENERIC
00073 #endif
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085 void lin_stack_trace(FILE *out, long level)
00086 {
00087 ucontext_t *ucontext = new ucontext_t;
00088 getcontext(ucontext);
00089 long count = 0;
00090 #ifdef TRACE_SOURCE_FILES
00091 char fname[256];
00092 const char *pname = NULL;
00093 unsigned int ln;
00094 #endif
00095
00096 #if defined(STACK_X86) || defined(STACK_IA64)
00097 Dl_info dlinfo;
00098 void **bp = 0;
00099 void *ip = 0;
00100 #else
00101 long i;
00102 void *bt[20];
00103 char **strings;
00104 size_t sz;
00105 #endif
00106
00107 #if defined(STACK_X86) || defined(STACK_IA64)
00108 # if defined(STACK_IA64) // 64 bit system
00109 ip = (void *)ucontext->uc_mcontext.gregs[REG_RIP];
00110 bp = (void **)ucontext->uc_mcontext.gregs[REG_RBP];
00111 # elif defined(STACK_X86) // 32 bit system
00112 ip = (void *)ucontext->uc_mcontext.gregs[REG_EIP];
00113 bp = (void **)ucontext->uc_mcontext.gregs[REG_EBP];
00114 # endif
00115
00116 #ifdef TRACE_SOURCE_FILES
00117 if ((get_bfd_pointer() == NULL) && (Init_bfd == 1))
00118 {
00119 if(dladdr(ip, &dlinfo))
00120 pname = dlinfo.dli_fname;
00121 else
00122 pname = get_program_name();
00123 if (pname)
00124 {
00125 libtrace_init(pname, NULL, NULL);
00126 Init_bfd = 0;
00127 }
00128 }
00129 #endif
00130 while(bp && ip)
00131 {
00132 if(!dladdr(ip, &dlinfo))
00133 break;
00134
00135 const char *symname = dlinfo.dli_sname;
00136 #ifndef NO_CPP_DEMANGLE
00137 int status;
00138 char *tmp = abi::__cxa_demangle(symname, NULL, NULL, &status);
00139 if(status == 0 && tmp)
00140 symname = tmp;
00141 #endif
00142
00143 if (count >= level)
00144 {
00145 if (count==level)
00146 {
00147 fprintf(out, "\nComplete call trace:\n");
00148 #ifdef TRACE_SOURCE_FILES
00149 if (get_bfd_pointer())
00150 {
00151 libtrace_resolve((void *)((char *)ip - 1), NULL, 0, fname, 256, ln);
00152 fprintf(out, "%s [%s, line %u]\n", symname, fname, ln);
00153 }
00154 else
00155 fprintf(out, "%s (address %p, raddress %#lx)\n", symname, ip,
00156 (unsigned long)(ip) - (unsigned long)(dlinfo.dli_saddr));
00157 #else
00158 fprintf(out, "%s (address %p, raddress %#lx)\n", symname, ip,
00159 (unsigned long)(ip) - (unsigned long)(dlinfo.dli_saddr));
00160 #endif
00161 }
00162 else
00163 {
00164 #ifdef TRACE_SOURCE_FILES
00165 if (get_bfd_pointer())
00166 {
00167 libtrace_resolve((void *)((char *)ip - 1), NULL, 0, fname, 256, ln);
00168 fprintf(out, " called by %s [%s, line %u]\n", symname, fname, ln);
00169 }
00170 else
00171 fprintf(out, " called by %s (address %p raddress %#lx)\n", symname, ip,
00172 (unsigned long)(ip) - (unsigned long)(dlinfo.dli_saddr));
00173 #else
00174 fprintf(out, " called by %s (address %p raddress %#lx)\n", symname, ip,
00175 (unsigned long)(ip) - (unsigned long)(dlinfo.dli_saddr));
00176 #endif
00177 }
00178 }
00179 #ifndef NO_CPP_DEMANGLE
00180 if(tmp)
00181 free(tmp);
00182 #endif
00183
00184 if(dlinfo.dli_sname && !strcmp(dlinfo.dli_sname, "main"))
00185 {
00186 fprintf(out, " in program %s\n\n", dlinfo.dli_fname);
00187 break;
00188 }
00189 count++;
00190 ip = (void *)bp[1];
00191 bp = (void **)bp[0];
00192 }
00193
00194 #else
00195 sz = backtrace(bt, 20);
00196 strings = backtrace_symbols(bt, sz);
00197
00198 fprintf(out, "\nCall trace (for max. %ld levels):\n", sz-level);
00199 fprintf(out, "%s\n", strings[level]);
00200 for(i = level+1; i < sz; ++i)
00201 fprintf(out, " called by %s()\n", strings[i]);
00202 fprintf(out, "\n");
00203 #endif
00204 delete ucontext;
00205 }
00206 #endif
00207
00208
00209
00210 #ifdef WIN_STACK_TRACE
00211 #include <windows.h>
00212 #ifdef __WATCOMC__
00213 #include <imagehlp.h>
00214
00215 #endif
00216 #ifndef __WATCOMC__
00217 #include <dbghelp.h>
00218 #endif
00219
00220 #define MAX_INSTRUCTION_LENGTH 100
00221 #define MAX_FUNCTION_PROLOG 100
00222 #define MAX_SYMBOL_LENGTH 512
00223
00224
00225 typedef DWORD (__stdcall *tSSO)(IN DWORD SymOptions);
00226 tSSO pSSO;
00227
00228
00229 typedef BOOL (__stdcall *tSI)(IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess);
00230 tSI pSI;
00231
00232
00233
00234
00235
00236
00237
00238
00239 #ifdef _X86_
00240
00241 typedef BOOL (__stdcall *tSGSFA)(IN HANDLE hProcess, IN DWORD dwAddr, OUT PDWORD pdwDisplacement,
00242 OUT PIMAGEHLP_SYMBOL Symbol);
00243 tSGSFA pSGSFA;
00244
00245
00246 typedef BOOL (__stdcall *tSGLFA)(IN HANDLE hProcess, IN DWORD dwAddr,
00247 OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE Line);
00248 tSGLFA pSGLFA;
00249 #endif
00250
00251 #if defined(_IA64_) || defined(_AMD64_)
00252
00253 typedef BOOL (__stdcall *tSGSFA)(IN HANDLE hProcess, IN DWORD64 dwAddr, OUT PDWORD64 pdwDisplacement,
00254 OUT PIMAGEHLP_SYMBOL64 Symbol);
00255 tSGSFA pSGSFA;
00256
00257
00258 typedef BOOL (__stdcall *tSGLFA)(IN HANDLE hProcess, IN DWORD64 dwAddr,
00259 OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE64 Line);
00260 tSGLFA pSGLFA;
00261
00262
00263 typedef BOOL (__stdcall *tSW)(DWORD MachineType, HANDLE hProcess, HANDLE hThread, LPSTACKFRAME64 StackFrame,
00264 PVOID ContextRecord, PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
00265 PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
00266 PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
00267 PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress);
00268 tSW pSW;
00269
00270
00271 typedef PVOID (__stdcall *tSFTA)(HANDLE hProcess, DWORD64 AddrBase);
00272 tSFTA pSFTA;
00273
00274
00275 typedef DWORD (__stdcall *tSGMB)(IN HANDLE hProcess, IN DWORD64 dwAddr);
00276 tSGMB pSGMB;
00277
00278 #endif
00279
00280
00281
00282
00283
00284
00285 HINSTANCE hDbghelpDll = NULL;
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297 void demangle_watcom(char *mname, char *dname)
00298 {
00299
00300 char *aux = strstr(mname, "W?");
00301 if (aux)
00302 {
00303 aux+=2;
00304 size_t i = 0;
00305
00306 while (aux[i] && aux[i] != '$')
00307 {
00308
00309 dname[i] = aux[i];
00310 i++;
00311 }
00312
00313 dname[i] = '\0';
00314 }
00315 else
00316
00317 memcpy(dname, mname, strlen(mname)+1);
00318 }
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335 char *remove_path_level(char *path, long level)
00336 {
00337 char *fname = NULL;
00338 long i = strlen(path)-1;
00339 char c;
00340 long stop = 0;
00341 while ((c = path[i]))
00342 {
00343
00344 if (c == '\\' || c == '/')
00345 {
00346 fname = path+i+1;
00347 stop++;
00348 if (stop == level)
00349 break;
00350 }
00351 i--;
00352 }
00353 if (fname == NULL)
00354 fname = path;
00355 return fname;
00356 }
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383 void win_stack_trace(FILE *out, long level)
00384 {
00385
00386 if (hDbghelpDll == NULL)
00387 {
00388 hDbghelpDll = LoadLibrary(TEXT("dbghelp.dll"));
00389 if (hDbghelpDll == NULL)
00390 {
00391 fprintf(stderr, "\n\nError: Cannot load dbghelp.dll,\n stack trace cannot be performed\n");
00392 fprintf(stderr, "in file %s, line %d, function %s\n", __FILE__, __LINE__, __FUNCTION__);
00393 return;
00394 }
00395
00396 pSI = (tSI) GetProcAddress(hDbghelpDll, "SymInitialize");
00397 pSSO = (tSSO) GetProcAddress(hDbghelpDll, "SymSetOptions");
00398 #ifdef _X86_
00399
00400
00401
00402
00403 pSGSFA = (tSGSFA) GetProcAddress(hDbghelpDll, "SymGetSymFromAddr");
00404 pSGLFA = (tSGLFA) GetProcAddress(hDbghelpDll, "SymGetLineFromAddr");
00405
00406 #endif
00407 #if defined(_IA64_) || defined(_AMD64_)
00408 pSGSFA = (tSGSFA) GetProcAddress(hDbghelpDll, "SymGetSymFromAddr64");
00409 pSGLFA = (tSGLFA) GetProcAddress(hDbghelpDll, "SymGetLineFromAddr64");
00410 #endif
00411
00412 if ((pSI == NULL) || (pSSO == NULL) || (pSGSFA == NULL) || (pSGLFA == NULL))
00413 {
00414 fprintf(stderr, "Error: some required function not found in imagehlp.dll\n" );
00415 fprintf(stderr, "in file %s, line %d, function %s\n", __FILE__, __LINE__, __FUNCTION__);
00416 FreeLibrary(hDbghelpDll);
00417 return;
00418 }
00419 }
00420
00421
00422 HANDLE hprocess = ::GetCurrentProcess ();
00423
00424 pSSO(SYMOPT_LOAD_LINES| SYMOPT_UNDNAME);
00425 if ( !pSI(hprocess, NULL, TRUE))
00426 {
00427 fprintf(stderr, "Error: debugging symbols cannot be initialized\n");
00428 fprintf(stderr, "in file %s, line %d, function %s\n", __FILE__, __LINE__, __FUNCTION__);
00429 }
00430
00431
00432 CONTEXT context = {0};
00433 HANDLE hthread = ::GetCurrentThread ();
00434 context.ContextFlags = CONTEXT_FULL;
00435 #ifdef _X86_
00436 DWORD reip;
00437 DWORD rebp;
00438 DWORD resp;
00439 #endif
00440
00441 #if defined(_IA64_) || defined(_AMD64_)
00442 DWORD64 reip;
00443 DWORD64 rebp;
00444 DWORD64 resp;
00445 #endif
00446
00447
00448
00449 do{
00450 #ifdef _X86_
00451 __asm {
00452 mov rebp, ebp
00453 mov resp, esp
00454 };
00455 context.Ebp = rebp;
00456 context.Esp = resp;
00457 #endif
00458 #if defined(_IA64_) || defined(_AMD64_)
00459
00460
00461
00462
00463
00464
00465 RtlCaptureContext(&context);
00466 #endif
00467
00468
00469
00470
00471
00472
00473 #ifdef _X86_
00474 context.Eip = (unsigned int)(&win_stack_trace) + MAX_FUNCTION_PROLOG;
00475 #endif
00476 #if defined(_IA64_) || defined(_AMD64_)
00477 context.Rip = (unsigned int)(&win_stack_trace) + MAX_FUNCTION_PROLOG;
00478 #endif
00479 }while(0);
00480
00481
00482
00483 STACKFRAME sf = {0};
00484 sf.AddrPC.Mode = AddrModeFlat;
00485 sf.AddrStack.Mode = AddrModeFlat;
00486 sf.AddrFrame.Mode = AddrModeFlat;
00487
00488 #ifdef _X86_
00489 sf.AddrPC.Offset = context.Eip;
00490 sf.AddrStack.Offset = context.Esp;
00491 sf.AddrFrame.Offset = context.Ebp;
00492 #endif
00493 #if defined(_IA64_) || defined(_AMD64_)
00494 sf.AddrPC.Offset = context.Rip;
00495 sf.AddrStack.Offset = context.Rsp;
00496 sf.AddrFrame.Offset = context.Rbp;
00497 #endif
00498
00499
00500 int count=0;
00501 reip = 0;
00502 unsigned range = 1;
00503 IMAGEHLP_LINE Line;
00504 DWORD offset = 0;
00505 memset(&Line, 0, sizeof(Line));
00506 Line.SizeOfStruct = sizeof(Line);
00507 #if defined(_IA64_) || defined(_AMD64_)
00508
00509 while (::pSW(IMAGE_FILE_MACHINE_I386, GetCurrentProcess(), GetCurrentThread(),
00510 &sf, &context, NULL, pSFTA, pSGMB, NULL))
00511 #else
00512 while (sf.AddrFrame.Offset != 0)
00513 #endif
00514 {
00515
00516
00517
00518
00519
00520
00521
00522 BYTE symbol_buffer[sizeof(IMAGEHLP_SYMBOL) + MAX_SYMBOL_LENGTH];
00523 PIMAGEHLP_SYMBOL psymbol = (PIMAGEHLP_SYMBOL)symbol_buffer;
00524 memset (symbol_buffer, 0, sizeof (symbol_buffer));
00525 psymbol->SizeOfStruct = sizeof(symbol_buffer);
00526 psymbol->MaxNameLength = 512;
00527
00528
00529 IMAGEHLP_LINE Line;
00530 memset(&Line, 0, sizeof(Line));
00531 Line.SizeOfStruct = sizeof(Line);
00532
00533 char *fname = NULL;
00534 DWORD offset_l = 0;
00535 #ifdef _X86_
00536 DWORD sym_displacement = 0;
00537 #endif
00538 #if defined(_IA64_) || defined(_AMD64_)
00539 DWORD64 sym_displacement = 0;
00540 #endif
00541
00542
00543
00544
00545 if ((sf.AddrPC.Offset == sf.AddrReturn.Offset) || (sf.AddrPC.Offset == reip))
00546 {
00547 fprintf(out, "\nError: Endless loop in the stack trace\n");
00548 break;
00549 }
00550
00551
00552 reip = sf.AddrPC.Offset;
00553
00554
00555 if (pSGSFA != NULL)
00556 {
00557 if (pSGSFA(::GetCurrentProcess(), sf.AddrPC.Offset, &sym_displacement, psymbol) == FALSE)
00558 fprintf(out, "\nCannot get the symbols: %u\n", GetLastError());
00559 }
00560
00561 if (pSGLFA != NULL)
00562 {
00563
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573
00574
00575
00576 unsigned range = 1;
00577 while ((pSGLFA(::GetCurrentProcess(), sf.AddrPC.Offset-range, &offset_l, &Line) == FALSE) && (range < MAX_INSTRUCTION_LENGTH))
00578 {
00579
00580 range++;
00581 }
00582 if (pSGLFA(::GetCurrentProcess(), sf.AddrPC.Offset-range, &offset_l, &Line) != FALSE)
00583 {
00584
00585 fname = remove_path_level(Line.FileName, 2);
00586 }
00587 }
00588
00589 char tname[MAX_SYMBOL_LENGTH];
00590 tname[0] = '\0';
00591
00592 if (count >= level)
00593 {
00594
00595 #ifdef __WATCOMC__
00596
00597
00598
00599 demangle_watcom(psymbol->Name, tname);
00600 #else
00601
00602
00603
00604
00605 memcpy(tname, psymbol->Name, strlen(psymbol->Name)+1);
00606 #endif
00607 if (count==level)
00608 {
00609 fprintf(out, "\nComplete call trace:\n");
00610 fprintf(out, "%s ", tname);
00611 if (fname == NULL)
00612 fprintf(out, "(address %p, raddress %#lx)\n", sf.AddrPC.Offset,
00613 (unsigned long)(sf.AddrPC.Offset) - (unsigned long)(psymbol->Address));
00614 else
00615 fprintf(out, "[%s, line %u]\n", fname, Line.LineNumber);
00616 }
00617 else
00618 {
00619 fprintf(out, " called by %s ", tname);
00620 if (fname == NULL)
00621 fprintf(out, "(address %p, raddress %#lx)\n", sf.AddrPC.Offset,
00622 (unsigned long)(sf.AddrPC.Offset) - (unsigned long)(psymbol->Address));
00623 else
00624 fprintf(out, "[%s, line %u]\n", fname, Line.LineNumber);
00625 }
00626 }
00627 if (strcmp(psymbol->Name, "main") == 0)
00628 break;
00629 count++;
00630
00631 #ifdef _X86_
00632
00633 sf.AddrPC.Offset = *((DWORD *)(sf.AddrFrame.Offset+sizeof(void*)));
00634
00635 sf.AddrFrame.Offset = *((DWORD *)(sf.AddrFrame.Offset));
00636
00637
00638 sf.AddrStack.Offset = sf.AddrFrame.Offset-sizeof(void*);
00639 #endif
00640 #if defined(_IA64_) || defined(_AMD64_)
00641
00642
00643
00644
00645
00646
00647
00648 #endif
00649 }
00650 fprintf(out, "\n");
00651 }
00652 #endif