Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
wdbg_sym.cpp
Go to the documentation of this file.
1 /* Copyright (c) 2013 Wildfire Games
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining
4  * a copy of this software and associated documentation files (the
5  * "Software"), to deal in the Software without restriction, including
6  * without limitation the rights to use, copy, modify, merge, publish,
7  * distribute, sublicense, and/or sell copies of the Software, and to
8  * permit persons to whom the Software is furnished to do so, subject to
9  * the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 /*
24  * Win32 stack trace and symbol engine.
25  */
26 
27 #include "precompiled.h"
29 
30 #include <cstdlib>
31 #include <cstdio>
32 #include <set>
33 
34 #include "lib/byte_order.h" // movzx_le64
35 #include "lib/module_init.h"
36 #include "lib/sysdep/cpu.h"
37 #include "lib/debug_stl.h"
38 #include "lib/app_hooks.h"
40 #include "lib/sysdep/os/win/wdbg.h"
43 
45 
46 static WUTIL_FUNC(pRtlCaptureContext, VOID, (PCONTEXT));
47 
49 {
50  WUTIL_IMPORT_KERNEL32(RtlCaptureContext, pRtlCaptureContext);
51  return INFO::OK;
52 }
53 
54 
55 //----------------------------------------------------------------------------
56 // dbghelp
57 //----------------------------------------------------------------------------
58 
59 // global for convenience (we only support a single process)
61 
62 // for StackWalk64; taken from PE header by InitDbghelp.
63 static WORD machine;
64 
66 {
67  hProcess = GetCurrentProcess();
68 
70 
71  // set options
72  // notes:
73  // - can be done before SymInitialize; we do so in case
74  // any of the options affect it.
75  // - do not set directly - that would zero any existing flags.
76  DWORD opts = pSymGetOptions();
77  //opts |= SYMOPT_DEBUG; // lots of debug spew in output window
78  opts |= SYMOPT_DEFERRED_LOADS; // the "fastest, most efficient way"
79  opts |= SYMOPT_LOAD_LINES;
80  opts |= SYMOPT_UNDNAME;
81  pSymSetOptions(opts);
82 
83  // initialize dbghelp.
84  // .. request symbols from all currently active modules be loaded.
85  const BOOL fInvadeProcess = TRUE;
86  // .. use default *symbol* search path. we don't use this to locate
87  // our PDB file because its absolute path is stored inside the EXE.
88  const PWSTR UserSearchPath = 0;
89  WinScopedPreserveLastError s; // SymInitializeW
90  const BOOL ok = pSymInitializeW(hProcess, UserSearchPath, fInvadeProcess);
91  WARN_IF_FALSE(ok);
92 
93  HMODULE hModule = GetModuleHandle(0);
94  IMAGE_NT_HEADERS* const header = pImageNtHeader(hModule);
95  machine = header->FileHeader.Machine;
96 
97  return INFO::OK;
98 }
99 
100 // ensure dbghelp is initialized exactly once.
101 // call every time before dbghelp functions are used.
102 // (on-demand initialization allows handling exceptions raised before
103 // winit.cpp init functions are called)
104 //
105 // NB: this may take SECONDS if OS symbols are installed and
106 // symserv wants to access the internet.
107 static void sym_init()
108 {
109  static ModuleInitState initState;
110  ModuleInit(&initState, InitDbghelp);
111 }
112 
113 
114 static STACKFRAME64 PopulateStackFrame(CONTEXT& context)
115 {
116  STACKFRAME64 sf;
117  memset(&sf, 0, sizeof(sf));
118  sf.AddrPC.Mode = AddrModeFlat;
119  sf.AddrFrame.Mode = AddrModeFlat;
120  sf.AddrStack.Mode = AddrModeFlat;
121 #if ARCH_AMD64
122  sf.AddrPC.Offset = context.Rip;
123  sf.AddrFrame.Offset = context.Rbp;
124  sf.AddrStack.Offset = context.Rsp;
125 #else
126  sf.AddrPC.Offset = context.Eip;
127  sf.AddrFrame.Offset = context.Ebp;
128  sf.AddrStack.Offset = context.Esp;
129 #endif
130  return sf;
131 }
132 
133 
134 static IMAGEHLP_STACK_FRAME PopulateImageStackFrame(const STACKFRAME64& sf)
135 {
136  IMAGEHLP_STACK_FRAME isf;
137  memset(&isf, 0, sizeof(isf));
138  // apparently only PC, FP and SP are necessary, but
139  // we copy everything to be safe.
140  isf.InstructionOffset = sf.AddrPC.Offset;
141  isf.ReturnOffset = sf.AddrReturn.Offset;
142  isf.FrameOffset = sf.AddrFrame.Offset;
143  isf.StackOffset = sf.AddrStack.Offset;
144  isf.BackingStoreOffset = sf.AddrBStore.Offset;
145  isf.FuncTableEntry = (ULONG64)sf.FuncTableEntry;
146  // (note: array of different types, can't copy directly)
147  for(int i = 0; i < 4; i++)
148  isf.Params[i] = sf.Params[i];
149  // isf.Reserved - already zeroed
150  isf.Virtual = sf.Virtual;
151  // isf.Reserved2 - already zeroed
152  return isf;
153 }
154 
155 
156 struct SYMBOL_INFO_PACKAGEW2 : public SYMBOL_INFO_PACKAGEW
157 {
159  {
160  si.SizeOfStruct = sizeof(si);
161  si.MaxNameLen = MAX_SYM_NAME;
162  }
163 };
164 
165 #pragma pack(push, 1)
166 
167 // note: we can't derive from TI_FINDCHILDREN_PARAMS because its members
168 // aren't guaranteed to precede ours (although they do in practice).
170 {
172  {
173  p.Start = 0;
174  p.Count = std::min(numChildren, maxChildren);
175  }
176 
177  static const DWORD maxChildren = 300;
178  TI_FINDCHILDREN_PARAMS p;
180 };
181 
182 #pragma pack(pop)
183 
184 
185 // actual implementation; made available so that functions already under
186 // the lock don't have to unlock (slow) to avoid recursive locking.
187 static Status ResolveSymbol_lk(void* ptr_of_interest, wchar_t* sym_name, wchar_t* file, int* line)
188 {
189  sym_init();
190 
191  const DWORD64 addr = (DWORD64)ptr_of_interest;
192  size_t successes = 0;
193 
194  WinScopedPreserveLastError s; // SymFromAddrW, SymGetLineFromAddrW64
195 
196  // get symbol name (if requested)
197  if(sym_name)
198  {
199  sym_name[0] = '\0';
200 
202  SYMBOL_INFOW* sym = &sp.si;
203  if(pSymFromAddrW(hProcess, addr, 0, sym))
204  {
205  wcscpy_s(sym_name, DEBUG_SYMBOL_CHARS, sym->Name);
206  successes++;
207  }
208  }
209 
210  // get source file and/or line number (if requested)
211  if(file || line)
212  {
213  file[0] = '\0';
214  *line = 0;
215 
216  IMAGEHLP_LINEW64 line_info = { sizeof(IMAGEHLP_LINEW64) };
217  DWORD displacement; // unused but required by pSymGetLineFromAddr64!
218  if(pSymGetLineFromAddrW64(hProcess, addr, &displacement, &line_info))
219  {
220  if(file)
221  {
222  // strip full path down to base name only.
223  // this loses information, but that isn't expected to be a
224  // problem and is balanced by not having to do this from every
225  // call site (full path is too long to display nicely).
226  const wchar_t* basename = path_name_only(line_info.FileName);
227  wcscpy_s(file, DEBUG_FILE_CHARS, basename);
228  successes++;
229  }
230 
231  if(line)
232  {
233  *line = line_info.LineNumber;
234  successes++;
235  }
236  }
237  }
238 
239  if(addr == 0 && GetLastError() == ERROR_MOD_NOT_FOUND)
240  SetLastError(0);
241  if(GetLastError() == ERROR_INVALID_ADDRESS)
242  SetLastError(0);
243 
244  return (successes != 0)? INFO::OK : ERR::FAIL;
245 }
246 
247 // file is the base name only, not path (see rationale in wdbg_sym).
248 // the PDB implementation is rather slow (~500µs).
249 Status debug_ResolveSymbol(void* ptr_of_interest, wchar_t* sym_name, wchar_t* file, int* line)
250 {
252  return ResolveSymbol_lk(ptr_of_interest, sym_name, file, line);
253 }
254 
255 
256 //----------------------------------------------------------------------------
257 // stack walk
258 //----------------------------------------------------------------------------
259 
261 {
262  // there are 4 ways to do so, in order of preference:
263  // - RtlCaptureContext (only available on WinXP or above)
264  // - assembly language subroutine (complicates the build system)
265  // - intentionally raise an SEH exception and capture its context
266  // (causes annoying "first chance exception" messages and
267  // can't co-exist with WinScopedLock's destructor)
268  // - GetThreadContext while suspended (a bit tricky + slow).
269  // note: it used to be common practice to query the current thread
270  // context, but WinXP SP2 and above require it be suspended.
271 
272  if(!pRtlCaptureContext)
273  return ERR::NOT_SUPPORTED; // NOWARN
274 
275  CONTEXT* context = (CONTEXT*)pcontext;
276  cassert(sizeof(CONTEXT) <= DEBUG_CONTEXT_SIZE);
277  memset(context, 0, sizeof(CONTEXT));
278  context->ContextFlags = CONTEXT_FULL;
279  pRtlCaptureContext(context);
280  return INFO::OK;
281 }
282 
283 
284 static Status CallStackWalk(STACKFRAME64& sf, CONTEXT& context)
285 {
287 
288  SetLastError(0); // StackWalk64 doesn't always SetLastError
289  const HANDLE hThread = GetCurrentThread();
290  if(!pStackWalk64(machine, hProcess, hThread, &sf, &context, 0, pSymFunctionTableAccess64, pSymGetModuleBase64, 0))
291  return ERR::FAIL; // NOWARN (no stack frames left)
292 
293  // (the frame pointer can be zero despite StackWalk64 returning TRUE.)
294  if(sf.AddrFrame.Offset == 0)
295  return ERR::FAIL; // NOWARN (no stack frames left)
296 
297  // huge WTF in x64 debug builds (dbghelp 6.12.0002.633):
298  // AddrFrame.Offset doesn't match the correct RBP value.
299  // StackWalk64 updates the context [http://bit.ly/lo1aqZ] and
300  // its Rbp is correct, so we'll use that.
301 #if ARCH_AMD64
302  sf.AddrFrame.Offset = context.Rbp;
303 #endif
304 
305  return INFO::OK;
306 }
307 
308 
309 // NB: CaptureStackBackTrace may be faster (http://msinilo.pl/blog/?p=40),
310 // but wasn't known during development.
311 Status wdbg_sym_WalkStack(StackFrameCallback cb, uintptr_t cbData, CONTEXT& context, const wchar_t* lastFuncToSkip)
312 {
313  sym_init();
314 
315  STACKFRAME64 sf = PopulateStackFrame(context);
316 
317  wchar_t func[DEBUG_SYMBOL_CHARS];
318 
320  for(;;) // each stack frame:
321  {
322  if(CallStackWalk(sf, context) != INFO::OK)
323  return ret;
324 
325  if(lastFuncToSkip)
326  {
327  void* const pc = (void*)(uintptr_t)sf.AddrPC.Offset;
328  if(debug_ResolveSymbol(pc, func, 0, 0) == INFO::OK)
329  {
330  if(wcsstr(func, lastFuncToSkip)) // this was the last one to skip
331  lastFuncToSkip = 0;
332  continue;
333  }
334  }
335 
336  ret = cb(&sf, cbData);
338  }
339 }
340 
341 
342 void* debug_GetCaller(void* pcontext, const wchar_t* lastFuncToSkip)
343 {
344  struct StoreAddress
345  {
346  static Status Func(const STACKFRAME64* sf, uintptr_t cbData)
347  {
348  const uintptr_t funcAddress = sf->AddrPC.Offset;
349 
350  // store funcAddress in our `output parameter'
351  memcpy((void*)cbData, &funcAddress, sizeof(funcAddress));
352 
353  return INFO::OK;
354  }
355  };
356  void* func;
357  wdbg_assert(pcontext != 0);
358  Status ret = wdbg_sym_WalkStack(&StoreAddress::Func, (uintptr_t)&func, *(CONTEXT*)pcontext, lastFuncToSkip);
359  return (ret == INFO::OK)? func : 0;
360 }
361 
362 
363 //-----------------------------------------------------------------------------
364 // helper routines for symbol value dump
365 //-----------------------------------------------------------------------------
366 
367 // infinite recursion has never happened, but we check for it anyway.
368 static const size_t maxIndirection = 255;
369 static const size_t maxLevel = 255;
370 
371 struct DumpState
372 {
373  size_t level;
374  size_t indirection;
375  uintptr_t moduleBase;
376  LPSTACKFRAME64 stackFrame;
377 
378  DumpState(uintptr_t moduleBase, LPSTACKFRAME64 stackFrame)
379  : level(0), indirection(0), moduleBase(moduleBase), stackFrame(stackFrame)
380  {
381  }
382 };
383 
384 
385 //----------------------------------------------------------------------------
386 
387 static size_t out_chars_left;
388 static wchar_t* out_pos;
389 
390 // (only warn once until next out_init to avoid flood of messages.)
392 
393 // some top-level (*) symbols cause tons of output - so much that they may
394 // single-handedly overflow the buffer (e.g. pointer to a tree of huge UDTs).
395 // we can't have that, so there is a limit in place as to how much a
396 // single top-level symbol can output. after that is reached, dumping is
397 // aborted for that symbol but continues for the subsequent top-level symbols.
398 //
399 // this is implemented as follows: dump_sym_cb latches the current output
400 // position; each dump_sym (through which all symbols go) checks if the
401 // new position exceeds the limit and aborts if so.
402 // slight wrinkle: since we don't want each level of UDTs to successively
403 // realize the limit has been hit and display the error message, we
404 // return ERR::SYM_SINGLE_SYMBOL_LIMIT once and thereafter INFO::SYM_SUPPRESS_OUTPUT.
405 //
406 // * example: local variables, as opposed to child symbols in a UDT.
407 static wchar_t* out_latched_pos;
409 
410 static void out_init(wchar_t* buf, size_t max_chars)
411 {
412  out_pos = buf;
413  out_chars_left = max_chars;
414  out_have_warned_of_overflow = false;
415  out_have_warned_of_limit = false;
416 }
417 
418 
419 static void out(const wchar_t* fmt, ...)
420 {
421  va_list args;
422  va_start(args, fmt);
423  // use vswprintf, not vswprintf_s, because we want to gracefully
424  // handle buffer overflows
425  int len = vswprintf(out_pos, out_chars_left, fmt, args);
426  va_end(args);
427 
428  // success
429  if(len >= 0)
430  {
431  out_pos += len;
432  // make sure out_chars_left remains nonnegative
433  if((size_t)len > out_chars_left)
434  {
435  DEBUG_WARN_ERR(ERR::LOGIC); // apparently wrote more than out_chars_left
436  len = (int)out_chars_left;
437  }
438  out_chars_left -= len;
439  }
440  // no more room left
441  else
442  {
443  // the buffer really is full yet out_chars_left may not be 0
444  // (since it isn't updated if vswprintf returns -1).
445  // must be set so subsequent calls don't try to squeeze stuff in.
446  out_chars_left = 0;
447 
448  // write a warning into the output buffer (once) so it isn't
449  // abruptly cut off (which looks like an error)
450  if(!out_have_warned_of_overflow)
451  {
452  out_have_warned_of_overflow = true;
453 
454  // with the current out_pos / out_chars_left variables, there's
455  // no way of knowing where the buffer actually ends. no matter;
456  // we'll just put the warning before out_pos and eat into the
457  // second newest text.
458  const wchar_t text[] = L"(no more room in buffer)";
459  wcscpy_s(out_pos-ARRAY_SIZE(text), ARRAY_SIZE(text), text); // safe
460  }
461  }
462 }
463 
464 
465 static void out_erase(size_t num_chars)
466 {
467  // don't do anything if end of buffer was hit (prevents repeatedly
468  // scribbling over the last few bytes).
469  if(out_have_warned_of_overflow)
470  return;
471 
472  out_chars_left += (ssize_t)num_chars;
473  out_pos -= num_chars;
474  *out_pos = '\0';
475  // make sure it's 0-terminated in case there is no further output.
476 }
477 
478 
479 // (see above)
480 static void out_latch_pos()
481 {
482  out_have_warned_of_limit = false;
483  out_latched_pos = out_pos;
484 }
485 
486 
487 // (see above)
489 {
490  if(out_have_warned_of_limit)
492  if(out_pos - out_latched_pos > 3000) // ~30 lines
493  {
494  out_have_warned_of_limit = true;
495  return ERR::SYM_SINGLE_SYMBOL_LIMIT; // NOWARN
496  }
497 
498  // no limit hit, proceed normally
499  return INFO::OK;
500 }
501 
502 //----------------------------------------------------------------------------
503 
504 #define INDENT STMT(for(size_t i__ = 0; i__ <= state.level; i__++) out(L" ");)
505 #define UNINDENT STMT(out_erase((state.level+1)*4);)
506 
507 
508 // does it look like an ASCII string is located at <addr>?
509 // set <stride> to 2 to search for WCS-2 strings (of western characters!).
510 // called by dump_sequence for its string special-case.
511 //
512 // algorithm: scan the "string" and count # text chars vs. garbage.
513 static bool is_string(const u8* p, size_t stride)
514 {
515  // note: access violations are caught by dump_sym; output is "?".
516  int score = 0;
517  for(;;)
518  {
519  // current character is:
520  const int c = *p & 0xff; // prevent sign extension
521  p += stride;
522  // .. text
523  if(isalnum(c))
524  score += 5;
525  // .. end of string
526  else if(!c)
527  break;
528  // .. garbage
529  else if(!isprint(c))
530  score -= 4;
531 
532  // got enough information either way => done.
533  // (we don't want to unnecessarily scan huge binary arrays)
534  if(abs(score) >= 10)
535  break;
536  }
537 
538  return (score > 0);
539 }
540 
541 
542 
543 
544 // forward decl; called by dump_sequence and some of dump_sym_*.
545 static Status dump_sym(DWORD id, const u8* p, DumpState& state);
546 
547 // from cvconst.h
548 //
549 // rationale: we don't provide a get_register routine, since only the
550 // value of FP is known to dump_frame_cb (via STACKFRAME64).
551 // displaying variables stored in registers is out of the question;
552 // all we can do is display FP-relative variables.
554 {
557 };
558 
559 
560 static void dump_error(Status err)
561 {
562  switch(err)
563  {
564  case 0:
565  // no error => no output
566  break;
568  out(L"(too much output; skipping to next top-level symbol)");
569  break;
571  out(L"(unavailable - located in another module)");
572  break;
574  out(L"(unavailable)");
575  break;
577  out(L"(unavailable - type info request failed (GLE=%d))", GetLastError());
578  break;
580  out(L"(unavailable - internal error)\r\n");
581  break;
583  // not an error; do not output anything. handled by caller.
584  break;
585  default:
586  out(L"(unavailable - unspecified error 0x%X (%d))", err, err);
587  break;
588  }
589 }
590 
591 
592 // moved out of dump_sequence.
593 static Status dump_string(const u8* p, size_t el_size)
594 {
595  // not char or wchar_t string
596  if(el_size != sizeof(char) && el_size != sizeof(wchar_t))
597  return INFO::CANNOT_HANDLE;
598  // not text
599  if(!is_string(p, el_size))
600  return INFO::CANNOT_HANDLE;
601 
602  wchar_t buf[512];
603  if(el_size == sizeof(wchar_t))
604  {
605  wcsncpy(buf, (const wchar_t*)p, ARRAY_SIZE(buf)); // can't use wcscpy_s because p might be too long
606  wcscpy_s(buf+ARRAY_SIZE(buf)-4, 4, L"..."); // ensure null-termination
607  }
608  // convert to wchar_t
609  else
610  {
611  size_t i;
612  for(i = 0; i < ARRAY_SIZE(buf)-1; i++)
613  {
614  buf[i] = (wchar_t)p[i];
615  if(buf[i] == '\0')
616  break;
617  }
618  buf[i] = '\0';
619  }
620 
621  out(L"\"%ls\"", buf);
622  return INFO::OK;
623 }
624 
625 
626 // moved out of dump_sequence.
627 static void seq_determine_formatting(size_t el_size, size_t el_count, bool* fits_on_one_line, size_t* num_elements_to_show)
628 {
629  if(el_size == sizeof(char))
630  {
631  *fits_on_one_line = el_count <= 16;
632  *num_elements_to_show = std::min((size_t)16u, el_count);
633  }
634  else if(el_size <= sizeof(int))
635  {
636  *fits_on_one_line = el_count <= 8;
637  *num_elements_to_show = std::min((size_t)12u, el_count);
638  }
639  else
640  {
641  *fits_on_one_line = false;
642  *num_elements_to_show = std::min((size_t)8u, el_count);
643  }
644 
645  // make sure empty containers are displayed with [0] {}, otherwise
646  // the lack of output looks like an error.
647  if(!el_count)
648  *fits_on_one_line = true;
649 }
650 
651 
652 static Status dump_sequence(DebugStlIterator el_iterator, void* internal, size_t el_count, DWORD el_type_id, size_t el_size, DumpState& state)
653 {
654  const u8* el_p = 0; // avoid "uninitialized" warning
655 
656  // special case: display as a string if the sequence looks to be text.
657  // do this only if container isn't empty because the otherwise the
658  // iterator may crash.
659  if(el_count)
660  {
661  el_p = el_iterator(internal, el_size);
662 
663  Status ret = dump_string(el_p, el_size);
664  if(ret == INFO::OK)
665  return ret;
666  }
667 
668  // choose formatting based on element size and count
669  bool fits_on_one_line;
670  size_t num_elements_to_show;
671  seq_determine_formatting(el_size, el_count, &fits_on_one_line, &num_elements_to_show);
672 
673  out(L"[%d] ", el_count);
674  state.level++;
675  out(fits_on_one_line? L"{ " : L"\r\n");
676 
677  for(size_t i = 0; i < num_elements_to_show; i++)
678  {
679  if(!fits_on_one_line)
680  INDENT;
681 
682  Status err = dump_sym(el_type_id, el_p, state);
683  el_p = el_iterator(internal, el_size);
684 
685  // there was no output for this child; undo its indentation (if any),
686  // skip everything below and proceed with the next child.
687  if(err == INFO::SYM_SUPPRESS_OUTPUT)
688  {
689  if(!fits_on_one_line)
690  UNINDENT;
691  continue;
692  }
693 
694  dump_error(err); // nop if err == INFO::OK
695  // add separator unless this is the last element (can't just
696  // erase below due to additional "...").
697  if(i != num_elements_to_show-1)
698  out(fits_on_one_line? L", " : L"\r\n");
699 
701  break;
702  } // for each child
703 
704  // indicate some elements were skipped
705  if(el_count != num_elements_to_show)
706  out(L" ...");
707 
708  state.level--;
709  if(fits_on_one_line)
710  out(L" }");
711  return INFO::OK;
712 }
713 
714 
715 static const u8* array_iterator(void* internal, size_t el_size)
716 {
717  const u8*& pos = *(const u8**)internal;
718  const u8* cur_pos = pos;
719  pos += el_size;
720  return cur_pos;
721 }
722 
723 
724 static Status dump_array(const u8* p, size_t el_count, DWORD el_type_id, size_t el_size, DumpState& state)
725 {
726  const u8* iterator_internal_pos = p;
727  return dump_sequence(array_iterator, &iterator_internal_pos,
728  el_count, el_type_id, el_size, state);
729 }
730 
731 
733 {
734  switch(dataKind)
735  {
736  case DataIsMember:
737  // address is already correct (udt_dump_normal retrieved the offset;
738  // we do it that way so we can check it against the total
739  // UDT size for safety) and SymFromIndex would fail
740  return INFO::SKIPPED;
741 
742  case DataIsUnknown:
744 
745  case DataIsStaticMember:
746  // this symbol is defined as static in another module =>
747  // there's nothing we can do.
748  return ERR::SYM_UNRETRIEVABLE_STATIC; // NOWARN
749 
750  case DataIsLocal:
751  case DataIsStaticLocal:
752  case DataIsParam:
753  case DataIsObjectPtr:
754  case DataIsFileStatic:
755  case DataIsGlobal:
756  case DataIsConstant:
757  // ok, can handle
758  return INFO::OK;
759  }
760 
761  WARN_RETURN(ERR::LOGIC); // UNREACHABLE
762 }
763 
764 static bool IsRelativeToFramePointer(DWORD flags, DWORD reg)
765 {
766  if(flags & SYMFLAG_FRAMEREL) // note: this is apparently obsolete
767  return true;
768  if((flags & SYMFLAG_REGREL) == 0)
769  return false;
770  if(reg == CV_REG_EBP || reg == CV_AMD64_RBP)
771  return true;
772  return false;
773 }
774 
775 static bool IsUnretrievable(DWORD flags)
776 {
777  // note: it is unlikely that the crashdump register context
778  // contains the correct values for this scope, so symbols
779  // stored in or relative to a general register are unavailable.
780  if(flags & SYMFLAG_REGISTER)
781  return true;
782 
783  // note: IsRelativeToFramePointer is called first, so if we still
784  // see this flag, the base register is not the frame pointer.
785  // since we most probably don't know its value in the current
786  // scope (see above), the symbol is inaccessible.
787  if(flags & SYMFLAG_REGREL)
788  return true;
789 
790  return false;
791 }
792 
793 static Status DetermineSymbolAddress(DWORD id, const SYMBOL_INFOW* sym, const DumpState& state, const u8** pp)
794 {
795  DWORD dataKind;
796  if(!pSymGetTypeInfo(hProcess, state.moduleBase, id, TI_GET_DATAKIND, &dataKind))
798  Status ret = CanHandleDataKind(dataKind);
800  if(ret == INFO::SKIPPED)
801  return INFO::OK; // pp is already correct
802 
803  // note: we have not yet observed a non-zero TI_GET_ADDRESSOFFSET or
804  // TI_GET_ADDRESS, and TI_GET_OFFSET is apparently equal to sym->Address.
805 
806  // get address
807  uintptr_t addr = (uintptr_t)sym->Address;
808  if(IsRelativeToFramePointer(sym->Flags, sym->Register))
809  addr += (uintptr_t)state.stackFrame->AddrFrame.Offset;
810  else if(IsUnretrievable(sym->Flags))
811  return ERR::SYM_UNRETRIEVABLE; // NOWARN
812 
813  *pp = (const u8*)(uintptr_t)addr;
814 
815  debug_printf(L"SYM| %ls at %p flags=%X dk=%d sym->addr=%I64X fp=%I64x\n", sym->Name, *pp, sym->Flags, dataKind, sym->Address, state.stackFrame->AddrFrame.Offset);
816  return INFO::OK;
817 }
818 
819 
820 //-----------------------------------------------------------------------------
821 // dump routines for each dbghelp symbol type
822 //-----------------------------------------------------------------------------
823 
824 // these functions return != 0 if they're not able to produce any
825 // reasonable output at all; the caller (dump_sym_data, dump_sequence, etc.)
826 // will display the appropriate error message via dump_error.
827 // called by dump_sym; lock is held.
828 
829 static Status dump_sym_array(DWORD type_id, const u8* p, DumpState& state)
830 {
831  ULONG64 size64 = 0;
832  if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_LENGTH, &size64))
834  const size_t size = (size_t)size64;
835 
836  // get element count and size
837  DWORD el_type_id = 0;
838  if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_TYPEID, &el_type_id))
840  // .. workaround: TI_GET_COUNT returns total struct size for
841  // arrays-of-struct. therefore, calculate as size / el_size.
842  ULONG64 el_size_;
843  if(!pSymGetTypeInfo(hProcess, state.moduleBase, el_type_id, TI_GET_LENGTH, &el_size_))
845  const size_t el_size = (size_t)el_size_;
846  ENSURE(el_size != 0);
847  const size_t num_elements = size/el_size;
848  ENSURE(num_elements != 0);
849 
850  return dump_array(p, num_elements, el_type_id, el_size, state);
851 }
852 
853 
854 //-----------------------------------------------------------------------------
855 
856 // if the current value is a printable character, display in that form.
857 // this isn't only done in btChar because characters are sometimes stored
858 // in integers.
860 {
861  if(data < 0x100)
862  {
863  int c = (int)data;
864  if(isprint(c))
865  out(L" ('%hc')", c);
866  }
867 }
868 
869 
870 static Status dump_sym_base_type(DWORD type_id, const u8* p, DumpState& state)
871 {
872  DWORD base_type;
873  if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_BASETYPE, &base_type))
875  ULONG64 size64 = 0;
876  if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_LENGTH, &size64))
878  const size_t size = (size_t)size64;
879 
880  // single out() call. note: we pass a single u64 for all sizes,
881  // which will only work on little-endian systems.
882  // must be declared before goto to avoid W4 warning.
883  const wchar_t* fmt = L"";
884 
885  u64 data = movzx_le64(p, size);
886  // if value is 0xCC..CC (uninitialized mem), we display as hex.
887  // the output would otherwise be garbage; this makes it obvious.
888  // note: be very careful to correctly handle size=0 (e.g. void*).
889  for(size_t i = 0; i < size; i++)
890  {
891  if(p[i] != 0xCC)
892  break;
893  if(i == size-1)
894  {
895  out(L"(uninitialized)");
896  return INFO::OK;
897  }
898  }
899 
900  switch(base_type)
901  {
902  // floating-point
903  case btFloat:
904  if(size == sizeof(float))
905  {
906  // NB: the C calling convention calls for float arguments to be
907  // converted to double. passing `data' wouldn't work because it's
908  // merely a zero-extended 32-bit representation of the float.
909  float value;
910  memcpy(&value, p, sizeof(value));
911  out(L"%f (0x%08I64X)", value, data);
912  }
913  else if(size == sizeof(double))
914  out(L"%g (0x%016I64X)", data, data);
915  else
916  DEBUG_WARN_ERR(ERR::LOGIC); // invalid float size
917  break;
918 
919  // boolean
920  case btBool:
921  ENSURE(size == sizeof(bool));
922  if(data == 0 || data == 1)
923  out(L"%ls", data? L"true " : L"false");
924  else
925  out(L"(bool)0x%02I64X", data);
926  break;
927 
928  // integers (displayed as decimal and hex)
929  // note: 0x00000000 can get annoying (0 would be nicer),
930  // but it indicates the variable size and makes for consistently
931  // formatted structs/arrays. (0x1234 0 0x5678 is ugly)
932  case btInt:
933  case btLong:
934  case btUInt:
935  case btULong:
936  if(size == 1)
937  {
938  // _TUCHAR
939  if(state.indirection)
940  {
941  state.indirection = 0;
942  return dump_array(p, 8, type_id, size, state);
943  }
944  fmt = L"%I64d (0x%02I64X)";
945  }
946  else if(size == 2)
947  fmt = L"%I64d (0x%04I64X)";
948  else if(size == 4)
949  fmt = L"%I64d (0x%08I64X)";
950  else if(size == 8)
951  fmt = L"%I64d (0x%016I64X)";
952  else
953  DEBUG_WARN_ERR(ERR::LOGIC); // invalid size for integers
954  out(fmt, data, data);
955  break;
956 
957  // character
958  case btChar:
959  case btWChar:
960  ENSURE(size == sizeof(char) || size == sizeof(wchar_t));
961  // char*, wchar_t*
962  if(state.indirection)
963  {
964  state.indirection = 0;
965  return dump_array(p, 8, type_id, size, state);
966  }
967  out(L"%d", data);
969  break;
970 
971  // note: void* is sometimes indicated as (pointer, btNoType).
972  case btVoid:
973  case btNoType:
974  // void* - cannot display what it's pointing to (type unknown).
975  if(state.indirection)
976  {
977  out_erase(4); // " -> "
978  fmt = L"";
979  }
980  else
981  DEBUG_WARN_ERR(ERR::LOGIC); // non-pointer btVoid or btNoType
982  break;
983 
984  default:
985  DEBUG_WARN_ERR(ERR::LOGIC); // unknown type
986  break;
987 
988  // unsupported complex types
989  case btBCD:
990  case btCurrency:
991  case btDate:
992  case btVariant:
993  case btComplex:
994  case btBit:
995  case btBSTR:
996  case btHresult:
997  return ERR::SYM_UNSUPPORTED; // NOWARN
998  }
999 
1000  return INFO::OK;
1001 }
1002 
1003 
1004 //-----------------------------------------------------------------------------
1005 
1006 static Status dump_sym_base_class(DWORD type_id, const u8* p, DumpState& state)
1007 {
1008  DWORD base_class_type_id;
1009  if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_TYPEID, &base_class_type_id))
1011 
1012  // this is a virtual base class. we can't display those because it'd
1013  // require reading the VTbl, which is difficult given lack of documentation
1014  // and just not worth it.
1015  DWORD vptr_ofs;
1016  if(pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_VIRTUALBASEPOINTEROFFSET, &vptr_ofs))
1017  return ERR::SYM_UNSUPPORTED; // NOWARN
1018 
1019  return dump_sym(base_class_type_id, p, state);
1020 }
1021 
1022 
1023 //-----------------------------------------------------------------------------
1024 
1025 static Status dump_sym_data(DWORD id, const u8* p, DumpState& state)
1026 {
1028  SYMBOL_INFOW* sym = &sp.si;
1029  if(!pSymFromIndexW(hProcess, state.moduleBase, id, sym))
1031 
1032  out(L"%ls = ", sym->Name);
1033 
1034  __try
1035  {
1036  RETURN_STATUS_IF_ERR(DetermineSymbolAddress(id, sym, state, &p));
1037  // display value recursively
1038  return dump_sym(sym->TypeIndex, p, state);
1039  }
1040  __except(EXCEPTION_EXECUTE_HANDLER)
1041  {
1042  return ERR::SYM_INTERNAL_ERROR; // NOWARN
1043  }
1044 }
1045 
1046 
1047 //-----------------------------------------------------------------------------
1048 
1049 static Status dump_sym_enum(DWORD type_id, const u8* p, DumpState& state)
1050 {
1051  ULONG64 size64 = 0;
1052  if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_LENGTH, &size64))
1054  const size_t size = (size_t)size64;
1055 
1056  const i64 enum_value = movsx_le64(p, size);
1057 
1058  // get array of child symbols (enumerants).
1059  DWORD numChildren;
1060  if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_CHILDRENCOUNT, &numChildren))
1062  TI_FINDCHILDREN_PARAMS2 fcp(numChildren);
1063  if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_FINDCHILDREN, &fcp))
1065  numChildren = fcp.p.Count; // was truncated to maxChildren
1066  const DWORD* children = fcp.p.ChildId;
1067 
1068  // for each child (enumerant):
1069  for(size_t i = 0; i < numChildren; i++)
1070  {
1071  DWORD child_data_id = children[i];
1072 
1073  // get this enumerant's value. we can't make any assumptions about
1074  // the variant's type or size - no restriction is documented.
1075  // rationale: VariantChangeType is much less tedious than doing
1076  // it manually and guarantees we cover everything. the OLE DLL is
1077  // already pulled in by e.g. OpenGL anyway.
1078  VARIANT v;
1079  if(!pSymGetTypeInfo(hProcess, state.moduleBase, child_data_id, TI_GET_VALUE, &v))
1081  if(VariantChangeType(&v, &v, 0, VT_I8) != S_OK)
1082  continue;
1083 
1084  // it's the one we want - output its name.
1085  if(enum_value == v.llVal)
1086  {
1087  const wchar_t* name;
1088  if(!pSymGetTypeInfo(hProcess, state.moduleBase, child_data_id, TI_GET_SYMNAME, &name))
1090  out(L"%ls", name);
1091  LocalFree((HLOCAL)name);
1092  return INFO::OK;
1093  }
1094  }
1095 
1096  // we weren't able to retrieve a matching enum value, but can still
1097  // produce reasonable output (the numeric value).
1098  // note: could goto here after a SGTI fails, but we fail instead
1099  // to make sure those errors are noticed.
1100  out(L"%I64d", enum_value);
1101  return INFO::OK;
1102 }
1103 
1104 
1105 //-----------------------------------------------------------------------------
1106 
1108 {
1110 }
1111 
1112 
1113 //-----------------------------------------------------------------------------
1114 
1116 {
1117  // this symbol gives class parent, return type, and parameter count.
1118  // unfortunately the one thing we care about, its name,
1119  // isn't exposed via TI_GET_SYMNAME, so we resolve it ourselves.
1120 
1121  wchar_t name[DEBUG_SYMBOL_CHARS];
1122  Status err = ResolveSymbol_lk((void*)p, name, 0, 0);
1123 
1124  if(state.indirection == 0)
1125  out(L"0x%p ", p);
1126  if(err == INFO::OK)
1127  out(L"(%ls)", name);
1128  return INFO::OK;
1129 }
1130 
1131 
1132 //-----------------------------------------------------------------------------
1133 
1134 // do not follow pointers that we have already displayed. this reduces
1135 // clutter a bit and prevents infinite recursion for cyclical references
1136 // (e.g. via struct S { S* p; } s; s.p = &s;)
1137 
1138 // note: allocating memory dynamically would cause trouble if dumping
1139 // the stack from within memory-related code (the allocation hook would
1140 // be reentered, which is not permissible).
1141 
1142 static const size_t maxVisited = 1000;
1143 static const u8* visited[maxVisited];
1144 static size_t numVisited;
1145 
1146 static void ptr_reset_visited()
1147 {
1148  numVisited = 0;
1149 }
1150 
1151 static bool ptr_already_visited(const u8* p)
1152 {
1153  for(size_t i = 0; i < numVisited; i++)
1154  {
1155  if(visited[i] == p)
1156  return true;
1157  }
1158 
1159  if(numVisited < maxVisited)
1160  {
1161  visited[numVisited] = p;
1162  numVisited++;
1163  }
1164  // capacity exceeded
1165  else
1166  {
1167  // warn user - but only once (we can't use the regular
1168  // debug_DisplayError and wdbg_assert doesn't have a
1169  // suppress mechanism)
1170  static bool haveComplained;
1171  if(!haveComplained)
1172  {
1173  debug_printf(L"WARNING: ptr_already_visited: capacity exceeded, increase maxVisited\n");
1174  debug_break();
1175  haveComplained = true;
1176  }
1177  }
1178 
1179  return false;
1180 }
1181 
1182 
1183 static Status dump_sym_pointer(DWORD type_id, const u8* p, DumpState& state)
1184 {
1185  ULONG64 size64 = 0;
1186  if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_LENGTH, &size64))
1188  const size_t size = (size_t)size64;
1189 
1190  // read+output pointer's value.
1191  p = (const u8*)(uintptr_t)movzx_le64(p, size);
1192  out(L"0x%p", p);
1193 
1194  // bail if it's obvious the pointer is bogus
1195  // (=> can't display what it's pointing to)
1196  if(debug_IsPointerBogus(p))
1197  return INFO::OK;
1198 
1199  // avoid duplicates and circular references
1200  if(ptr_already_visited(p))
1201  {
1202  out(L" (see above)");
1203  return INFO::OK;
1204  }
1205 
1206  // display what the pointer is pointing to.
1207  // if the pointer is invalid (despite "bogus" check above),
1208  // dump_data_sym recovers via SEH and prints an error message.
1209  // if the pointed-to value turns out to uninteresting (e.g. void*),
1210  // the responsible dump_sym* will erase "->", leaving only address.
1211  out(L" -> ");
1212  if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_TYPEID, &type_id))
1214 
1215  // prevent infinite recursion just to be safe (shouldn't happen)
1216  if(state.indirection >= maxIndirection)
1218  state.indirection++;
1219  return dump_sym(type_id, p, state);
1220 }
1221 
1222 
1223 //-----------------------------------------------------------------------------
1224 
1225 
1226 static Status dump_sym_typedef(DWORD type_id, const u8* p, DumpState& state)
1227 {
1228  if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_TYPEID, &type_id))
1230  return dump_sym(type_id, p, state);
1231 }
1232 
1233 
1234 //-----------------------------------------------------------------------------
1235 
1236 
1237 // determine type and size of the given child in a UDT.
1238 // useful for UDTs that contain typedefs describing their contents,
1239 // e.g. value_type in STL containers.
1240 static Status udt_get_child_type(const wchar_t* child_name, ULONG numChildren, const DWORD* children, const DumpState& state, DWORD* el_type_id, size_t* el_size)
1241 {
1242  const DWORD lastError = GetLastError();
1243 
1244  *el_type_id = 0;
1245  *el_size = 0;
1246 
1247  for(ULONG i = 0; i < numChildren; i++)
1248  {
1249  const DWORD child_id = children[i];
1250 
1252  SYMBOL_INFOW* sym = &sp.si;
1253  if(!pSymFromIndexW(hProcess, state.moduleBase, child_id, sym))
1254  {
1255  // this happens for several UDTs; cause is unknown.
1256  ENSURE(GetLastError() == ERROR_NOT_FOUND);
1257  continue;
1258  }
1259  if(!wcscmp(sym->Name, child_name))
1260  {
1261  *el_type_id = sym->TypeIndex;
1262  *el_size = (size_t)sym->Size;
1263  return INFO::OK;
1264  }
1265  }
1266 
1267  SetLastError(lastError);
1268 
1269  // (happens if called for containers that are treated as STL but are not)
1270  return ERR::SYM_CHILD_NOT_FOUND; // NOWARN
1271 }
1272 
1273 
1274 static Status udt_dump_std(const wchar_t* type_name, const u8* p, size_t size, DumpState& state, ULONG numChildren, const DWORD* children)
1275 {
1276  Status err;
1277 
1278  // not a C++ standard library object; can't handle it.
1279  if(wcsncmp(type_name, L"std::", 5) != 0)
1280  return INFO::CANNOT_HANDLE;
1281 
1282  // check for C++ objects that should be displayed via udt_dump_normal.
1283  // C++03 containers are special-cased and the rest (apart from those here)
1284  // are ignored, because for the most part they are spew.
1285  if(!wcsncmp(type_name, L"std::pair", 9) ||
1286  !wcsncmp(type_name, L"std::tr1::", 10))
1287  return INFO::CANNOT_HANDLE;
1288 
1289  // display contents of STL containers
1290  // .. get element type
1291  DWORD el_type_id;
1292  size_t el_size;
1293  err = udt_get_child_type(L"value_type", numChildren, children, state, &el_type_id, &el_size);
1294  if(err != INFO::OK)
1295  goto not_valid_container;
1296  // .. get iterator and # elements
1297  size_t el_count;
1298  DebugStlIterator el_iterator;
1300  err = debug_stl_get_container_info(type_name, p, size, el_size, &el_count, &el_iterator, it_mem);
1301  if(err != INFO::OK)
1302  goto not_valid_container;
1303  return dump_sequence(el_iterator, it_mem, el_count, el_type_id, el_size, state);
1304 not_valid_container:
1305 
1306  // build and display detailed "error" message.
1307  wchar_t buf[100];
1308  const wchar_t* text;
1309  // .. object named std::* but doesn't include a "value_type" child =>
1310  // it's a non-STL C++ stdlib object. wasn't handled by the
1311  // special case above, so we just display its simplified type name
1312  // (the contents are usually spew).
1313  if(err == ERR::SYM_CHILD_NOT_FOUND)
1314  text = L"";
1315  // .. not one of the containers we can analyse.
1316  else if(err == ERR::STL_CNT_UNKNOWN)
1317  text = L"unknown ";
1318  else if(err == ERR::STL_CNT_UNSUPPORTED)
1319  text = L"unsupported ";
1320  // .. container of a known type but contents are invalid.
1321  else if(err == ERR::STL_CNT_INVALID)
1322  text = L"uninitialized/invalid ";
1323  // .. some other error encountered
1324  else
1325  {
1326  wchar_t description[200];
1327  (void)StatusDescription(err, description, ARRAY_SIZE(description));
1328  swprintf_s(buf, ARRAY_SIZE(buf), L"error \"%ls\" while analyzing ", description);
1329  text = buf;
1330  }
1331 
1332  // (debug_stl modifies its input string in-place; type_name is
1333  // a const string returned by dbghelp)
1334  wchar_t type_name_buf[DEBUG_SYMBOL_CHARS];
1335  wcscpy_s(type_name_buf, ARRAY_SIZE(type_name_buf), type_name);
1336 
1337  out(L"(%ls%ls)", text, debug_stl_simplify_name(type_name_buf));
1338  return INFO::OK;
1339 }
1340 
1341 
1342 static bool udt_should_suppress(const wchar_t* type_name)
1343 {
1344  // specialized HANDLEs are defined as pointers to structs by
1345  // DECLARE_HANDLE. we only want the numerical value (pointer address),
1346  // so prevent these structs from being displayed.
1347  // note: no need to check for indirection; these are only found in
1348  // HANDLEs (which are pointers).
1349  // removed obsolete defs: HEVENT, HFILE, HUMPD
1350  if(type_name[0] != 'H')
1351  goto not_handle;
1352 #define SUPPRESS_HANDLE(name) if(!wcscmp(type_name, L#name L"__")) return true;
1353  SUPPRESS_HANDLE(HACCEL);
1354  SUPPRESS_HANDLE(HBITMAP);
1355  SUPPRESS_HANDLE(HBRUSH);
1356  SUPPRESS_HANDLE(HCOLORSPACE);
1357  SUPPRESS_HANDLE(HCURSOR);
1358  SUPPRESS_HANDLE(HDC);
1359  SUPPRESS_HANDLE(HENHMETAFILE);
1360  SUPPRESS_HANDLE(HFONT);
1361  SUPPRESS_HANDLE(HGDIOBJ);
1362  SUPPRESS_HANDLE(HGLOBAL);
1363  SUPPRESS_HANDLE(HGLRC);
1364  SUPPRESS_HANDLE(HHOOK);
1365  SUPPRESS_HANDLE(HICON);
1366  SUPPRESS_HANDLE(HIMAGELIST);
1367  SUPPRESS_HANDLE(HIMC);
1369  SUPPRESS_HANDLE(HKEY);
1370  SUPPRESS_HANDLE(HKL);
1371  SUPPRESS_HANDLE(HKLOCAL);
1372  SUPPRESS_HANDLE(HMENU);
1373  SUPPRESS_HANDLE(HMETAFILE);
1374  SUPPRESS_HANDLE(HMODULE);
1375  SUPPRESS_HANDLE(HMONITOR);
1376  SUPPRESS_HANDLE(HPALETTE);
1377  SUPPRESS_HANDLE(HPEN);
1378  SUPPRESS_HANDLE(HRGN);
1379  SUPPRESS_HANDLE(HRSRC);
1380  SUPPRESS_HANDLE(HSTR);
1381  SUPPRESS_HANDLE(HTASK);
1382  SUPPRESS_HANDLE(HWINEVENTHOOK);
1383  SUPPRESS_HANDLE(HWINSTA);
1384  SUPPRESS_HANDLE(HWND);
1385 not_handle:
1386 
1387  return false;
1388 }
1389 
1390 
1391 static Status udt_dump_suppressed(const wchar_t* type_name, const u8* UNUSED(p), size_t UNUSED(size), DumpState state, ULONG UNUSED(numChildren), const DWORD* UNUSED(children))
1392 {
1393  if(!udt_should_suppress(type_name))
1394  return INFO::CANNOT_HANDLE;
1395 
1396  // the data symbol is pointer-to-UDT. since we won't display its
1397  // contents, leave only the pointer's value.
1398  if(state.indirection)
1399  out_erase(4); // " -> "
1400 
1401  // indicate something was deliberately left out
1402  // (otherwise, lack of output may be taken for an error)
1403  out(L" (..)");
1404 
1405  return INFO::OK;
1406 }
1407 
1408 
1409 // (by now) non-trivial heuristic to determine if a UDT should be
1410 // displayed on one line or several. split out of udt_dump_normal.
1411 static bool udt_fits_on_one_line(const wchar_t* type_name, size_t child_count, size_t total_size)
1412 {
1413  // special case: always put CStr* on one line
1414  // (std::*string are displayed directly, but these go through
1415  // udt_dump_normal. we want to avoid the ensuing 3-line output)
1416  if(!wcscmp(type_name, L"CStr") || !wcscmp(type_name, L"CStr8") || !wcscmp(type_name, L"CStrW"))
1417  return true;
1418 
1419  // try to get actual number of relevant children
1420  // (typedefs etc. are never displayed, but are included in child_count.
1421  // we have to balance that vs. tons of static members, which aren't
1422  // reflected in total_size).
1423  // .. prevent division by 0.
1424  if(child_count == 0)
1425  child_count = 1;
1426  // special-case a few types that would otherwise be classified incorrectly
1427  // (due to having more or less than expected relevant children)
1428  if(!wcsncmp(type_name, L"std::pair", 9))
1429  child_count = 2;
1430 
1431  const size_t avg_size = total_size / child_count;
1432  // (if 0, no worries - child_count will probably be large and
1433  // we return false, which is a safe default)
1434 
1435  // small UDT with a few (small) members: fits on one line.
1436  if(child_count <= 3 && avg_size <= sizeof(int))
1437  return true;
1438 
1439  return false;
1440 }
1441 
1442 
1443 static Status udt_dump_normal(const wchar_t* type_name, const u8* p, size_t size, DumpState state, ULONG numChildren, const DWORD* children)
1444 {
1445  // special case: boost::unordered types are complex and may cause a stack overflow
1446  // see http://trac.wildfiregames.com/ticket/1813
1447  // TODO: at least give some info about them
1448  if(!wcsncmp(type_name, L"boost::unordered", 16))
1449  return INFO::CANNOT_HANDLE;
1450 
1451  const bool fits_on_one_line = udt_fits_on_one_line(type_name, numChildren, size);
1452 
1453  // prevent infinite recursion just to be safe (shouldn't happen)
1454  if(state.level >= maxLevel)
1456  state.level++;
1457 
1458  out(fits_on_one_line? L"{ " : L"\r\n");
1459 
1460  bool displayed_anything = false;
1461  for(ULONG i = 0; i < numChildren; i++)
1462  {
1463  const DWORD child_id = children[i];
1464 
1465  // get offset. if not available, skip this child
1466  // (we only display data here, not e.g. typedefs)
1467  DWORD ofs = 0;
1468  if(!pSymGetTypeInfo(hProcess, state.moduleBase, child_id, TI_GET_OFFSET, &ofs))
1469  continue;
1470  if(ofs >= size)
1471  {
1472  debug_printf(L"INVALID_UDT %ls %d %d\n", type_name, ofs, size);
1473  }
1474  //ENSURE(ofs < size);
1475 
1476  if(!fits_on_one_line)
1477  INDENT;
1478 
1479  const u8* el_p = p+ofs;
1480  Status err = dump_sym(child_id, el_p, state);
1481 
1482  // there was no output for this child; undo its indentation (if any),
1483  // skip everything below and proceed with the next child.
1484  if(err == INFO::SYM_SUPPRESS_OUTPUT)
1485  {
1486  if(!fits_on_one_line)
1487  UNINDENT;
1488  continue;
1489  }
1490 
1491  displayed_anything = true;
1492  dump_error(err); // nop if err == INFO::OK
1493  out(fits_on_one_line? L", " : L"\r\n");
1494 
1495  if(err == ERR::SYM_SINGLE_SYMBOL_LIMIT)
1496  break;
1497  } // for each child
1498 
1499  state.level--;
1500 
1501  if(!displayed_anything)
1502  {
1503  out_erase(2); // "{ " or "\r\n"
1504  out(L"(%ls)", type_name);
1505  return INFO::OK;
1506  }
1507 
1508  // remove trailing comma separator
1509  // note: we can't avoid writing it by checking if i == numChildren-1:
1510  // each child might be the last valid data member.
1511  if(fits_on_one_line)
1512  {
1513  out_erase(2); // ", "
1514  out(L" }");
1515  }
1516 
1517  return INFO::OK;
1518 }
1519 
1520 
1521 static Status dump_sym_udt(DWORD type_id, const u8* p, DumpState& state)
1522 {
1523  ULONG64 size64 = 0;
1524  if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_LENGTH, &size64))
1526  const size_t size = (size_t)size64;
1527 
1528  // get array of child symbols (members/functions/base classes).
1529  DWORD numChildren;
1530  if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_CHILDRENCOUNT, &numChildren))
1532  TI_FINDCHILDREN_PARAMS2 fcp(numChildren);
1533  if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_FINDCHILDREN, &fcp))
1535  numChildren = fcp.p.Count; // was truncated to maxChildren
1536  const DWORD* children = fcp.p.ChildId;
1537 
1538  const wchar_t* type_name;
1539  if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_SYMNAME, &type_name))
1541 
1542  Status ret;
1543  // note: order is important (e.g. STL special-case must come before
1544  // suppressing UDTs, which tosses out most other C++ stdlib classes)
1545 
1546  ret = udt_dump_std (type_name, p, size, state, numChildren, children);
1547  if(ret != INFO::CANNOT_HANDLE)
1548  goto done;
1549 
1550  ret = udt_dump_suppressed(type_name, p, size, state, numChildren, children);
1551  if(ret != INFO::CANNOT_HANDLE)
1552  goto done;
1553 
1554  ret = udt_dump_normal (type_name, p, size, state, numChildren, children);
1555  if(ret != INFO::CANNOT_HANDLE)
1556  goto done;
1557 
1558 done:
1559  LocalFree((HLOCAL)type_name);
1560  return ret;
1561 }
1562 
1563 
1564 //-----------------------------------------------------------------------------
1565 
1566 
1567 static Status dump_sym_vtable(DWORD UNUSED(type_id), const u8* UNUSED(p), DumpState& UNUSED(state))
1568 {
1569  // unsupported (vtable internals are undocumented; too much work).
1571 }
1572 
1573 
1574 //-----------------------------------------------------------------------------
1575 
1576 
1577 static Status dump_sym_unknown(DWORD type_id, const u8* UNUSED(p), DumpState& state)
1578 {
1579  // redundant (already done in dump_sym), but this is rare.
1580  DWORD type_tag;
1581  if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_SYMTAG, &type_tag))
1583 
1584  debug_printf(L"SYM: unknown tag: %d\n", type_tag);
1585  out(L"(unknown symbol type)");
1586  return INFO::OK;
1587 }
1588 
1589 
1590 //-----------------------------------------------------------------------------
1591 
1592 typedef Status (*DumpFunc)(DWORD typeId, const u8* p, DumpState& state);
1593 
1595 {
1596  switch(typeTag)
1597  {
1598  case SymTagArrayType:
1599  return dump_sym_array;
1600  case SymTagBaseType:
1601  return dump_sym_base_type;
1602  case SymTagBaseClass:
1603  return dump_sym_base_class;
1604  case SymTagData:
1605  return dump_sym_data;
1606  case SymTagEnum:
1607  return dump_sym_enum;
1608  case SymTagFunction:
1609  return dump_sym_function;
1610  case SymTagFunctionType:
1611  return dump_sym_function_type;
1612  case SymTagPointerType:
1613  return dump_sym_pointer;
1614  case SymTagTypedef:
1615  return dump_sym_typedef;
1616  case SymTagUDT:
1617  return dump_sym_udt;
1618  case SymTagVTable:
1619  return dump_sym_vtable;
1620  default:
1621  return dump_sym_unknown;
1622  }
1623 }
1624 
1625 
1626 // write name and value of the symbol <type_id> to the output buffer.
1627 // delegates to dump_sym_* depending on the symbol's tag.
1628 static Status dump_sym(DWORD type_id, const u8* p, DumpState& state)
1629 {
1631 
1632  DWORD typeTag;
1633  if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_SYMTAG, &typeTag))
1635  const DumpFunc dumpFunc = DumpFuncFromTypeTag(typeTag);
1636  return dumpFunc(type_id, p, state);
1637 }
1638 
1639 
1640 //-----------------------------------------------------------------------------
1641 // stack trace
1642 //-----------------------------------------------------------------------------
1643 
1644 static bool ShouldSkipSymbol(const wchar_t* name)
1645 {
1646  if(!wcscmp(name, L"suppress__"))
1647  return true;
1648  if(!wcscmp(name, L"__profile"))
1649  return true;
1650  return false;
1651 }
1652 
1653 // output the symbol's name and value via dump_sym*.
1654 // called from dump_frame_cb for each local symbol; lock is held.
1655 static BOOL CALLBACK dump_sym_cb(SYMBOL_INFOW* sym, ULONG UNUSED(size), PVOID userContext)
1656 {
1657  if(ShouldSkipSymbol(sym->Name))
1658  return TRUE; // continue
1659 
1660  out_latch_pos(); // see decl
1661  const u8* p = (const u8*)(uintptr_t)sym->Address;
1662  DumpState state((uintptr_t)sym->ModBase, (LPSTACKFRAME64)userContext);
1663 
1664  INDENT;
1665  Status err = dump_sym(sym->Index, p, state);
1666  dump_error(err);
1667  if(err == INFO::SYM_SUPPRESS_OUTPUT)
1668  UNINDENT;
1669  else
1670  out(L"\r\n");
1671 
1672  return TRUE; // continue
1673 }
1674 
1675 // called by wdbg_sym_WalkStack for each stack frame
1676 static Status dump_frame_cb(const STACKFRAME64* sf, uintptr_t UNUSED(userContext))
1677 {
1678  void* func = (void*)(uintptr_t)sf->AddrPC.Offset;
1679 
1680  wchar_t func_name[DEBUG_SYMBOL_CHARS]; wchar_t file[DEBUG_FILE_CHARS]; int line;
1681  Status ret = ResolveSymbol_lk(func, func_name, file, &line);
1682  if(ret == INFO::OK)
1683  {
1684  // don't trace back further than the app's entry point
1685  // (no one wants to see this frame). checking for the
1686  // function name isn't future-proof, but not stopping is no big deal.
1687  // an alternative would be to check if module=kernel32, but
1688  // that would cut off callbacks as well.
1689  // note: the stdcall mangled name includes parameter size, which is
1690  // different in 64-bit, so only check the first characters.
1691  if(!wcsncmp(func_name, L"_BaseProcessStart", 17) ||
1692  !wcscmp(func_name, L"BaseThreadInitThunk"))
1693  return INFO::OK;
1694 
1695  // skip any mainCRTStartup frames
1696  if(!wcscmp(func_name, L"__tmainCRTStartup"))
1697  return INFO::OK;
1698  if(!wcscmp(func_name, L"mainCRTStartup"))
1699  return INFO::OK;
1700 
1701  out(L"%ls (%ls:%d)\r\n", func_name, file, line);
1702  }
1703  else
1704  out(L"%p\r\n", func);
1705 
1706  WinScopedPreserveLastError s; // SymSetContext
1707 
1708  // only enumerate symbols for this stack frame
1709  // (i.e. its locals and parameters)
1710  // problem: debug info is scope-aware, so we won't see any variables
1711  // declared in sub-blocks. we'd have to pass an address in that block,
1712  // which isn't worth the trouble.
1713  IMAGEHLP_STACK_FRAME isf = PopulateImageStackFrame(*sf);
1714  const PIMAGEHLP_CONTEXT ic = 0; // ignored
1715  // NB: this sometimes fails for reasons unknown in a static
1716  // member function, possibly because the return address is in kernel32
1717  (void)pSymSetContext(hProcess, &isf, ic);
1718 
1719  const ULONG64 base = 0; const wchar_t* const mask = 0; // use scope set by pSymSetContext
1720  pSymEnumSymbolsW(hProcess, base, mask, dump_sym_cb, (PVOID)sf);
1721 
1722  if(GetLastError() == ERROR_NOT_SUPPORTED) // no debug info present?
1723  SetLastError(0);
1724 
1725  out(L"\r\n");
1726  return INFO::OK;
1727 }
1728 
1729 
1730 Status debug_DumpStack(wchar_t* buf, size_t maxChars, void* pcontext, const wchar_t* lastFuncToSkip)
1731 {
1732  static intptr_t busy;
1733  if(!cpu_CAS(&busy, 0, 1))
1734  return ERR::REENTERED; // NOWARN
1735 
1736  out_init(buf, maxChars);
1738 
1739  wdbg_assert(pcontext != 0);
1740  Status ret = wdbg_sym_WalkStack(dump_frame_cb, 0, *(CONTEXT*)pcontext, lastFuncToSkip);
1741 
1743  busy = 0;
1744 
1745  return ret;
1746 }
1747 
1748 
1749 //-----------------------------------------------------------------------------
1750 
1751 // write out a "minidump" containing register and stack state; this enables
1752 // examining the crash in a debugger. called by wdbg_exception_filter.
1753 // heavily modified from http://www.codeproject.com/debug/XCrashReportPt3.asp
1754 // lock must be held.
1755 void wdbg_sym_WriteMinidump(EXCEPTION_POINTERS* exception_pointers)
1756 {
1757  sym_init();
1758 
1759  WinScopedLock lock(WDBG_SYM_CS);
1760 
1761  OsPath path = ah_get_log_dir()/"crashlog.dmp";
1762  HANDLE hFile = CreateFileW(OsString(path).c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, 0, CREATE_ALWAYS, 0, 0);
1763  if(hFile == INVALID_HANDLE_VALUE)
1764  {
1765  DEBUG_DISPLAY_ERROR(L"wdbg_sym_WriteMinidump: unable to create crashlog.dmp.");
1766  return;
1767  }
1768 
1769  MINIDUMP_EXCEPTION_INFORMATION mei;
1770  mei.ThreadId = GetCurrentThreadId();
1771  mei.ExceptionPointers = exception_pointers;
1772  mei.ClientPointers = FALSE;
1773  // exception_pointers is not in our address space.
1774 
1775  // note: we don't store other crashlog info within the dump file
1776  // (UserStreamParam), since we will need to generate a plain text file on
1777  // non-Windows platforms. users will just have to send us both files.
1778 
1779  HANDLE hProcess = GetCurrentProcess();
1780  DWORD pid = GetCurrentProcessId();
1781  if(!pMiniDumpWriteDump || !pMiniDumpWriteDump(hProcess, pid, hFile, MiniDumpNormal, &mei, 0, 0))
1782  DEBUG_DISPLAY_ERROR(L"wdbg_sym_WriteMinidump: unable to generate minidump.");
1783 
1784  CloseHandle(hFile);
1785 }
#define RETURN_STATUS_FROM_CALLBACK(expression)
Definition: status.h:338
const Status STL_CNT_UNSUPPORTED
Definition: debug_stl.h:34
#define u8
Definition: types.h:39
Definition: dbghelp.h:80
const Status LOGIC
Definition: status.h:409
CV_HREG_e
Definition: wdbg_sym.cpp:553
static Status dump_sequence(DebugStlIterator el_iterator, void *internal, size_t el_count, DWORD el_type_id, size_t el_size, DumpState &state)
Definition: wdbg_sym.cpp:652
#define UNUSED(param)
mark a function parameter as unused and avoid the corresponding compiler warning. ...
static WUTIL_FUNC(pRtlCaptureContext, VOID,(PCONTEXT))
static Status CanHandleDataKind(DWORD dataKind)
Definition: wdbg_sym.cpp:732
Status wdbg_sym_WalkStack(StackFrameCallback cb, uintptr_t cbData, CONTEXT &context, const wchar_t *lastFuncToSkip)
Iterate over a call stack, invoking a callback for each frame encountered.
Definition: wdbg_sym.cpp:311
static bool out_have_warned_of_overflow
Definition: wdbg_sym.cpp:391
static Status udt_dump_suppressed(const wchar_t *type_name, const u8 *p, size_t size, DumpState state, ULONG numChildren, const DWORD *children)
Definition: wdbg_sym.cpp:1391
#define COMPILER_FENCE
prevent the compiler from reordering loads or stores across this point.
const Status SYM_UNSUPPORTED
Definition: debug.h:406
static const size_t DEBUG_SYMBOL_CHARS
Maximum number of characters (including null terminator) written to user&#39;s buffers by debug_ResolveSy...
Definition: debug.h:428
Definition: dbghelp.h:95
static Status dump_frame_cb(const STACKFRAME64 *sf, uintptr_t userContext)
Definition: wdbg_sym.cpp:1676
static bool udt_should_suppress(const wchar_t *type_name)
Definition: wdbg_sym.cpp:1342
const Status OK
Definition: status.h:386
some WinAPI functions SetLastError(0) on success, which is bad because it can hide previous errors...
Definition: wutil.h:119
const Status SYM_SUPPRESS_OUTPUT
Definition: debug.h:420
static void out_erase(size_t num_chars)
Definition: wdbg_sym.cpp:465
Definition: dbghelp.h:88
static BOOL CALLBACK dump_sym_cb(SYMBOL_INFOW *sym, ULONG size, PVOID userContext)
Definition: wdbg_sym.cpp:1655
#define SUPPRESS_HANDLE(name)
void wdbg_sym_WriteMinidump(EXCEPTION_POINTERS *exception_pointers)
Definition: wdbg_sym.cpp:1755
static DumpFunc DumpFuncFromTypeTag(DWORD typeTag)
Definition: wdbg_sym.cpp:1594
static Status dump_sym_unknown(DWORD type_id, const u8 *p, DumpState &state)
Definition: wdbg_sym.cpp:1577
static void out(const wchar_t *fmt,...)
Definition: wdbg_sym.cpp:419
const Status STL_CNT_INVALID
Definition: debug_stl.h:36
static Status dump_sym_array(DWORD type_id, const u8 *p, DumpState &state)
Definition: wdbg_sym.cpp:829
static Status udt_get_child_type(const wchar_t *child_name, ULONG numChildren, const DWORD *children, const DumpState &state, DWORD *el_type_id, size_t *el_size)
Definition: wdbg_sym.cpp:1240
static const size_t maxLevel
Definition: wdbg_sym.cpp:369
static bool out_have_warned_of_limit
Definition: wdbg_sym.cpp:408
Definition: dbghelp.h:87
static Status udt_dump_std(const wchar_t *type_name, const u8 *p, size_t size, DumpState &state, ULONG numChildren, const DWORD *children)
Definition: wdbg_sym.cpp:1274
static const size_t maxIndirection
Definition: wdbg_sym.cpp:368
static Status ResolveSymbol_lk(void *ptr_of_interest, wchar_t *sym_name, wchar_t *file, int *line)
Definition: wdbg_sym.cpp:187
#define UNINDENT
Definition: wdbg_sym.cpp:505
static void out_latch_pos()
Definition: wdbg_sym.cpp:480
static Status udt_dump_normal(const wchar_t *type_name, const u8 *p, size_t size, DumpState state, ULONG numChildren, const DWORD *children)
Definition: wdbg_sym.cpp:1443
Definition: dbghelp.h:91
void dbghelp_ImportFunctions()
DumpState(uintptr_t moduleBase, LPSTACKFRAME64 stackFrame)
Definition: wdbg_sym.cpp:378
static wchar_t * out_latched_pos
Definition: wdbg_sym.cpp:407
const Status SYM_CHILD_NOT_FOUND
Definition: debug.h:407
static ModuleInitState initState
Definition: h_mgr.cpp:742
int swprintf_s(wchar_t *buf, size_t max_chars, const wchar_t *fmt,...) WPRINTF_ARGS(3)
static Status DetermineSymbolAddress(DWORD id, const SYMBOL_INFOW *sym, const DumpState &state, const u8 **pp)
Definition: wdbg_sym.cpp:793
const Status CANNOT_HANDLE
Definition: status.h:396
int BOOL
Definition: wgl.h:51
wchar_t * debug_stl_simplify_name(wchar_t *name)
reduce complicated STL symbol names to human-readable form.
Definition: debug_stl.cpp:80
static HANDLE hProcess
Definition: wdbg_sym.cpp:60
static Status dump_sym_base_type(DWORD type_id, const u8 *p, DumpState &state)
Definition: wdbg_sym.cpp:870
i64 movsx_le64(const u8 *p, size_t size_bytes)
sign-extend &lt;size&gt; (truncated to 8) bytes of little-endian data to i64, starting at address &lt;p&gt; (need...
Definition: byte_order.cpp:190
static Status wdbg_sym_Init()
Definition: wdbg_sym.cpp:48
static void seq_determine_formatting(size_t el_size, size_t el_count, bool *fits_on_one_line, size_t *num_elements_to_show)
Definition: wdbg_sym.cpp:627
static bool ShouldSkipSymbol(const wchar_t *name)
Definition: wdbg_sym.cpp:1644
static Status dump_sym_function_type(DWORD type_id, const u8 *p, DumpState &state)
Definition: wdbg_sym.cpp:1115
#define ARRAY_SIZE(name)
int wcscpy_s(wchar_t *dst, size_t max_dst_chars, const wchar_t *src)
TI_FINDCHILDREN_PARAMS p
Definition: wdbg_sym.cpp:178
const Status SYM_UNRETRIEVABLE_STATIC
Definition: debug.h:402
Status(* StackFrameCallback)(const _tagSTACKFRAME64 *frame, uintptr_t cbData)
called for each stack frame found by wdbg_sym_WalkStack.
Definition: wdbg_sym.h:43
const Status NOT_SUPPORTED
Definition: status.h:429
uintptr_t moduleBase
Definition: wdbg_sym.cpp:375
static bool IsRelativeToFramePointer(DWORD flags, DWORD reg)
Definition: wdbg_sym.cpp:764
Definition: dbghelp.h:83
LIB_API Status debug_CaptureContext(void *context)
Definition: udbg.cpp:42
static void sym_init()
Definition: wdbg_sym.cpp:107
const Status SYM_UNRETRIEVABLE
Definition: debug.h:403
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
Definition: dbghelp.h:81
Status debug_stl_get_container_info(const wchar_t *type_name, const u8 *p, size_t size, size_t el_size, size_t *el_count, DebugStlIterator *el_iterator, void *it_mem)
prepare to enumerate the elements of arbitrarily typed STL containers.
Definition: debug_stl.cpp:549
#define CALLBACK
Definition: wgl.h:39
intptr_t ModuleInitState
initialization state of a module (class, source file, etc.) must be initialized to zero (e...
Definition: module_init.h:35
static bool udt_fits_on_one_line(const wchar_t *type_name, size_t child_count, size_t total_size)
Definition: wdbg_sym.cpp:1411
static Status dump_sym_base_class(DWORD type_id, const u8 *p, DumpState &state)
Definition: wdbg_sym.cpp:1006
LIB_API int debug_IsPointerBogus(const void *p)
check if a pointer appears to be totally invalid.
Definition: udbg.cpp:114
const Status SYM_NO_STACK_FRAMES_FOUND
Definition: debug.h:401
#define WINIT_REGISTER_CRITICAL_INIT(func)
Definition: winit.h:136
static Status dump_sym_typedef(DWORD type_id, const u8 *p, DumpState &state)
Definition: wdbg_sym.cpp:1226
static Status dump_string(const u8 *p, size_t el_size)
Definition: wdbg_sym.cpp:593
Definition: path.h:75
static Status dump_sym_pointer(DWORD type_id, const u8 *p, DumpState &state)
Definition: wdbg_sym.cpp:1183
void * HANDLE
Definition: wgl.h:62
static const DWORD maxChildren
Definition: wdbg_sym.cpp:177
Definition: dbghelp.h:94
const Status REENTERED
Definition: status.h:412
static void ptr_reset_visited()
Definition: wdbg_sym.cpp:1146
static Status dump_array(const u8 *p, size_t el_count, DWORD el_type_id, size_t el_size, DumpState &state)
Definition: wdbg_sym.cpp:724
unsigned long DWORD
Definition: wgl.h:56
static IMAGEHLP_STACK_FRAME PopulateImageStackFrame(const STACKFRAME64 &sf)
Definition: wdbg_sym.cpp:134
static const size_t DEBUG_FILE_CHARS
Definition: debug.h:429
Status(* DumpFunc)(DWORD typeId, const u8 *p, DumpState &state)
Definition: wdbg_sym.cpp:1592
static wchar_t * out_pos
Definition: wdbg_sym.cpp:388
void VOID
Definition: wgl.h:49
static bool ptr_already_visited(const u8 *p)
Definition: wdbg_sym.cpp:1151
const Status SYM_TYPE_INFO_UNAVAILABLE
Definition: debug.h:404
LIB_API void * debug_GetCaller(void *context, const wchar_t *lastFuncToSkip)
return the caller of a certain function on the call stack.
Definition: bdbg.cpp:38
size_t level
Definition: wdbg_sym.cpp:373
static const u8 * visited[maxVisited]
Definition: wdbg_sym.cpp:1143
i64 Status
Error handling system.
Definition: status.h:171
void debug_break()
trigger a breakpoint when reached/&quot;called&quot;.
Definition: udbg.cpp:48
static Status dump_sym_vtable(DWORD type_id, const u8 *p, DumpState &state)
Definition: wdbg_sym.cpp:1567
static Status dump_sym_udt(DWORD type_id, const u8 *p, DumpState &state)
Definition: wdbg_sym.cpp:1521
static void AppendCharacterIfPrintable(u64 data)
Definition: wdbg_sym.cpp:859
Definition: dbghelp.h:84
const size_t DEBUG_STL_MAX_ITERATOR_SIZE
no STL iterator is larger than this; see below.
Definition: debug_stl.h:63
static const u8 * array_iterator(void *internal, size_t el_size)
Definition: wdbg_sym.cpp:715
intptr_t ssize_t
Definition: wposix_types.h:82
#define DEBUG_WARN_ERR(status)
display the error dialog with text corresponding to the given error code.
Definition: debug.h:331
DWORD childrenStorage[maxChildren-1]
Definition: wdbg_sym.cpp:179
#define u64
Definition: types.h:42
static size_t numVisited
Definition: wdbg_sym.cpp:1144
static void dump_error(Status err)
Definition: wdbg_sym.cpp:560
static Status dump_sym_enum(DWORD type_id, const u8 *p, DumpState &state)
Definition: wdbg_sym.cpp:1049
static void out_init(wchar_t *buf, size_t max_chars)
Definition: wdbg_sym.cpp:410
const Status SKIPPED
Definition: status.h:392
#define i64
Definition: types.h:37
unsigned short wchar_t
Definition: wgl.h:78
static const size_t maxVisited
Definition: wdbg_sym.cpp:1142
const u8 *(* DebugStlIterator)(void *internal, size_t el_size)
abstraction of all STL iterators used by debug_stl.
Definition: debug_stl.h:58
const Status SYM_INTERNAL_ERROR
Definition: debug.h:405
bool cpu_CAS(volatile intptr_t *location, intptr_t expected, intptr_t newValue)
atomic &quot;compare and swap&quot;.
Definition: arm.cpp:36
wchar_t * StatusDescription(Status status, wchar_t *buf, size_t max_chars)
generate textual description of a Status.
Definition: status.cpp:79
#define WARN_IF_FALSE(expression)
Definition: status.h:360
const Status STL_CNT_UNKNOWN
Definition: debug_stl.h:33
static Status dump_sym(DWORD id, const u8 *p, DumpState &state)
Definition: wdbg_sym.cpp:1628
static Status dump_sym_data(DWORD id, const u8 *p, DumpState &state)
Definition: wdbg_sym.cpp:1025
LPSTACKFRAME64 stackFrame
Definition: wdbg_sym.cpp:376
static Status dump_sym_function(DWORD type_id, const u8 *p, DumpState &state)
Definition: wdbg_sym.cpp:1107
const wchar_t * path_name_only(const wchar_t *path)
Get the path component of a path.
Definition: path.cpp:85
Definition: dbghelp.h:86
static Status CallStackWalk(STACKFRAME64 &sf, CONTEXT &context)
Definition: wdbg_sym.cpp:284
#define DEBUG_DISPLAY_ERROR(description)
Definition: debug.h:197
#define WARN_RETURN(status)
Definition: status.h:255
static const size_t DEBUG_CONTEXT_SIZE
Definition: debug.h:451
const Status FAIL
Definition: status.h:406
size_t indirection
Definition: wdbg_sym.cpp:374
static Status out_check_limit()
Definition: wdbg_sym.cpp:488
#define INDENT
Definition: wdbg_sym.cpp:504
LIB_API Status debug_ResolveSymbol(void *ptr_of_interest, wchar_t *sym_name, wchar_t *file, int *line)
read and return symbol information for the given address.
Definition: bdbg.cpp:99
const OsPath & ah_get_log_dir()
return path to directory into which crash dumps should be written.
Definition: app_hooks.cpp:151
const Status SYM_NESTING_LIMIT
Definition: debug.h:409
static bool IsUnretrievable(DWORD flags)
Definition: wdbg_sym.cpp:775
#define cassert(expr)
Compile-time assertion.
TI_FINDCHILDREN_PARAMS2(DWORD numChildren)
Definition: wdbg_sym.cpp:171
const Status SYM_SINGLE_SYMBOL_LIMIT
Definition: debug.h:412
#define wdbg_assert(expr)
similar to ENSURE but safe to use during critical init or while under the heap or dbghelp locks...
Definition: wdbg.h:43
static bool is_string(const u8 *p, size_t stride)
Definition: wdbg_sym.cpp:513
static WORD machine
Definition: wdbg_sym.cpp:63
Status ModuleInit(volatile ModuleInitState *initState, Status(*init)())
calls a user-defined init function if initState is zero.
Definition: module_init.cpp:40
static STACKFRAME64 PopulateStackFrame(CONTEXT &context)
Definition: wdbg_sym.cpp:114
static size_t out_chars_left
Definition: wdbg_sym.cpp:387
static Status InitDbghelp()
Definition: wdbg_sym.cpp:65
#define WUTIL_IMPORT_KERNEL32(procName, varName)
Definition: wutil.h:63
u64 movzx_le64(const u8 *p, size_t size_bytes)
see write_be16
Definition: byte_order.cpp:148
void debug_printf(const wchar_t *fmt,...)
write a formatted string to the debug channel, subject to filtering (see below).
Definition: debug.cpp:142
static enum @41 state
static std::string OsString(const OsPath &path)
Definition: os_path.h:42
LIB_API Status debug_DumpStack(wchar_t *buf, size_t maxChars, void *context, const wchar_t *lastFuncToSkip)
write a complete stack trace (including values of local variables) into the specified buffer...
Definition: bdbg.cpp:52
#define RETURN_STATUS_IF_ERR(expression)
Definition: status.h:276