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.

573 lines
15 KiB

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