NCBI C++ ToolKit
ncbi_stack_win32.cpp
Go to the documentation of this file.

Go to the SVN repository for this file.

1 /* $Id: ncbi_stack_win32.cpp 87307 2019-08-16 16:59:06Z vasilche $
2  * ===========================================================================
3  *
4  * PUBLIC DOMAIN NOTICE
5  * National Center for Biotechnology Information
6  *
7  * This software/database is a "United States Government Work" under the
8  * terms of the United States Copyright Act. It was written as part of
9  * the author's official duties as a United States Government employee and
10  * thus cannot be copyrighted. This software/database is freely available
11  * to the public for use. The National Library of Medicine and the U.S.
12  * Government have not placed any restriction on its use or reproduction.
13  *
14  * Although all reasonable efforts have been taken to ensure the accuracy
15  * and reliability of the software and data, the NLM and the U.S.
16  * Government do not and cannot warrant the performance or results that
17  * may be obtained by using this software or data. The NLM and the U.S.
18  * Government disclaim all warranties, express or implied, including
19  * warranties of performance, merchantability or fitness for any particular
20  * purpose.
21  *
22  * Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Authors: Mike DiCuccio
27  *
28  * File Description:
29  *
30  */
31 
32 #include <ncbi_pch.hpp>
33 #include <corelib/ncbiapp_api.hpp>
34 #include <corelib/ncbidll.hpp>
36 #include <corelib/error_codes.hpp>
37 
38 #include <windows.h>
39 
40 #include <dbghelp.h>
41 
42 
43 #define NCBI_USE_ERRCODE_X Corelib_Stack
44 
45 
47 
48 
49 #define lenof(a) (sizeof(a) / sizeof((a)[0]))
50 #define MAXNAMELEN 1024 // max name length for found symbols
51 #define IMGSYMLEN (sizeof IMAGEHLP_SYMBOL)
52 #define TTBUFLEN 65535
53 
54 
56 {
61 };
62 
63 typedef vector<SModuleEntry> TModules;
64 typedef TModules::iterator ModuleListIter;
65 
66 
67 // miscellaneous toolhelp32 declarations; we cannot #include the header
68 // because not all systems may have it
69 #define MAX_MODULE_NAME32 255
70 #define TH32CS_SNAPMODULE 0x00000008
71 #pragma pack(push, 8)
72 typedef struct tagMODULEENTRY32
73 {
75  DWORD th32ModuleID; // This module
76  DWORD th32ProcessID; // owning process
77  DWORD GlblcntUsage; // Global usage count on the module
78  DWORD ProccntUsage; // Module usage count in th32ProcessID's context
79  BYTE* modBaseAddr; // Base address of module in th32ProcessID's context
80  DWORD modBaseSize; // Size in bytes of module starting at modBaseAddr
81  HMODULE hModule; // The hModule of this module in th32ProcessID's context
83  char szExePath[MAX_PATH];
85 
88 #pragma pack(pop)
89 
90 
91 static bool s_FillModuleListTH32(TModules& modules, DWORD pid)
92 {
93  // CreateToolhelp32Snapshot()
94  typedef HANDLE (__stdcall *FCreateToolhelp32Snapshot)(WORD dwFlags,
95  DWORD th32ProcessID);
96  // Module32First()
97  typedef BOOL (__stdcall *FModule32First)(HANDLE hSnapshot,
98  LPMODULEENTRY32 lpme);
99  // Module32Next()
100  typedef BOOL (__stdcall *FModule32Next)(HANDLE hSnapshot,
101  LPMODULEENTRY32 lpme);
102 
103  // I think the DLL is called tlhelp32.dll on Win9X, so we try both
104  FCreateToolhelp32Snapshot CreateToolhelp32Snapshot;
105  FModule32First Module32First;
106  FModule32Next Module32Next;
107 
108  try {
109  const char *dllname[] = {
110  "kernel32.dll",
111  "tlhelp32.dll",
112  NULL
113  };
114 
115  const char* libname = dllname[0];
116  unique_ptr<CDll> dll;
117  while (libname) {
118  dll.reset(new CDll(libname, CDll::eLoadNow, CDll::eAutoUnload));
119 
120  CreateToolhelp32Snapshot =
121  dll->GetEntryPoint_Func("CreateToolhelp32Snapshot",
122  &CreateToolhelp32Snapshot);
123  Module32First =
124  dll->GetEntryPoint_Func("Module32First", &Module32First);
125  Module32Next =
126  dll->GetEntryPoint_Func("Module32Next", &Module32Next);
127 
128  if (CreateToolhelp32Snapshot && Module32First && Module32Next) {
129  break;
130  }
131  }
132 
133  if ( !CreateToolhelp32Snapshot ) {
134  NCBI_THROW(CCoreException, eCore,
135  "toolhelp32 functions not available");
136  }
137 
138  HANDLE handle = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
139  if (handle == (HANDLE) -1) {
140  NCBI_THROW(CCoreException, eCore,
141  "failed to create toolhelp32 snapshot");
142  }
143 
144  MODULEENTRY32 me;
145  me.dwSize = sizeof(MODULEENTRY32);
146  bool done = !Module32First(handle, &me) ? true : false;
147  while ( !done ) {
148  // here, we have a filled-in MODULEENTRY32
149  SModuleEntry e;
150  e.imageName = me.szExePath;
151  e.moduleName = me.szModule;
152  e.baseAddress = (DWORD) me.modBaseAddr;
153  e.size = me.modBaseSize;
154  modules.push_back(e);
155 
156  done = !Module32Next(handle, &me) ? true : false;
157  }
158 
159  CloseHandle(handle);
160 
161  return modules.size() != 0;
162  }
163  catch (exception& e) {
164  ERR_POST_X(1, Error << "Error retrieving toolhelp32 symbols: " << e.what());
165  }
166  catch (...) {
167  ERR_POST_X(2, Error << "Unknown error retrieving toolhelp32 symbols");
168  }
169 
170  return false;
171 }
172 
173 
174 // declarations for PSAPI
175 // this won't be present on all systems, so we include them here
176 
177 typedef struct _MODULEINFO {
182 
183 
184 static bool s_FillModuleListPSAPI(TModules& mods, DWORD pid, HANDLE hProcess)
185 {
186  try {
187  // EnumProcessModules()
188  typedef BOOL (__stdcall *FEnumProcessModules)(HANDLE hProcess,
189  HMODULE *lphModule,
190  DWORD cb,
191  LPDWORD lpcbNeeded);
192  // GetModuleFileNameEx()
193  typedef DWORD (__stdcall *FGetModuleFileNameExA)(HANDLE hProcess,
194  HMODULE hModule,
195  LPSTR lpFilename,
196  DWORD nSize);
197  // GetModuleBaseName() -- redundant, as GMFNE() has the same prototype, but who cares?
198  typedef DWORD (__stdcall *FGetModuleBaseNameA)(HANDLE hProcess,
199  HMODULE hModule,
200  LPSTR lpFilename,
201  DWORD nSize);
202  // GetModuleInformation()
203  typedef BOOL (__stdcall *FGetModuleInformation)(HANDLE hProcess,
204  HMODULE hModule,
205  LPMODULEINFO pmi,
206  DWORD nSize);
207 
208  FEnumProcessModules EnumProcessModules;
209  FGetModuleFileNameExA GetModuleFileNameExA;
210  FGetModuleBaseNameA GetModuleBaseNameA;
211  FGetModuleInformation GetModuleInformation;
212 
213  mods.clear();
214  CDll dll("psapi.dll", CDll::eLoadNow, CDll::eAutoUnload);
215 
216  EnumProcessModules =
217  dll.GetEntryPoint_Func("EnumProcessModules",
218  &EnumProcessModules);
219  GetModuleFileNameExA =
220  dll.GetEntryPoint_Func("GetModuleFileNameExA",
221  &GetModuleFileNameExA);
222  GetModuleBaseNameA =
223  dll.GetEntryPoint_Func("GetModuleBaseNameA",
224  &GetModuleBaseNameA);
225  GetModuleInformation =
226  dll.GetEntryPoint_Func("GetModuleInformation",
227  &GetModuleInformation);
228 
229  if ( !EnumProcessModules ||
230  !GetModuleFileNameExA ||
231  !GetModuleBaseNameA ||
232  !GetModuleInformation ) {
233  return false;
234  }
235 
236  vector<HMODULE> modules;
237  modules.resize(4096);
238 
239  string tmp;
240  DWORD needed;
241  if ( !EnumProcessModules(hProcess,
242  &modules[0],
243  modules.size()*sizeof(HMODULE),
244  &needed) ) {
245  NCBI_THROW(CCoreException, eCore,
246  "EnumProcessModules() failed");
247  }
248 
249  if ( needed > modules.size() * sizeof(HMODULE)) {
250  NCBI_THROW(CCoreException, eCore,
251  string("More than ") +
252  NStr::SizetToString(modules.size()) + " modules");
253  }
254 
255 
256  needed /= sizeof(HMODULE);
257  for (size_t i = 0; i < needed; ++i) {
258  // for each module, get:
259  // base address, size
260  MODULEINFO mi;
261  GetModuleInformation(hProcess, modules[i], &mi, sizeof(mi));
262 
263  SModuleEntry e;
264  e.baseAddress = (DWORD) mi.lpBaseOfDll;
265  e.size = mi.SizeOfImage;
266 
267  // image file name
268  char tt[2048];
269  tt[0] = '\0';
270  GetModuleFileNameExA(hProcess, modules[i], tt, sizeof(tt));
271  e.imageName = tt;
272 
273  // module name
274  tt[0] = '\0';
275  GetModuleBaseNameA(hProcess, modules[i], tt, sizeof(tt));
276  e.moduleName = tt;
277 
278  mods.push_back(e);
279  }
280 
281  return true;
282  }
283  catch (exception& e) {
284  ERR_POST_X(3, Error << "Error getting PSAPI symbols: " << e.what());
285  }
286  catch (...) {
287  }
288 
289  return false;
290 }
291 
292 
293 static bool s_FillModuleList(TModules& modules, DWORD pid, HANDLE hProcess)
294 {
295  // try toolhelp32 first
296  if (s_FillModuleListTH32(modules, pid)) {
297  return true;
298  }
299  // nope? try psapi, then
300  return s_FillModuleListPSAPI(modules, pid, hProcess);
301 }
302 
303 
305 {
306 public:
307  CSymbolGuard(void);
308  ~CSymbolGuard(void);
309 
310  void UpdateSymbols(void);
311 private:
312  // Remember loaded modules
314 
316 };
317 
318 
320 {
321  // our current process and thread within that process
322  HANDLE curr_proc = GetCurrentProcess();
323 
324  try {
325  string search_path(CDir::GetCwd());
326  string tmp;
327  tmp.resize(2048);
328  if (GetModuleFileNameA(0, const_cast<char*>(tmp.data()),
329  tmp.length())) {
330  string::size_type pos = tmp.find_last_of("\\/");
331  if (pos != string::npos) {
332  tmp.erase(pos);
333  }
334  search_path = tmp + ';' + search_path;
335  }
336 
337  const char* ptr = getenv("_NT_SYMBOL_PATH");
338  if (ptr) {
339  string tmp(ptr);
340  search_path = tmp + ';' + search_path;
341  }
342  ptr = getenv("_NT_ALTERNATE_SYMBOL_PATH");
343  if (ptr) {
344  string tmp(ptr);
345  search_path = tmp + ';' + search_path;
346  }
347  ptr = getenv("SYSTEMROOT");
348  if (ptr) {
349  string tmp(ptr);
350  search_path = tmp + ';' + search_path;
351  }
352 
353  // init symbol handler stuff (SymInitialize())
354  if ( !SymInitialize(curr_proc,
355  const_cast<char*>(search_path.c_str()),
356  false) ) {
357  NCBI_THROW(CCoreException, eCore, "SymInitialize() failed");
358  }
359 
360  // set up default options
361  DWORD symOptions = SymGetOptions();
362  symOptions |= SYMOPT_LOAD_LINES;
363  symOptions &= ~SYMOPT_UNDNAME;
364  SymSetOptions(symOptions);
365 
366  // pre-load our symbols
367  UpdateSymbols();
368  }
369  catch (exception& e) {
370  ERR_POST_X(4, Error << "Error loading symbols for stack trace: "
371  << e.what());
372  }
373  catch (...) {
374  ERR_POST_X(5, Error
375  << "Unknown error initializing symbols for stack trace.");
376  }
377 }
378 
379 
381 {
382  SymCleanup(GetCurrentProcess());
383 }
384 
385 
387 {
388  HANDLE proc = GetCurrentProcess();
389  DWORD pid = GetCurrentProcessId();
390 
391  // Enumerate modules and tell dbghelp.dll about them.
392  // On NT, this is not necessary, but it won't hurt.
393  TModules modules;
394 
395  // fill in module list
396  s_FillModuleList(modules, pid, proc);
397 
398  NON_CONST_ITERATE (TModules, it, modules) {
399  TLoadedModules::const_iterator module = m_Loaded.find(it->moduleName);
400  if (module != m_Loaded.end()) {
401  continue;
402  }
403  DWORD module_addr = SymLoadModule(proc, 0,
404  const_cast<char*>(it->imageName.c_str()),
405  const_cast<char*>(it->moduleName.c_str()),
406  it->baseAddress, it->size);
407  if ( !module_addr ) {
408  ERR_POST_X(6, Error << "Error loading symbols for module: "
409  << it->moduleName);
410  } else {
411  _TRACE("Loaded symbols from " << it->moduleName);
412  m_Loaded.insert(it->moduleName);
413  }
414  }
415 }
416 
417 
419 
420 
421 class CStackTraceImpl
422 {
423 public:
426 
428 
429 private:
430  typedef STACKFRAME TStackFrame;
431  typedef vector<TStackFrame> TStack;
432 
433  TStack m_Stack;
434 };
435 
436 
437 #define GET_CURRENT_CONTEXT(c, contextFlags) \
438  do { \
439  memset(&c, 0, sizeof(CONTEXT)); \
440  c.ContextFlags = contextFlags; \
441  __asm call x \
442  __asm x: pop eax \
443  __asm mov c.Eip, eax \
444  __asm mov c.Ebp, ebp \
445  __asm mov c.Esp, esp \
446  } while(0)
447 
448 
449 #pragma warning( push )
450 #pragma warning( disable : 4748)
451 
453 {
454  HANDLE curr_proc = GetCurrentProcess();
455  HANDLE thread = GetCurrentThread();
456 
457  // we could use ImageNtHeader() here instead
458  DWORD img_type = IMAGE_FILE_MACHINE_I386;
459 
460  // init CONTEXT record so we know where to start the stackwalk
461  CONTEXT c;
462  GET_CURRENT_CONTEXT(c, CONTEXT_FULL);
463 
464  // current stack frame
465  STACKFRAME s;
466  memset(&s, 0, sizeof s);
467  s.AddrPC.Offset = c.Eip;
468  s.AddrPC.Mode = AddrModeFlat;
469  s.AddrFrame.Offset = c.Ebp;
470  s.AddrFrame.Mode = AddrModeFlat;
471  s.AddrStack.Offset = c.Esp;
472  s.AddrStack.Mode = AddrModeFlat;
473 
474  try {
475  unsigned int max_depth = CStackTrace::s_GetStackTraceMaxDepth();
476  for (size_t frame = 0; frame <= max_depth; ++frame) {
477  // get next stack frame
478  if ( !StackWalk(img_type, curr_proc, thread, &s, &c, NULL,
479  SymFunctionTableAccess,
480  SymGetModuleBase,
481  NULL) ) {
482  break;
483  }
484 
485  // Discard the top frames describing current function
486  if (frame < 1) {
487  continue;
488  }
489 
490  if ( !s.AddrPC.Offset ) {
491  continue;
492  }
493 
494  m_Stack.push_back(s);
495  }
496  }
497  catch (exception& e) {
498  ERR_POST_X(7, Error << "Error getting stack trace: " << e.what());
499  }
500  catch (...) {
501  ERR_POST_X(8, Error << "Unknown error getting stack trace");
502  }
503 }
504 
505 #pragma warning( pop )
506 
507 
509 {
510 }
511 
512 
514 {
515  if ( m_Stack.empty() ) {
516  return;
517  }
518 
519  s_SymbolGuard->UpdateSymbols();
520 
521  HANDLE curr_proc = GetCurrentProcess();
522 
523  IMAGEHLP_SYMBOL *pSym = NULL;
524  pSym = (IMAGEHLP_SYMBOL *) malloc(IMGSYMLEN + MAXNAMELEN);
525  memset(pSym, 0, IMGSYMLEN + MAXNAMELEN);
526  pSym->SizeOfStruct = IMGSYMLEN;
527  pSym->MaxNameLength = MAXNAMELEN;
528 
529  IMAGEHLP_LINE Line;
530  memset(&Line, 0, sizeof Line);
531  Line.SizeOfStruct = sizeof(Line);
532 
533  IMAGEHLP_MODULE Module;
534  memset(&Module, 0, sizeof Module);
535  Module.SizeOfStruct = sizeof(Module);
536 
537  DWORD offs = 0;
538 
539  try {
540  ITERATE(TStack, it, m_Stack) {
541  CStackTrace::SStackFrameInfo sf_info((void*)it->AddrPC.Offset);
542  sf_info.func = "<cannot get function name for this address>";
543 
544  if ( !SymGetSymFromAddr(curr_proc,
545  it->AddrPC.Offset,
546  &offs,
547  pSym) ) {
548  stack.push_back(sf_info);
549  continue;
550  }
551  sf_info.offs = offs;
552 
553  // retrieve function names, if we can
554  //char undName[MAXNAMELEN];
555  char undFullName[MAXNAMELEN];
556  //UnDecorateSymbolName(pSym->Name, undName,
557  // MAXNAMELEN, UNDNAME_NAME_ONLY);
558  UnDecorateSymbolName(pSym->Name, undFullName,
559  MAXNAMELEN, UNDNAME_COMPLETE);
560 
561  sf_info.func = undFullName;
562 
563  // retrieve file and line number info
564  if (SymGetLineFromAddr(curr_proc,
565  it->AddrPC.Offset,
566  &offs,
567  &Line)) {
568  sf_info.file = Line.FileName;
569  sf_info.line = Line.LineNumber;
570  } else {
571  _TRACE("failed to get line number for " << sf_info.func);
572  }
573 
574  // get library info, if it is available
575  if ( !SymGetModuleInfo(curr_proc,
576  it->AddrPC.Offset,
577  &Module) ) {
578  ERR_POST_X(10, Error << "failed to get module info for "
579  << sf_info.func);
580  } else {
581  sf_info.module = Module.ModuleName;
582  sf_info.module += "[";
583  sf_info.module += Module.ImageName;
584  sf_info.module += "]";
585  }
586 
587  stack.push_back(sf_info);
588  }
589  }
590  catch (exception& e) {
591  ERR_POST_X(11, Error << "Error getting stack trace: " << e.what());
592  }
593  catch (...) {
594  ERR_POST_X(12, Error << "Unknown error getting stack trace");
595  }
596 
597  free(pSym);
598 }
599 
600 
CCoreException –.
Definition: ncbiexpt.hpp:1476
CDll –.
Definition: ncbidll.hpp:107
CSafeStatic<>::
~CStackTraceImpl(void)
backward::StackTrace TStack
void Expand(CStackTrace::TStack &stack)
vector< TStackFrame > TStack
list< SStackFrameInfo > TStack
Definition: ncbi_stack.hpp:78
static unsigned int s_GetStackTraceMaxDepth(void)
Definition: ncbi_stack.cpp:158
TLoadedModules m_Loaded
void UpdateSymbols(void)
set< string > TLoadedModules
Definition: svg.hpp:432
Module manipulator.
Definition: ncbiexpt.hpp:1218
iterator_bool insert(const value_type &val)
Definition: set.hpp:149
const_iterator find(const key_type &key) const
Definition: set.hpp:137
const_iterator end() const
Definition: set.hpp:136
parent_type::const_iterator const_iterator
Definition: set.hpp:79
#define true
Definition: bool.h:35
static const char * proc
Definition: stats.c:21
static char tmp[3200]
Definition: utf8.c:42
char * LPSTR
Definition: sqlfront.h:34
void * LPVOID
Definition: sqlfront.h:38
unsigned char BYTE
Definition: sybdb.h:334
#define ITERATE(Type, Var, Cont)
ITERATE macro to sequence through container elements.
Definition: ncbimisc.hpp:815
#define NON_CONST_ITERATE(Type, Var, Cont)
Non constant version of ITERATE macro.
Definition: ncbimisc.hpp:822
string
Definition: cgiapp.hpp:690
#define NULL
Definition: ncbistd.hpp:225
#define _TRACE(message)
Definition: ncbidbg.hpp:122
#define ERR_POST_X(err_subcode, message)
Error posting with default error code and given error subcode.
Definition: ncbidiag.hpp:550
TFunc GetEntryPoint_Func(const string &name, TFunc *func)
Get DLLs entry point (function).
Definition: ncbidll.hpp:273
@ eAutoUnload
Definition: ncbidll.hpp:144
@ eLoadNow
Definition: ncbidll.hpp:138
void Error(CExceptionArgs_Base &args)
Definition: ncbiexpt.hpp:1197
#define NCBI_THROW(exception_class, err_code, message)
Generic macro to throw an exception, given the exception class, error code and message string.
Definition: ncbiexpt.hpp:704
static string GetCwd(void)
Get the current working directory.
Definition: ncbifile.cpp:3708
#define END_NCBI_SCOPE
End previously defined NCBI scope.
Definition: ncbistl.hpp:103
#define BEGIN_NCBI_SCOPE
Define ncbi namespace.
Definition: ncbistl.hpp:100
static string SizetToString(size_t value, TNumToStringFlags flags=0, int base=10)
Convert size_t to string.
Definition: ncbistr.cpp:2742
#define HANDLE
An abstraction for a file handle.
Definition: mdb.c:383
Definition of all error codes used in corelib (xncbi.lib).
int i
BOOL(WINAPI * FModule32Next)(HANDLE hSnapshot, LPMODULEENTRY32_A lpme)
BOOL(WINAPI * FEnumProcessModules)(HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded)
DWORD(WINAPI * FGetModuleFileNameExA)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize)
BOOL(WINAPI * FModule32First)(HANDLE hSnapshot, LPMODULEENTRY32_A lpme)
Static variables safety - create on demand, destroy on application termination.
#define IMGSYMLEN
struct _MODULEINFO MODULEINFO
vector< SModuleEntry > TModules
static bool s_FillModuleListPSAPI(TModules &mods, DWORD pid, HANDLE hProcess)
MODULEENTRY32 * LPMODULEENTRY32
static bool s_FillModuleListTH32(TModules &modules, DWORD pid)
MODULEENTRY32 * PMODULEENTRY32
#define MAXNAMELEN
#define TH32CS_SNAPMODULE
static CSafeStatic< CSymbolGuard > s_SymbolGuard
#define GET_CURRENT_CONTEXT(c, contextFlags)
static bool s_FillModuleList(TModules &modules, DWORD pid, HANDLE hProcess)
struct _MODULEINFO * LPMODULEINFO
struct tagMODULEENTRY32 MODULEENTRY32
TModules::iterator ModuleListIter
#define MAX_MODULE_NAME32
Define class Dll and for Portable DLL handling.
#define BOOL
Definition: odbcinst.h:18
DWORD * LPDWORD
Definition: sqltypes.h:113
unsigned short WORD
Definition: sqltypes.h:94
unsigned int DWORD
Definition: sqltypes.h:98
Structure for holding stack trace data.
Definition: ncbi_stack.hpp:51
std::string moduleName
std::string imageName
char szExePath[MAX_PATH]
done
Definition: token1.c:1
void free(voidpf ptr)
voidp malloc(uInt size)
Modified on Fri Sep 20 14:58:00 2024 by modify_doxy.py rev. 669887