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.

592 lines
16 KiB

  1. /*++
  2. Copyright (c) 1989-2000 Microsoft Corporation
  3. Module Name:
  4. UserDump.cpp
  5. Abstract:
  6. This module implements the code for generating a user mode dump.
  7. Author:
  8. clupu created 01/31/2001
  9. Revision History:
  10. --*/
  11. #include "stdafx.h"
  12. #include <crash.h>
  13. #include "Debugger.h"
  14. #include "UserDumpp.h"
  15. #define MEM_SIZE (64*1024)
  16. //
  17. // Private data structure used for communcating
  18. // crash dump data to the callback function.
  19. //
  20. typedef struct _CRASH_DUMP_INFO
  21. {
  22. PPROCESS_INFO pProcess;
  23. EXCEPTION_DEBUG_INFO* ExceptionInfo;
  24. DWORD MemoryCount;
  25. DWORD_PTR Address;
  26. PUCHAR MemoryData;
  27. MEMORY_BASIC_INFORMATION mbi;
  28. SIZE_T MbiOffset;
  29. SIZE_T MbiRemaining;
  30. PTHREAD_INFO pCurrentThread;
  31. IMAGEHLP_MODULE mi;
  32. PCRASH_MODULE CrashModule;
  33. } CRASH_DUMP_INFO, *PCRASH_DUMP_INFO;
  34. //
  35. // Local function prototypes
  36. //
  37. DWORD_PTR GetTeb( HANDLE hThread )
  38. {
  39. NTSTATUS Status;
  40. THREAD_BASIC_INFORMATION ThreadBasicInfo;
  41. DWORD_PTR Address = 0;
  42. Status = NtQueryInformationThread( hThread,
  43. ThreadBasicInformation,
  44. &ThreadBasicInfo,
  45. sizeof( ThreadBasicInfo ),
  46. NULL );
  47. if ( NT_SUCCESS(Status) )
  48. {
  49. Address = (DWORD_PTR)ThreadBasicInfo.TebBaseAddress;
  50. }
  51. return Address;
  52. }
  53. BOOL
  54. CrashDumpCallback(
  55. IN DWORD DataType, // requested data type
  56. OUT PVOID* DumpData, // pointer to a pointer to the data
  57. OUT LPDWORD DumpDataLength, // pointer to the data length
  58. IN OUT PVOID cdi // private data
  59. )
  60. /*++
  61. Return: TRUE on success, FALSE otherwise.
  62. Desc: This function is the callback used by crashlib.
  63. Its purpose is to provide data to CreateUserDump()
  64. for writting to the crashdump file.
  65. --*/
  66. {
  67. PCRASH_DUMP_INFO CrashdumpInfo = (PCRASH_DUMP_INFO)cdi;
  68. switch ( DataType )
  69. {
  70. case DMP_DEBUG_EVENT:
  71. *DumpData = &CrashdumpInfo->pProcess->DebugEvent;
  72. *DumpDataLength = sizeof(DEBUG_EVENT);
  73. break;
  74. case DMP_THREAD_STATE:
  75. {
  76. static CRASH_THREAD CrashThread;
  77. PTHREAD_INFO pCurrentThread;
  78. *DumpData = &CrashThread;
  79. if ( CrashdumpInfo->pCurrentThread == NULL )
  80. {
  81. pCurrentThread = CrashdumpInfo->pProcess->pFirstThreadInfo;
  82. }
  83. else
  84. {
  85. pCurrentThread = CrashdumpInfo->pCurrentThread->pNext;
  86. }
  87. CrashdumpInfo->pCurrentThread = pCurrentThread;
  88. if ( pCurrentThread == NULL )
  89. {
  90. return FALSE;
  91. }
  92. ZeroMemory(&CrashThread, sizeof(CrashThread));
  93. CrashThread.ThreadId = pCurrentThread->dwThreadId;
  94. CrashThread.SuspendCount = SuspendThread(pCurrentThread->hThread);
  95. if ( CrashThread.SuspendCount != (DWORD)-1 )
  96. {
  97. ResumeThread(pCurrentThread->hThread);
  98. }
  99. CrashThread.PriorityClass = GetPriorityClass(CrashdumpInfo->pProcess->hProcess);
  100. CrashThread.Priority = GetThreadPriority(pCurrentThread->hThread);
  101. CrashThread.Teb = GetTeb(pCurrentThread->hThread);
  102. *DumpDataLength = sizeof(CRASH_THREAD);
  103. break;
  104. }
  105. case DMP_MEMORY_BASIC_INFORMATION:
  106. while ( TRUE )
  107. {
  108. CrashdumpInfo->Address += CrashdumpInfo->mbi.RegionSize;
  109. if ( !VirtualQueryEx(CrashdumpInfo->pProcess->hProcess,
  110. (LPVOID)CrashdumpInfo->Address,
  111. &CrashdumpInfo->mbi,
  112. sizeof(MEMORY_BASIC_INFORMATION)) )
  113. {
  114. return FALSE;
  115. }
  116. if ( (CrashdumpInfo->mbi.Protect & PAGE_GUARD) ||
  117. (CrashdumpInfo->mbi.Protect & PAGE_NOACCESS) )
  118. {
  119. continue;
  120. }
  121. if ( (CrashdumpInfo->mbi.State & MEM_FREE) ||
  122. (CrashdumpInfo->mbi.State & MEM_RESERVE) )
  123. {
  124. continue;
  125. }
  126. break;
  127. }
  128. *DumpData = &CrashdumpInfo->mbi;
  129. *DumpDataLength = sizeof(MEMORY_BASIC_INFORMATION);
  130. break;
  131. case DMP_THREAD_CONTEXT:
  132. {
  133. PTHREAD_INFO pCurrentThread;
  134. if ( CrashdumpInfo->pCurrentThread == NULL )
  135. {
  136. pCurrentThread = CrashdumpInfo->pProcess->pFirstThreadInfo;
  137. }
  138. else
  139. {
  140. pCurrentThread = CrashdumpInfo->pCurrentThread->pNext;
  141. }
  142. CrashdumpInfo->pCurrentThread = pCurrentThread;
  143. if ( pCurrentThread == NULL )
  144. {
  145. return FALSE;
  146. }
  147. *DumpData = &CrashdumpInfo->pCurrentThread->Context;
  148. *DumpDataLength = sizeof(CONTEXT);
  149. break;
  150. }
  151. case DMP_MODULE:
  152. if ( CrashdumpInfo->mi.BaseOfImage == 0 )
  153. {
  154. return FALSE;
  155. }
  156. CrashdumpInfo->CrashModule->BaseOfImage = CrashdumpInfo->mi.BaseOfImage;
  157. CrashdumpInfo->CrashModule->SizeOfImage = CrashdumpInfo->mi.ImageSize;
  158. CrashdumpInfo->CrashModule->ImageNameLength = strlen(CrashdumpInfo->mi.ImageName) + 1;
  159. strcpy( CrashdumpInfo->CrashModule->ImageName, CrashdumpInfo->mi.ImageName );
  160. *DumpData = CrashdumpInfo->CrashModule;
  161. *DumpDataLength = sizeof(CRASH_MODULE) + CrashdumpInfo->CrashModule->ImageNameLength;
  162. if ( !SymGetModuleInfo(CrashdumpInfo->pProcess->hProcess,
  163. (DWORD_PTR)-1,
  164. &CrashdumpInfo->mi) )
  165. {
  166. CrashdumpInfo->mi.BaseOfImage = 0;
  167. }
  168. break;
  169. case DMP_MEMORY_DATA:
  170. if ( !CrashdumpInfo->MemoryCount )
  171. {
  172. CrashdumpInfo->Address = 0;
  173. CrashdumpInfo->MbiOffset = 0;
  174. CrashdumpInfo->MbiRemaining = 0;
  175. ZeroMemory( &CrashdumpInfo->mbi, sizeof(MEMORY_BASIC_INFORMATION) );
  176. CrashdumpInfo->MemoryData = (PUCHAR)VirtualAlloc(NULL,
  177. MEM_SIZE,
  178. MEM_COMMIT,
  179. PAGE_READWRITE);
  180. }
  181. if ( !CrashdumpInfo->MbiRemaining )
  182. {
  183. while ( TRUE )
  184. {
  185. CrashdumpInfo->Address += CrashdumpInfo->mbi.RegionSize;
  186. if ( !VirtualQueryEx(CrashdumpInfo->pProcess->hProcess,
  187. (LPVOID)CrashdumpInfo->Address,
  188. &CrashdumpInfo->mbi,
  189. sizeof(MEMORY_BASIC_INFORMATION)) )
  190. {
  191. if ( CrashdumpInfo->MemoryData )
  192. {
  193. VirtualFree(CrashdumpInfo->MemoryData, MEM_SIZE, MEM_RELEASE);
  194. }
  195. return FALSE;
  196. }
  197. if ( (CrashdumpInfo->mbi.Protect & PAGE_GUARD) ||
  198. (CrashdumpInfo->mbi.Protect & PAGE_NOACCESS) )
  199. {
  200. continue;
  201. }
  202. if ( (CrashdumpInfo->mbi.State & MEM_FREE) ||
  203. (CrashdumpInfo->mbi.State & MEM_RESERVE) )
  204. {
  205. continue;
  206. }
  207. CrashdumpInfo->MbiOffset = 0;
  208. CrashdumpInfo->MbiRemaining = CrashdumpInfo->mbi.RegionSize;
  209. CrashdumpInfo->MemoryCount += 1;
  210. break;
  211. }
  212. }
  213. *DumpDataLength = (DWORD)__min( CrashdumpInfo->MbiRemaining, MEM_SIZE );
  214. CrashdumpInfo->MbiRemaining -= *DumpDataLength;
  215. ReadProcessMemory(CrashdumpInfo->pProcess->hProcess,
  216. (PUCHAR)((DWORD_PTR)CrashdumpInfo->mbi.BaseAddress + CrashdumpInfo->MbiOffset),
  217. CrashdumpInfo->MemoryData,
  218. *DumpDataLength,
  219. NULL);
  220. *DumpData = CrashdumpInfo->MemoryData;
  221. CrashdumpInfo->MbiOffset += *DumpDataLength;
  222. break;
  223. }
  224. return TRUE;
  225. }
  226. BOOL
  227. CreateUserDump(
  228. IN LPTSTR pszFileName,
  229. IN PDBGHELP_CREATE_USER_DUMP_CALLBACK DmpCallback,
  230. IN PVOID lpv
  231. )
  232. /*++
  233. Return: TRUE on success, FALSE otherwise.
  234. Desc: Creates the dump file.
  235. --*/
  236. {
  237. OSVERSIONINFOW OsVersion = {0};
  238. USERMODE_CRASHDUMP_HEADER DumpHeader = {0};
  239. DWORD cb;
  240. HANDLE hFile = INVALID_HANDLE_VALUE;
  241. BOOL rval;
  242. PVOID DumpData;
  243. DWORD DumpDataLength;
  244. SECURITY_ATTRIBUTES SecAttrib;
  245. SECURITY_DESCRIPTOR SecDescript;
  246. //
  247. // Create a DACL that allows all access to the directory.
  248. //
  249. SecAttrib.nLength = sizeof(SECURITY_ATTRIBUTES);
  250. SecAttrib.lpSecurityDescriptor = &SecDescript;
  251. SecAttrib.bInheritHandle = FALSE;
  252. InitializeSecurityDescriptor(&SecDescript, SECURITY_DESCRIPTOR_REVISION);
  253. SetSecurityDescriptorDacl(&SecDescript, TRUE, NULL, FALSE);
  254. hFile = CreateFile(pszFileName,
  255. GENERIC_READ | GENERIC_WRITE,
  256. 0,
  257. &SecAttrib,
  258. CREATE_ALWAYS,
  259. FILE_ATTRIBUTE_NORMAL,
  260. NULL);
  261. if ( hFile == NULL || hFile == INVALID_HANDLE_VALUE )
  262. {
  263. return FALSE;
  264. }
  265. //
  266. // Write out an empty header.
  267. //
  268. if ( !WriteFile(hFile, &DumpHeader, sizeof(DumpHeader), &cb, NULL) )
  269. {
  270. goto bad_file;
  271. }
  272. //
  273. // Write the debug event.
  274. //
  275. DumpHeader.DebugEventOffset = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
  276. DmpCallback(DMP_DEBUG_EVENT, &DumpData, &DumpDataLength, lpv);
  277. if ( !WriteFile(hFile, DumpData, sizeof(DEBUG_EVENT), &cb, NULL) )
  278. {
  279. goto bad_file;
  280. }
  281. //
  282. // Write the memory map.
  283. //
  284. DumpHeader.MemoryRegionOffset = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
  285. do
  286. {
  287. __try {
  288. rval = DmpCallback(DMP_MEMORY_BASIC_INFORMATION,
  289. &DumpData,
  290. &DumpDataLength,
  291. lpv);
  292. } __except (EXCEPTION_EXECUTE_HANDLER) {
  293. rval = FALSE;
  294. }
  295. if ( rval )
  296. {
  297. DumpHeader.MemoryRegionCount += 1;
  298. if ( !WriteFile(hFile, DumpData, sizeof(MEMORY_BASIC_INFORMATION), &cb, NULL) )
  299. {
  300. goto bad_file;
  301. }
  302. }
  303. } while ( rval );
  304. //
  305. // Write the thread contexts.
  306. //
  307. DumpHeader.ThreadOffset = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
  308. do
  309. {
  310. __try {
  311. rval = DmpCallback(DMP_THREAD_CONTEXT,
  312. &DumpData,
  313. &DumpDataLength,
  314. lpv);
  315. } __except (EXCEPTION_EXECUTE_HANDLER) {
  316. rval = FALSE;
  317. }
  318. if ( rval )
  319. {
  320. if ( !WriteFile(hFile, DumpData, DumpDataLength, &cb, NULL) )
  321. {
  322. goto bad_file;
  323. }
  324. DumpHeader.ThreadCount += 1;
  325. }
  326. } while ( rval );
  327. //
  328. // Write the thread states.
  329. //
  330. DumpHeader.ThreadStateOffset = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
  331. do
  332. {
  333. __try {
  334. rval = DmpCallback(DMP_THREAD_STATE,
  335. &DumpData,
  336. &DumpDataLength,
  337. lpv);
  338. } __except (EXCEPTION_EXECUTE_HANDLER) {
  339. rval = FALSE;
  340. }
  341. if ( rval )
  342. {
  343. if ( !WriteFile(hFile, DumpData, sizeof(CRASH_THREAD), &cb, NULL) )
  344. {
  345. goto bad_file;
  346. }
  347. }
  348. } while ( rval );
  349. //
  350. // Write the module table.
  351. //
  352. DumpHeader.ModuleOffset = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
  353. do
  354. {
  355. __try {
  356. rval = DmpCallback(DMP_MODULE,
  357. &DumpData,
  358. &DumpDataLength,
  359. lpv);
  360. } __except (EXCEPTION_EXECUTE_HANDLER) {
  361. rval = FALSE;
  362. }
  363. if ( rval )
  364. {
  365. if ( !WriteFile(hFile,
  366. DumpData,
  367. sizeof(CRASH_MODULE) + ((PCRASH_MODULE)DumpData)->ImageNameLength,
  368. &cb,
  369. NULL) )
  370. {
  371. goto bad_file;
  372. }
  373. DumpHeader.ModuleCount += 1;
  374. }
  375. } while ( rval );
  376. //
  377. // Write the virtual memory
  378. //
  379. DumpHeader.DataOffset = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
  380. do
  381. {
  382. __try {
  383. rval = DmpCallback(DMP_MEMORY_DATA,
  384. &DumpData,
  385. &DumpDataLength,
  386. lpv);
  387. } __except (EXCEPTION_EXECUTE_HANDLER) {
  388. rval = FALSE;
  389. }
  390. if ( rval )
  391. {
  392. if ( !WriteFile(hFile, DumpData, DumpDataLength, &cb, NULL) )
  393. {
  394. goto bad_file;
  395. }
  396. }
  397. } while ( rval );
  398. //
  399. // The VersionInfo is optional.
  400. //
  401. DumpHeader.VersionInfoOffset = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
  402. //
  403. // re-write the dump header with some valid data.
  404. //
  405. GetVersionEx(&OsVersion);
  406. DumpHeader.Signature = USERMODE_CRASHDUMP_SIGNATURE;
  407. DumpHeader.MajorVersion = OsVersion.dwMajorVersion;
  408. DumpHeader.MinorVersion = OsVersion.dwMinorVersion;
  409. #ifdef _X86_
  410. DumpHeader.MachineImageType = IMAGE_FILE_MACHINE_I386;
  411. DumpHeader.ValidDump = USERMODE_CRASHDUMP_VALID_DUMP32;
  412. #else
  413. DumpHeader.MachineImageType = IMAGE_FILE_MACHINE_IA64;
  414. DumpHeader.ValidDump = USERMODE_CRASHDUMP_VALID_DUMP64;
  415. #endif
  416. SetFilePointer(hFile, 0, 0, FILE_BEGIN);
  417. if ( !WriteFile(hFile, &DumpHeader, sizeof(DumpHeader), &cb, NULL) )
  418. {
  419. goto bad_file;
  420. }
  421. CloseHandle(hFile);
  422. return TRUE;
  423. bad_file:
  424. CloseHandle(hFile);
  425. DeleteFile(pszFileName);
  426. return FALSE;
  427. }
  428. BOOL
  429. GenerateUserModeDump(
  430. LPTSTR pszFileName,
  431. PPROCESS_INFO pProcess,
  432. LPEXCEPTION_DEBUG_INFO ed
  433. )
  434. {
  435. CRASH_DUMP_INFO CrashdumpInfo = {0};
  436. BOOL bRet;
  437. PTHREAD_INFO pThread;
  438. CrashdumpInfo.mi.SizeOfStruct = sizeof(CrashdumpInfo.mi);
  439. CrashdumpInfo.pProcess = pProcess;
  440. CrashdumpInfo.ExceptionInfo = ed;
  441. //
  442. // Get the thread context for all the threads.
  443. //
  444. pThread = pProcess->pFirstThreadInfo;
  445. while ( pThread != NULL )
  446. {
  447. pThread->Context.ContextFlags = CONTEXT_FULL;
  448. GetThreadContext(pThread->hThread, &pThread->Context);
  449. pThread = pThread->pNext;
  450. }
  451. //
  452. // Get first entry in the module list.
  453. //
  454. if ( !SymInitialize(pProcess->hProcess, NULL, FALSE) )
  455. {
  456. return FALSE;
  457. }
  458. if ( !SymGetModuleInfo(pProcess->hProcess, 0, &CrashdumpInfo.mi) )
  459. {
  460. return FALSE;
  461. }
  462. CrashdumpInfo.CrashModule = (PCRASH_MODULE)LocalAlloc(LPTR, 4096);
  463. bRet = CreateUserDump(pszFileName, CrashDumpCallback, &CrashdumpInfo);
  464. LocalFree(CrashdumpInfo.CrashModule);
  465. return bRet;
  466. }