Source code of Windows XP (NT5)
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.

530 lines
12 KiB

  1. /*++
  2. Copyright (c) 1996-2000 Microsoft Corporation
  3. Module Name:
  4. database.c
  5. Abstract:
  6. Quick and not-so-dirty user-mode dh for heap.
  7. This module contains the functions and structures used to
  8. read the whole stack trace database of a target process and
  9. subsequently querying it.
  10. Author(s):
  11. Silviu Calinoiu (SilviuC) 07-Feb-00
  12. Revision History:
  13. SilviuC 06-Feb-00 Initial version
  14. --*/
  15. #include <ctype.h>
  16. #include <stdlib.h>
  17. #include <stdio.h>
  18. #include <nt.h>
  19. #include <ntrtl.h>
  20. #include <nturtl.h>
  21. #include <ntos.h>
  22. #define NOWINBASEINTERLOCK
  23. #include <windows.h>
  24. #include <lmcons.h>
  25. // #include <imagehlp.h>
  26. #include <dbghelp.h>
  27. #include <heap.h>
  28. #include <heappagi.h>
  29. #include <stktrace.h>
  30. #include "types.h"
  31. #include "symbols.h"
  32. #include "miscellaneous.h"
  33. #include "database.h"
  34. // SilviuC: do we really need all these includes?
  35. PVOID
  36. GetTargetProcessDatabaseAddress (
  37. HANDLE Process
  38. )
  39. {
  40. PVOID Address;
  41. BOOL Result;
  42. PVOID DbAddress;
  43. //
  44. // SymbolAddress will return a NULL address on error.
  45. //
  46. Address = SymbolAddress (STACK_TRACE_DB_NAME);
  47. if (Address == NULL) {
  48. return NULL;
  49. }
  50. Result = READVM (Address, &DbAddress, sizeof DbAddress);
  51. if (Result == FALSE) {
  52. Comment ( "ntdll.dll symbols are bad or we are not tracking "
  53. "allocations in the target process.");
  54. return NULL;
  55. }
  56. if (DbAddress == NULL) {
  57. Comment ( "Stack trace collection is not enabled for this process. "
  58. "Please use the gflags tool with the +ust option to enable it. \n");
  59. Error (NULL, 0,
  60. "Stack trace collection is not enabled for this process. "
  61. "Please use the gflags tool with the +ust option to enable it. \n");
  62. return NULL;
  63. }
  64. return DbAddress;
  65. }
  66. // returns TRUE if successful
  67. BOOL
  68. TraceDbInitialize (
  69. HANDLE Process
  70. )
  71. {
  72. SIZE_T Index;
  73. BOOL Result;
  74. SIZE_T BytesRead;
  75. DWORD OldProtect;
  76. PVOID TargetAddress;
  77. PVOID SourceAddress;
  78. SYSTEM_INFO SystemInfo;
  79. SIZE_T PageSize;
  80. ULONG PageCount = 0;
  81. PVOID TargetDbAddress;
  82. SIZE_T DatabaseSize;
  83. SIZE_T TotalDbSize;
  84. STACK_TRACE_DATABASE Db;
  85. GetSystemInfo (&SystemInfo);
  86. PageSize = (SIZE_T)(SystemInfo.dwPageSize);
  87. TargetDbAddress = GetTargetProcessDatabaseAddress (Process);
  88. if( TargetDbAddress == NULL ) {
  89. return FALSE;
  90. }
  91. //
  92. // Figure out the trace database size.
  93. //
  94. Result = ReadProcessMemory (Process,
  95. TargetDbAddress,
  96. &Db,
  97. sizeof Db,
  98. &BytesRead);
  99. if (Result == FALSE) {
  100. Error (NULL, 0,
  101. "Failed to read trace database header (error %u)",
  102. GetLastError());
  103. return FALSE;
  104. }
  105. TotalDbSize = (ULONG_PTR)(Db.EntryIndexArray) - (ULONG_PTR)(Db.CommitBase);
  106. //
  107. // Allocate memory for the database duplicate.
  108. //
  109. Globals.Database = VirtualAlloc (NULL,
  110. TotalDbSize,
  111. MEM_RESERVE | MEM_COMMIT,
  112. PAGE_READWRITE);
  113. if (Globals.Database == NULL) {
  114. Error (NULL, 0,
  115. "Failed to allocate memory for database (error %u)",
  116. GetLastError());
  117. return FALSE;
  118. }
  119. //
  120. // Read the whole thing
  121. //
  122. Comment ("Reading target process trace database ...");
  123. DatabaseSize = PageSize;
  124. for (Index = 0; Index < DatabaseSize; Index += PageSize) {
  125. SourceAddress = (PVOID)((SIZE_T)(TargetDbAddress) + Index);
  126. TargetAddress = (PVOID)((SIZE_T)(Globals.Database) + Index);
  127. Result = ReadProcessMemory (Process,
  128. SourceAddress,
  129. TargetAddress,
  130. PageSize,
  131. &BytesRead);
  132. if (Index == 0) {
  133. //
  134. // This is the first page of the database. We can now detect
  135. // the real size of what we need to read.
  136. //
  137. if (Result == FALSE) {
  138. Comment ("Failed to read trace database (error %u)", GetLastError());
  139. return FALSE;
  140. }
  141. else {
  142. PSTACK_TRACE_DATABASE pDb;
  143. pDb= (PSTACK_TRACE_DATABASE)(Globals.Database);
  144. DatabaseSize= (SIZE_T)(pDb->EntryIndexArray) - (SIZE_T)(pDb->CommitBase);
  145. Comment ("Database size %p", DatabaseSize);
  146. }
  147. }
  148. }
  149. Comment ("Trace database read.", PageCount);
  150. if (Globals.DumpFileName) {
  151. TraceDbBinaryDump ();
  152. return FALSE;
  153. }
  154. return TRUE;
  155. }
  156. PVOID
  157. RelocateDbAddress (
  158. PVOID TargetAddress
  159. )
  160. {
  161. ULONG_PTR TargetBase;
  162. ULONG_PTR LocalBase;
  163. PVOID LocalAddress;
  164. LocalBase = (ULONG_PTR)(Globals.Database);
  165. TargetBase = (ULONG_PTR)(((PSTACK_TRACE_DATABASE)LocalBase)->CommitBase);
  166. LocalAddress = (PVOID)((ULONG_PTR)TargetAddress - TargetBase + LocalBase);
  167. return LocalAddress;
  168. }
  169. VOID
  170. TraceDbDump (
  171. )
  172. {
  173. PSTACK_TRACE_DATABASE Db;
  174. USHORT I;
  175. PRTL_STACK_TRACE_ENTRY Entry;
  176. PRTL_STACK_TRACE_ENTRY * IndexArray;
  177. Comment ("Dumping raw data from the trace database ...");
  178. Info ("");
  179. Db = (PSTACK_TRACE_DATABASE)(Globals.Database);
  180. Globals.ComplainAboutUnresolvedSymbols = TRUE;
  181. for (I = 1; I <= Db->NumberOfEntriesAdded; I += 1) {
  182. if (Globals.RawIndex > 0 && Globals.RawIndex != I) {
  183. continue;
  184. }
  185. IndexArray = (PRTL_STACK_TRACE_ENTRY *) RelocateDbAddress (Db->EntryIndexArray);
  186. if (IndexArray[-I] == NULL) {
  187. Warning (NULL, 0, "Null/inaccessible trace pointer for trace index %u", I);
  188. continue;
  189. }
  190. Entry = (PRTL_STACK_TRACE_ENTRY) RelocateDbAddress (IndexArray[-I]);
  191. if (I != Entry->Index) {
  192. Warning (NULL, 0, "Allocation trace index %u does not match trace entry index %u",
  193. I, Entry->Index);
  194. continue;
  195. }
  196. Info (" %u alloc(s) by: BackTrace%05u", Entry->TraceCount, I);
  197. UmdhDumpStackByIndex (I);
  198. }
  199. }
  200. BOOL
  201. TraceDbBinaryDump (
  202. )
  203. {
  204. PSTACK_TRACE_DATABASE Db;
  205. SIZE_T DatabaseSize;
  206. HANDLE DumpFile;
  207. DWORD BytesWritten;
  208. BOOL Result;
  209. Db = (PSTACK_TRACE_DATABASE)(Globals.Database);
  210. DatabaseSize = (SIZE_T)(Db->EntryIndexArray) - (SIZE_T)(Db->CommitBase);
  211. Comment ("Creating the binary dump for the trace database in `%s'.",
  212. Globals.DumpFileName);
  213. DumpFile = CreateFile (Globals.DumpFileName,
  214. GENERIC_WRITE,
  215. 0,
  216. NULL,
  217. CREATE_ALWAYS,
  218. 0,
  219. NULL);
  220. if (DumpFile == INVALID_HANDLE_VALUE) {
  221. Comment ( "Failed to create the binary dump file (error %u)",
  222. GetLastError());
  223. return FALSE;
  224. }
  225. Result = WriteFile (DumpFile,
  226. Globals.Database,
  227. (DWORD)DatabaseSize,
  228. &BytesWritten,
  229. NULL);
  230. if (Result == FALSE || BytesWritten != DatabaseSize) {
  231. Comment ("Failed to write the binary dump of trace database (error %u)",
  232. GetLastError());
  233. return FALSE;
  234. }
  235. CloseHandle (DumpFile);
  236. Comment ("Finished the binary dump.");
  237. return TRUE;
  238. }
  239. VOID
  240. UmdhDumpStackByIndex(
  241. IN USHORT TraceIndex
  242. )
  243. /*++
  244. Routine Description:
  245. This routine dumps a stack as it is stored in the stack trace database.
  246. The trace index is used to find out the actual stack trace.
  247. Arguments:
  248. TraceIndex - index of the stack trace.
  249. Return Value:
  250. None.
  251. Side effects:
  252. The trace is dumped to standard output.
  253. --*/
  254. {
  255. PSTACK_TRACE_DATABASE StackTraceDb;
  256. PRTL_STACK_TRACE_ENTRY Entry;
  257. PRTL_STACK_TRACE_ENTRY * IndexArray;
  258. PVOID Addr;
  259. BOOL Result;
  260. TRACE StackTrace;
  261. if (TraceIndex == 0) {
  262. //
  263. // An index of 0 is returned by RtlLogStackBackTrace for an error
  264. // condition, typically when the stack trace db has not been
  265. // initialized.
  266. //
  267. Info ("No trace was saved for this allocation (Index == 0).");
  268. return;
  269. }
  270. StackTraceDb = (PSTACK_TRACE_DATABASE)(Globals.Database);
  271. //
  272. // Read the pointer to the array of pointers to stack traces, then read
  273. // the actual stack trace.
  274. //
  275. IndexArray = (PRTL_STACK_TRACE_ENTRY *) RelocateDbAddress (StackTraceDb->EntryIndexArray);
  276. if (IndexArray[-TraceIndex] == NULL) {
  277. Info ("Null/inaccessible trace pointer for trace index %u", TraceIndex);
  278. return;
  279. }
  280. Entry = (PRTL_STACK_TRACE_ENTRY) RelocateDbAddress (IndexArray[-TraceIndex]);
  281. if (TraceIndex != Entry->Index) {
  282. Error (NULL, 0, "Allocation trace index %u does not match trace entry index %u",
  283. TraceIndex, Entry->Index);
  284. return;
  285. }
  286. //
  287. // Read the stack trace pointers
  288. //
  289. ZeroMemory (&StackTrace, sizeof StackTrace);
  290. StackTrace.te_EntryCount = min (Entry->Depth, MAX_STACK_DEPTH);
  291. StackTrace.te_Address = (PULONG_PTR)(&(Entry->BackTrace));
  292. UmdhDumpStack (&StackTrace);
  293. //
  294. // StackTrace is about to go out of scope, free any data we allocated
  295. // for it. te_Address points to stack, but the te_Module, te_Name, and
  296. // te_Offset fields were allocated by UmdhResolveName.
  297. //
  298. XFREE(StackTrace.te_Module);
  299. XFREE(StackTrace.te_Name);
  300. XFREE(StackTrace.te_Offset);
  301. //
  302. // SilviuC: We should probably read the whole trace database during
  303. // process startup instead of poking the process space all the time.
  304. //
  305. }
  306. /*
  307. * UmdhDumpStack
  308. *
  309. * Send data in a LIST of TRACE_ENTRYs to the log function.
  310. *
  311. * t is the TRACE which we are to 'dump'.
  312. */
  313. // silviuc: sanitize
  314. VOID
  315. UmdhDumpStack (
  316. IN PTRACE Trace
  317. )
  318. {
  319. ULONG i;
  320. PCHAR FullName;
  321. IMAGEHLP_LINE LineInfo;
  322. DWORD Displacement;
  323. BOOL LineInfoPresent;
  324. if (Trace == NULL) {
  325. return;
  326. }
  327. for (i = 0; i < Trace->te_EntryCount; i += 1) {
  328. if (Trace->te_Address[i] != 0) {
  329. FullName = GetSymbolicNameForAddress (Globals.Target,
  330. Trace->te_Address[i]);
  331. LineInfoPresent = FALSE;
  332. if (Globals.LineInfo) {
  333. ZeroMemory (&LineInfo, sizeof LineInfo);
  334. LineInfo.SizeOfStruct = sizeof LineInfo;
  335. LineInfoPresent = SymGetLineFromAddr (Globals.Target,
  336. Trace->te_Address[i],
  337. &Displacement,
  338. &LineInfo);
  339. }
  340. if (FullName) {
  341. if (Globals.Verbose) {
  342. if (LineInfoPresent) {
  343. Info (" %p : %s (%s, %u)",
  344. Trace->te_Address[i],
  345. FullName,
  346. FullName,
  347. LineInfo.FileName,
  348. LineInfo.LineNumber);
  349. }
  350. else {
  351. Info (" %p : %s",
  352. Trace->te_Address[i],
  353. FullName);
  354. }
  355. }
  356. else {
  357. if (LineInfoPresent) {
  358. Info (" %s (%s, %u)",
  359. FullName,
  360. LineInfo.FileName,
  361. LineInfo.LineNumber);
  362. }
  363. else {
  364. Info (" %s",
  365. FullName);
  366. }
  367. }
  368. }
  369. else {
  370. Info (" %p : <no module information>", Trace->te_Address[i]);
  371. }
  372. }
  373. }
  374. Info ("\n");
  375. }