Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

299 lines
7.9 KiB

  1. //----------------------------------------------------------------------------
  2. //
  3. // Simple example of how to use non-invasive self-attach to get
  4. // stack traces for assertion failures.
  5. //
  6. // Copyright (C) Microsoft Corporation, 2001.
  7. //
  8. //----------------------------------------------------------------------------
  9. #include <stdlib.h>
  10. #include <stdio.h>
  11. #include <stdarg.h>
  12. #include <windows.h>
  13. #include <dbgeng.h>
  14. #include "out.hpp"
  15. #define DE_ASSERT(Expr) \
  16. if (!(Expr)) AssertionFailed(__FILE__, __LINE__, #Expr); else 0
  17. PSTR g_SymbolPath;
  18. ULONG g_Pid;
  19. BOOL g_Suspend;
  20. BOOL g_NoDebuggerCheck;
  21. IDebugClient* g_Client;
  22. IDebugControl* g_Control;
  23. IDebugSymbols* g_Symbols;
  24. void
  25. ReleaseInterfaces(void)
  26. {
  27. if (g_Symbols != NULL)
  28. {
  29. g_Symbols->Release();
  30. }
  31. if (g_Control != NULL)
  32. {
  33. g_Control->Release();
  34. }
  35. if (g_Client != NULL)
  36. {
  37. //
  38. // Request a simple end to any current session.
  39. // This may or may not do anything but it isn't
  40. // harmful to call it.
  41. //
  42. // We don't want to see any output from the shutdown.
  43. g_Client->SetOutputCallbacks(NULL);
  44. g_Client->EndSession(DEBUG_END_ACTIVE_DETACH);
  45. g_Client->Release();
  46. }
  47. }
  48. void
  49. Exit(int Code, PCSTR Format, ...)
  50. {
  51. // Clean up any resources.
  52. ReleaseInterfaces();
  53. // Output an error message if given.
  54. if (Format != NULL)
  55. {
  56. va_list Args;
  57. va_start(Args, Format);
  58. vfprintf(stderr, Format, Args);
  59. va_end(Args);
  60. }
  61. exit(Code);
  62. }
  63. void
  64. CreateInterfaces(void)
  65. {
  66. HRESULT Status;
  67. // Start things off by getting an initial interface from
  68. // the engine. This can be any engine interface but is
  69. // generally IDebugClient as the client interface is
  70. // where sessions are started.
  71. if ((Status = DebugCreate(__uuidof(IDebugClient),
  72. (void**)&g_Client)) != S_OK)
  73. {
  74. Exit(1, "DebugCreate failed, 0x%X\n", Status);
  75. }
  76. // Query for some other interfaces that we'll need.
  77. if ((Status = g_Client->QueryInterface(__uuidof(IDebugControl),
  78. (void**)&g_Control)) != S_OK ||
  79. (Status = g_Client->QueryInterface(__uuidof(IDebugSymbols),
  80. (void**)&g_Symbols)) != S_OK)
  81. {
  82. Exit(1, "QueryInterface failed, 0x%X\n", Status);
  83. }
  84. }
  85. void
  86. SelfAttach(void)
  87. {
  88. HRESULT Status;
  89. // Don't set the output callbacks yet as we don't want
  90. // to see any of the initial debugger output.
  91. if (g_SymbolPath != NULL)
  92. {
  93. if ((Status = g_Symbols->SetSymbolPath(g_SymbolPath)) != S_OK)
  94. {
  95. Exit(1, "SetSymbolPath failed, 0x%X\n", Status);
  96. }
  97. }
  98. // Everything's set up so do the attach.
  99. if ((Status = g_Client->
  100. AttachProcess(0, g_Pid,
  101. DEBUG_ATTACH_NONINVASIVE |
  102. (g_Suspend ? 0 :
  103. DEBUG_ATTACH_NONINVASIVE_NO_SUSPEND))) != S_OK)
  104. {
  105. Exit(1, "AttachProcess failed, 0x%X\n", Status);
  106. }
  107. // Finish initialization by waiting for the attach event.
  108. // This should return quickly as a non-invasive attach
  109. // can complete immediately.
  110. if ((Status = g_Control->WaitForEvent(DEBUG_WAIT_DEFAULT,
  111. INFINITE)) != S_OK)
  112. {
  113. Exit(1, "WaitForEvent failed, 0x%X\n", Status);
  114. }
  115. // Everything is now initialized and we can make any
  116. // queries we want.
  117. }
  118. void
  119. DumpStack(PCONTEXT Context)
  120. {
  121. HRESULT Status;
  122. int Count = 50;
  123. char CxrCommand[64];
  124. printf("\nFirst %d frames of the call stack:\n", Count);
  125. // Install output callbacks so we get the output from the stack dump.
  126. if ((Status = g_Client->SetOutputCallbacks(&g_OutputCb)) != S_OK)
  127. {
  128. Exit(1, "SetOutputCallbacks failed, 0x%X\n", Status);
  129. }
  130. sprintf(CxrCommand, ".cxr 0x%p", Context);
  131. // Print the call stack for the given context.
  132. if ((Status = g_Control->
  133. Execute(DEBUG_OUTCTL_IGNORE, CxrCommand,
  134. DEBUG_EXECUTE_NOT_LOGGED)) != S_OK)
  135. {
  136. Exit(1, "Execute failed, 0x%X\n", Status);
  137. }
  138. // If the code is optimized at all it is important to have
  139. // accurate symbols to get the correct stack.
  140. if ((Status = g_Control->
  141. OutputStackTrace(DEBUG_OUTCTL_ALL_CLIENTS, NULL,
  142. Count, DEBUG_STACK_SOURCE_LINE |
  143. DEBUG_STACK_FRAME_ADDRESSES |
  144. DEBUG_STACK_COLUMN_NAMES |
  145. DEBUG_STACK_FRAME_NUMBERS)) != S_OK)
  146. {
  147. Exit(1, "OutputStackTrace failed, 0x%X\n", Status);
  148. }
  149. // Done with output.
  150. if ((Status = g_Client->SetOutputCallbacks(NULL)) != S_OK)
  151. {
  152. Exit(1, "SetOutputCallbacks failed, 0x%X\n", Status);
  153. }
  154. //
  155. // The full engine API is available so many other things
  156. // could be done here.
  157. //
  158. // A dump file could be written with WriteDumpFile.
  159. // The raw stack data could be collected with GetStackTrace and
  160. // saved along with or instead of the text.
  161. // An analysis of the current program state could be done
  162. // to automatically diagnose simple problems.
  163. //
  164. // The primary thing to watch out for is that context information
  165. // for running threads will be stale. This could be avoided
  166. // by enumerating and suspending all other threads after the
  167. // attach completes and then resuming before the assert
  168. // returns controls. Otherwise, switching between threads will
  169. // refresh the thread context and can be used to poll the context
  170. // state.
  171. //
  172. }
  173. DWORD
  174. AssertionExceptionDump(PEXCEPTION_POINTERS Exception)
  175. {
  176. CreateInterfaces();
  177. SelfAttach();
  178. DumpStack(Exception->ContextRecord);
  179. ReleaseInterfaces();
  180. return EXCEPTION_EXECUTE_HANDLER;
  181. }
  182. void
  183. AssertionFailed(PSTR File, int Line, PSTR ExprText)
  184. {
  185. printf("Assertion failed: %s(%d):\n %s\n",
  186. File, Line, ExprText);
  187. if (!g_NoDebuggerCheck && IsDebuggerPresent())
  188. {
  189. // We're already running under a debugger so just break in.
  190. DebugBreak();
  191. }
  192. else
  193. {
  194. // No debugger, so just get a stack from the current
  195. // routine and then continue on. We need a context
  196. // for the currently running code, so force an exception
  197. // to get an EXCEPTION_POINTERS structure with context
  198. // information that we can use to get a stack trace.
  199. __try
  200. {
  201. RaiseException(0x1234, 0, 0, NULL);
  202. }
  203. __except(AssertionExceptionDump(GetExceptionInformation()))
  204. {
  205. // Nothing to do.
  206. }
  207. }
  208. }
  209. void
  210. ParseCommandLine(int Argc, char** Argv)
  211. {
  212. g_Pid = GetCurrentProcessId();
  213. g_Suspend = FALSE;
  214. g_NoDebuggerCheck = FALSE;
  215. while (--Argc > 0)
  216. {
  217. Argv++;
  218. if (!strcmp(*Argv, "-d"))
  219. {
  220. g_NoDebuggerCheck = TRUE;
  221. }
  222. else if (!strcmp(*Argv, "-p"))
  223. {
  224. if (Argc < 2)
  225. {
  226. Exit(1, "-p missing argument\n");
  227. }
  228. Argv++;
  229. Argc--;
  230. g_Pid = atoi(*Argv);
  231. }
  232. else if (!strcmp(*Argv, "-s"))
  233. {
  234. g_Suspend = TRUE;
  235. }
  236. else if (!strcmp(*Argv, "-y"))
  237. {
  238. if (Argc < 2)
  239. {
  240. Exit(1, "-y missing argument\n");
  241. }
  242. Argv++;
  243. Argc--;
  244. g_SymbolPath = *Argv;
  245. }
  246. else
  247. {
  248. Exit(1, "Unknown command line argument '%s'\n", *Argv);
  249. }
  250. }
  251. }
  252. void __cdecl
  253. main(int Argc, char** Argv)
  254. {
  255. ParseCommandLine(Argc, Argv);
  256. DE_ASSERT(Argc == 0);
  257. }