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.

1561 lines
36 KiB

  1. /*++
  2. Copyright (c) 1997-2000 Microsoft Corporation
  3. Module Name:
  4. process.c
  5. Abstract:
  6. Manipulation routines for cpdata structures.
  7. Author:
  8. Melur Raghuraman (mraghu) 03-Oct-1997
  9. Environment:
  10. Revision History:
  11. --*/
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14. #include "cpdata.h"
  15. #include "tracectr.h"
  16. #include "item.h"
  17. extern PTRACE_CONTEXT_BLOCK TraceContext;
  18. extern PWCHAR CpdiGuidToString(PWCHAR s, LPGUID piid);
  19. VOID
  20. InitDiskRecord(
  21. PTDISK_RECORD pDisk,
  22. ULONG DiskNumber
  23. )
  24. {
  25. PWCHAR name;
  26. if (pDisk == NULL){
  27. return;
  28. }
  29. memset(pDisk, 0, sizeof(TDISK_RECORD));
  30. InitializeListHead(&pDisk->ProcessListHead);
  31. InitializeListHead(&pDisk->FileListHead);
  32. pDisk->DiskNumber = DiskNumber;
  33. name = (PWCHAR)malloc(16 * sizeof(WCHAR));
  34. if (name != NULL) {
  35. wsprintfW(name, L"Disk%d", DiskNumber);
  36. }
  37. pDisk->DiskName = name;
  38. }
  39. VOID
  40. InitFileRecord(
  41. PFILE_RECORD pFile
  42. )
  43. {
  44. if (pFile == NULL){
  45. return;
  46. }
  47. memset(pFile, 0, sizeof(FILE_RECORD));
  48. InitializeListHead(&pFile->ProtoProcessListHead);
  49. }
  50. VOID
  51. InitThreadRecord(
  52. PTHREAD_RECORD pThread
  53. )
  54. {
  55. if (pThread == NULL)
  56. return;
  57. memset(pThread, 0, sizeof(THREAD_RECORD));
  58. InitializeListHead( &pThread->DiskListHead );
  59. InitializeListHead( &pThread->TransListHead );
  60. InitializeListHead( &pThread->HPFReadListHead );
  61. InitializeListHead( &pThread->HPFWriteListHead );
  62. }
  63. VOID
  64. InitTransRecord(
  65. PTRANS_RECORD pTrans
  66. )
  67. {
  68. if (pTrans == NULL)
  69. return;
  70. memset(pTrans, 0, sizeof(TRANS_RECORD));
  71. InitializeListHead(&pTrans->SubTransListHead);
  72. }
  73. VOID
  74. InitMofData(
  75. PMOF_DATA pMofData
  76. )
  77. {
  78. if (pMofData == NULL)
  79. return;
  80. memset(pMofData, 0, sizeof(MOF_DATA));
  81. pMofData->MaxKCpu = -1;
  82. pMofData->MinKCpu = -1;
  83. pMofData->MaxUCpu = -1;
  84. pMofData->MinUCpu = -1;
  85. }
  86. VOID
  87. InitProcessRecord(
  88. PPROCESS_RECORD pProcess
  89. )
  90. {
  91. if (pProcess == NULL)
  92. return;
  93. memset(pProcess, 0, sizeof(PROCESS_RECORD));
  94. InitializeListHead( &pProcess->ThreadListHead );
  95. InitializeListHead( &pProcess->DiskListHead );
  96. InitializeListHead( &pProcess->FileListHead );
  97. InitializeListHead( &pProcess->ModuleListHead );
  98. InitializeListHead( &pProcess->HPFListHead );
  99. }
  100. BOOLEAN AddModuleRecord(PMODULE_RECORD * ppModule,
  101. ULONG lBaseAddress,
  102. ULONG lModuleSize,
  103. WCHAR * strModuleName)
  104. {
  105. PMODULE_RECORD pModule;
  106. if ( (ppModule == NULL) || (strModuleName == NULL) ) {
  107. return FALSE;
  108. }
  109. pModule = (PMODULE_RECORD)malloc(sizeof(MODULE_RECORD));
  110. if(NULL != pModule){
  111. memset(pModule, 0, sizeof(MODULE_RECORD));
  112. pModule->strModuleName =
  113. malloc(sizeof(WCHAR) * (lstrlenW(strModuleName) + 1));
  114. if (pModule->strModuleName == NULL)
  115. {
  116. free(pModule);
  117. pModule = NULL;
  118. }
  119. else
  120. {
  121. wcscpy(pModule->strModuleName, strModuleName);
  122. pModule->lBaseAddress = lBaseAddress;
  123. pModule->lModuleSize = lModuleSize;
  124. }
  125. }
  126. * ppModule = pModule;
  127. return (BOOLEAN) (* ppModule != NULL);
  128. }
  129. BOOLEAN AddHPFFileRecord(
  130. PHPF_FILE_RECORD * ppHPFFileRecord,
  131. ULONG RecordID,
  132. ULONG IrpFlags,
  133. ULONG DiskNumber,
  134. ULONGLONG ByteOffset,
  135. ULONG BytesCount,
  136. PVOID fDO
  137. )
  138. {
  139. PHPF_FILE_RECORD pHPFFileRecord = malloc(sizeof(HPF_FILE_RECORD));
  140. if (pHPFFileRecord)
  141. {
  142. pHPFFileRecord->RecordID = RecordID;
  143. pHPFFileRecord->IrpFlags = IrpFlags;
  144. pHPFFileRecord->DiskNumber = DiskNumber;
  145. pHPFFileRecord->ByteOffset = ByteOffset;
  146. pHPFFileRecord->BytesCount = BytesCount;
  147. pHPFFileRecord->fDO = fDO;
  148. }
  149. * ppHPFFileRecord = pHPFFileRecord;
  150. return (BOOLEAN) (* ppHPFFileRecord != NULL);
  151. }
  152. BOOLEAN AddHPFRecord(
  153. PHPF_RECORD * ppHPFRecord,
  154. ULONG lFaultAddress,
  155. PVOID fDO,
  156. LONG ByteCount,
  157. LONGLONG ByteOffset
  158. )
  159. {
  160. PHPF_RECORD pHPFRecord = malloc(sizeof(HPF_RECORD));
  161. if (pHPFRecord)
  162. {
  163. pHPFRecord->fDO = fDO;
  164. pHPFRecord->lFaultAddress = lFaultAddress;
  165. pHPFRecord->lByteCount = ByteCount;
  166. pHPFRecord->lByteOffset = ByteOffset;
  167. InitializeListHead(& pHPFRecord->HPFReadListHead);
  168. }
  169. * ppHPFRecord = pHPFRecord;
  170. return (BOOLEAN) (* ppHPFRecord != NULL);
  171. }
  172. void DeleteHPFRecord(
  173. PHPF_RECORD pHPFRecord
  174. )
  175. {
  176. PLIST_ENTRY pHead;
  177. PLIST_ENTRY pNext;
  178. PHPF_FILE_RECORD pHPFFileRecord;
  179. if (!pHPFRecord)
  180. return;
  181. RemoveEntryList(& pHPFRecord->Entry);
  182. pHead = & pHPFRecord->HPFReadListHead;
  183. pNext = pHead->Flink;
  184. while (pNext != pHead)
  185. {
  186. pHPFFileRecord = CONTAINING_RECORD(pNext, HPF_FILE_RECORD, Entry);
  187. pNext = pNext->Flink;
  188. RemoveEntryList(& pHPFFileRecord->Entry);
  189. free(pHPFFileRecord);
  190. }
  191. free(pHPFRecord);
  192. return;
  193. }
  194. VOID
  195. InitWorkloadRecord(
  196. PWORKLOAD_RECORD pWorkload
  197. )
  198. {
  199. if (pWorkload == NULL)
  200. return;
  201. memset(pWorkload, 0, sizeof(WORKLOAD_RECORD));
  202. InitializeListHead( &pWorkload->DiskListHead );
  203. }
  204. VOID
  205. DeleteWorkloadRecord(
  206. PWORKLOAD_RECORD pWorkload
  207. )
  208. {
  209. if (pWorkload == NULL)
  210. return;
  211. }
  212. PPROTO_PROCESS_RECORD
  213. AddProtoProcess(
  214. PFILE_RECORD pFile,
  215. PPROCESS_RECORD pProcess
  216. )
  217. {
  218. PPROTO_PROCESS_RECORD pProto;
  219. if (pFile == NULL || pProcess == NULL)
  220. return NULL;
  221. pProto = malloc(sizeof(PROTO_PROCESS_RECORD));
  222. if (pProto == NULL) {
  223. return NULL;
  224. }
  225. memset(pProto, 0, sizeof(PROTO_PROCESS_RECORD));
  226. pProto->ProcessRecord = pProcess;
  227. InsertHeadList( &pFile->ProtoProcessListHead, &pProto->Entry );
  228. return pProto;
  229. }
  230. PPROTO_PROCESS_RECORD
  231. FindProtoProcessRecord(
  232. PFILE_RECORD pFile,
  233. PPROCESS_RECORD pProcess
  234. )
  235. {
  236. PLIST_ENTRY Next, Head;
  237. PPROTO_PROCESS_RECORD pProto;
  238. if (pFile == NULL || pProcess == NULL)
  239. return NULL;
  240. Head = &pFile->ProtoProcessListHead;
  241. Next = Head->Flink;
  242. while (Next != Head) {
  243. pProto = CONTAINING_RECORD(Next, PROTO_PROCESS_RECORD, Entry);
  244. if (pProcess == pProto->ProcessRecord)
  245. return pProto;
  246. Next = Next->Flink;
  247. }
  248. return (AddProtoProcess(pFile, pProcess));
  249. }
  250. BOOLEAN
  251. AddProcess(
  252. ULONG ProcessId,
  253. PPROCESS_RECORD *ReturnedProcess
  254. )
  255. {
  256. PPROCESS_RECORD Process;
  257. PMODULE_RECORD pModule = NULL;
  258. Process = malloc(sizeof(PROCESS_RECORD));
  259. if (Process == NULL) {
  260. return FALSE;
  261. }
  262. InitProcessRecord( Process );
  263. Process->PID = ProcessId;
  264. if (!AddModuleRecord(& pModule, 0, 0, L"other")) {
  265. free(Process);
  266. return FALSE;
  267. }
  268. pModule->pProcess = Process;
  269. EnterTracelibCritSection();
  270. InsertHeadList( &CurrentSystem.ProcessListHead, &Process->Entry );
  271. InsertHeadList(& Process->ModuleListHead, & pModule->Entry);
  272. LeaveTracelibCritSection();
  273. *ReturnedProcess = Process;
  274. return TRUE;
  275. }
  276. BOOLEAN
  277. DeleteProcess(
  278. PPROCESS_RECORD Process
  279. )
  280. {
  281. PLIST_ENTRY Next, Head;
  282. PTHREAD_RECORD Thread;
  283. PTDISK_RECORD Disk;
  284. PFILE_RECORD pFile;
  285. PMODULE_RECORD pModule;
  286. PHPF_RECORD pHPFRecord;
  287. if (Process == NULL)
  288. return FALSE;
  289. EnterTracelibCritSection();
  290. RemoveEntryList( &Process->Entry );
  291. LeaveTracelibCritSection();
  292. Head = &Process->ThreadListHead;
  293. Next = Head->Flink;
  294. while (Next != Head) {
  295. Thread = CONTAINING_RECORD( Next, THREAD_RECORD, Entry );
  296. Next = Next->Flink;
  297. DeleteThread( Thread );
  298. }
  299. Head = &Process->DiskListHead;
  300. Next = Head->Flink;
  301. while (Next != Head) {
  302. Disk = CONTAINING_RECORD( Next, TDISK_RECORD, Entry );
  303. Next = Next->Flink;
  304. DeleteDisk( Disk );
  305. }
  306. Head = &Process->HPFListHead;
  307. Next = Head->Flink;
  308. while (Next != Head) {
  309. pHPFRecord = CONTAINING_RECORD(Next, HPF_RECORD, Entry);
  310. Next = Next->Flink;
  311. DeleteHPFRecord(pHPFRecord);
  312. }
  313. Head = &Process->FileListHead;
  314. Next = Head->Flink;
  315. while (Next != Head) {
  316. pFile = CONTAINING_RECORD( Next, FILE_RECORD, Entry );
  317. Next = Next->Flink;
  318. RemoveEntryList( &pFile->Entry );
  319. if (pFile->FileName != NULL)
  320. free(pFile->FileName);
  321. free(pFile);
  322. }
  323. Head = &Process->ModuleListHead;
  324. Next = Head->Flink;
  325. while (Next != Head)
  326. {
  327. pModule = CONTAINING_RECORD(Next, MODULE_RECORD, Entry);
  328. Next = Next->Flink;
  329. RemoveEntryList(& pModule->Entry);
  330. if(pModule->strModuleName)
  331. {
  332. free(pModule->strModuleName);
  333. }
  334. free(pModule);
  335. }
  336. if (Process->ImageName != NULL)
  337. free(Process->ImageName);
  338. if (Process->UserName != NULL)
  339. free(Process->UserName);
  340. free( Process );
  341. return TRUE;
  342. }
  343. BOOLEAN
  344. AddThread(
  345. ULONG ThreadId,
  346. PEVENT_TRACE pEvent,
  347. PTHREAD_RECORD * ResultThread
  348. )
  349. {
  350. PTHREAD_RECORD Thread;
  351. PEVENT_TRACE_HEADER pHeader = (PEVENT_TRACE_HEADER) & pEvent->Header;
  352. int i;
  353. Thread = malloc(sizeof(THREAD_RECORD));
  354. if (Thread == NULL)
  355. {
  356. return FALSE;
  357. }
  358. InitThreadRecord(Thread);
  359. Thread->TimeStart = Thread->TimeEnd
  360. = (ULONGLONG) pHeader->TimeStamp.QuadPart;
  361. Thread->TID = ThreadId;
  362. Thread->ProcessorID = pEvent->ClientContext & 0x000000FF;
  363. i = (int)Thread->TID;
  364. i = i % THREAD_HASH_TABLESIZE;
  365. InsertHeadList(&CurrentSystem.ThreadHashList[i], &Thread->Entry);
  366. *ResultThread = Thread;
  367. return TRUE;
  368. }
  369. BOOLEAN
  370. DeleteThread(
  371. PTHREAD_RECORD Thread
  372. )
  373. {
  374. PLIST_ENTRY pHead;
  375. PLIST_ENTRY pNext;
  376. PHPF_FILE_RECORD pHPFFileRecord;
  377. if (Thread == NULL)
  378. return FALSE;
  379. RemoveEntryList( &Thread->Entry );
  380. DeleteTransList( &Thread->TransListHead, 0 );
  381. pHead = & Thread->HPFReadListHead;
  382. pNext = pHead->Flink;
  383. while (pNext != pHead)
  384. {
  385. pHPFFileRecord = CONTAINING_RECORD(pNext, HPF_FILE_RECORD, Entry);
  386. pNext = pNext->Flink;
  387. RemoveEntryList(& pHPFFileRecord->Entry);
  388. free(pHPFFileRecord);
  389. }
  390. pHead = & Thread->HPFWriteListHead;
  391. pNext = pHead->Flink;
  392. while (pNext != pHead)
  393. {
  394. pHPFFileRecord = CONTAINING_RECORD(pNext, HPF_FILE_RECORD, Entry);
  395. pNext = pNext->Flink;
  396. RemoveEntryList(& pHPFFileRecord->Entry);
  397. free(pHPFFileRecord);
  398. }
  399. free( Thread );
  400. return TRUE;
  401. }
  402. PTRANS_RECORD
  403. FindTransByList(
  404. PLIST_ENTRY Head,
  405. LPGUID pGuid,
  406. ULONG level
  407. )
  408. {
  409. PLIST_ENTRY Next;
  410. PTRANS_RECORD pTrans = NULL;
  411. // Recursively look for the list that does
  412. // not contain a running guid
  413. //
  414. if (level <= MAX_TRANS_LEVEL && Head != NULL)
  415. {
  416. Next = Head->Flink;
  417. while (Next != Head)
  418. {
  419. pTrans = CONTAINING_RECORD(Next, TRANS_RECORD, Entry);
  420. if (pTrans->bStarted)
  421. {
  422. if ( (level == 0 || level == 1)
  423. && IsEqualGUID(pTrans->pGuid, pGuid))
  424. {
  425. return pTrans;
  426. }
  427. else if (level > 0)
  428. {
  429. return FindTransByList(& pTrans->SubTransListHead,
  430. pGuid,
  431. level - 1);
  432. }
  433. }
  434. Next = Next->Flink;
  435. }
  436. }
  437. // Found the correct list; now find
  438. // the matching transaction
  439. //
  440. if (level == 0 && Head != NULL)
  441. {
  442. Next = Head->Flink;
  443. while (Next != Head)
  444. {
  445. pTrans = CONTAINING_RECORD(Next, TRANS_RECORD, Entry);
  446. if (IsEqualGUID( pTrans->pGuid, pGuid))
  447. {
  448. return pTrans;
  449. }
  450. Next = Next->Flink;
  451. }
  452. // If not Found, go ahead and add it.
  453. //
  454. pTrans = malloc(sizeof(TRANS_RECORD));
  455. if (pTrans == NULL)
  456. {
  457. return NULL;
  458. }
  459. InitTransRecord(pTrans);
  460. pTrans->pGuid = pGuid;
  461. InsertHeadList( Head, &pTrans->Entry );
  462. }
  463. return pTrans;
  464. }
  465. PMOF_DATA
  466. FindMofData(
  467. PMOF_INFO pMofInfo,
  468. PWCHAR strSortKey
  469. )
  470. {
  471. PLIST_ENTRY Next;
  472. PLIST_ENTRY Head;
  473. PMOF_DATA pMofData = NULL;
  474. if (pMofInfo == NULL) {
  475. return NULL;
  476. }
  477. Head = &pMofInfo->DataListHead;
  478. if (Head != NULL)
  479. {
  480. Next = Head->Flink;
  481. while (Next != Head)
  482. {
  483. pMofData = CONTAINING_RECORD(Next, MOF_DATA, Entry);
  484. if (strSortKey == NULL && pMofData->strSortKey == NULL)
  485. {
  486. return pMofData;
  487. }
  488. else if ( strSortKey != NULL
  489. && pMofData->strSortKey != NULL
  490. && !wcscmp(pMofData->strSortKey, strSortKey))
  491. {
  492. return pMofData;
  493. }
  494. Next = Next->Flink;
  495. }
  496. // If not Found, go ahead and add it.
  497. //
  498. pMofData = (PMOF_DATA)malloc(sizeof(MOF_DATA));
  499. if (pMofData == NULL)
  500. {
  501. return NULL;
  502. }
  503. InitMofData(pMofData);
  504. if (strSortKey != NULL)
  505. {
  506. pMofData->strSortKey =
  507. (PWCHAR) malloc((lstrlenW(strSortKey) + 1) * sizeof(WCHAR));
  508. if (pMofData->strSortKey != NULL) {
  509. wcscpy(pMofData->strSortKey, strSortKey);
  510. }
  511. }
  512. InsertHeadList(Head, &pMofData->Entry);
  513. }
  514. return pMofData;
  515. }
  516. BOOLEAN
  517. DeleteTrans(
  518. PTRANS_RECORD Trans
  519. )
  520. {
  521. if (Trans == NULL)
  522. return FALSE;
  523. RemoveEntryList( &Trans->Entry );
  524. free( Trans );
  525. return TRUE;
  526. }
  527. BOOLEAN
  528. DeleteTransList(
  529. PLIST_ENTRY Head,
  530. ULONG level
  531. )
  532. {
  533. PLIST_ENTRY Next;
  534. PTRANS_RECORD pTrans;
  535. if( Head == NULL || level > MAX_TRANS_LEVEL )
  536. return FALSE;
  537. Next = Head->Flink;
  538. while(Next != Head){
  539. pTrans = CONTAINING_RECORD( Next, TRANS_RECORD, Entry );
  540. Next = Next->Flink;
  541. DeleteTransList( &pTrans->SubTransListHead, level+1);
  542. DeleteTrans( pTrans );
  543. }
  544. return TRUE;
  545. }
  546. PPROCESS_RECORD
  547. FindProcessById(
  548. ULONG Id,
  549. BOOLEAN CheckAlive
  550. )
  551. {
  552. PLIST_ENTRY Next, Head;
  553. PPROCESS_RECORD Process=NULL;
  554. EnterTracelibCritSection();
  555. Head = &CurrentSystem.ProcessListHead;
  556. Next = Head->Flink;
  557. while (Next != Head) {
  558. Process = CONTAINING_RECORD( Next, PROCESS_RECORD, Entry );
  559. if (Process->PID == Id) {
  560. LeaveTracelibCritSection();
  561. if ((Process->DeadFlag) && (CheckAlive))
  562. return NULL;
  563. else
  564. return Process;
  565. }
  566. Next = Next->Flink;
  567. }
  568. LeaveTracelibCritSection();
  569. return NULL;
  570. }
  571. PTHREAD_RECORD
  572. FindGlobalThreadById(
  573. ULONG ThreadId,
  574. PEVENT_TRACE pEvent
  575. )
  576. {
  577. PLIST_ENTRY Next,
  578. Head;
  579. PTHREAD_RECORD Thread;
  580. PEVENT_TRACE_HEADER pHeader = (PEVENT_TRACE_HEADER) & pEvent->Header;
  581. int i = (int)ThreadId;
  582. i = i % THREAD_HASH_TABLESIZE;
  583. Head = &CurrentSystem.ThreadHashList[i];
  584. Next = Head->Flink;
  585. while (Next != Head)
  586. {
  587. Thread = CONTAINING_RECORD(Next, THREAD_RECORD, Entry);
  588. Next = Next->Flink;
  589. if (Thread->TID == ThreadId)
  590. {
  591. if (ThreadId == 0)
  592. {
  593. ULONG ProcessorId = pEvent->ClientContext & 0x000000FF;
  594. if (ProcessorId != Thread->ProcessorID)
  595. {
  596. continue;
  597. }
  598. }
  599. if (!Thread->DeadFlag)
  600. {
  601. return Thread;
  602. }
  603. else if ( Thread->TimeEnd
  604. == (ULONGLONG) pHeader->TimeStamp.QuadPart)
  605. {
  606. return Thread;
  607. }
  608. else
  609. {
  610. // The alive thead must be at the head of the list
  611. // otherwise bail
  612. //
  613. return NULL;
  614. }
  615. }
  616. }
  617. return NULL;
  618. }
  619. PWORKLOAD_RECORD
  620. FindWorkloadById(
  621. ULONG Id
  622. )
  623. {
  624. PLIST_ENTRY Next, Head;
  625. PWORKLOAD_RECORD Workload = NULL;
  626. Head = &CurrentSystem.WorkloadListHead;
  627. Next = Head->Flink;
  628. while (Next != Head) {
  629. Workload = CONTAINING_RECORD( Next, WORKLOAD_RECORD, Entry );
  630. if (Workload->ClassNumber == Id) {
  631. return Workload;
  632. }
  633. Next = Next->Flink;
  634. }
  635. return NULL;
  636. }
  637. BOOLEAN
  638. AddDisk(
  639. ULONG DiskNumber,
  640. PTDISK_RECORD *ReturnedDisk
  641. )
  642. {
  643. PTDISK_RECORD Disk;
  644. Disk = malloc(sizeof(TDISK_RECORD));
  645. if (Disk == NULL) {
  646. return FALSE;
  647. }
  648. InitDiskRecord(Disk, DiskNumber);
  649. Disk->DiskNumber = DiskNumber;
  650. InsertHeadList( &CurrentSystem.GlobalDiskListHead, &Disk->Entry );
  651. *ReturnedDisk = Disk;
  652. return TRUE;
  653. }
  654. BOOLEAN
  655. DeleteDisk(
  656. PTDISK_RECORD Disk
  657. )
  658. {
  659. PLIST_ENTRY Head, Next;
  660. PPROCESS_RECORD Process;
  661. PFILE_RECORD File;
  662. if (Disk == NULL)
  663. return FALSE;
  664. RemoveEntryList( &Disk->Entry );
  665. if (Disk->DiskName != NULL)
  666. free(Disk->DiskName);
  667. Head = &Disk->ProcessListHead;
  668. Next = Head->Flink;
  669. while (Next != Head) {
  670. Process = CONTAINING_RECORD( Next, PROCESS_RECORD, Entry );
  671. Next = Next->Flink;
  672. DeleteProcess( Process );
  673. }
  674. Head = &Disk->FileListHead;
  675. Next = Head->Flink;
  676. while (Next != Head) {
  677. File = CONTAINING_RECORD( Next, FILE_RECORD, Entry );
  678. Next = Next->Flink;
  679. DeleteFileRecord( File );
  680. }
  681. free( Disk );
  682. return TRUE;
  683. }
  684. PTDISK_RECORD
  685. FindLocalDiskById(
  686. PLIST_ENTRY Head,
  687. ULONG Id
  688. )
  689. {
  690. PLIST_ENTRY Next;
  691. PTDISK_RECORD Disk = NULL;
  692. if (Head == NULL)
  693. return NULL;
  694. Next = Head->Flink;
  695. while (Next != Head) {
  696. Disk = CONTAINING_RECORD( Next, TDISK_RECORD, Entry );
  697. if (Disk->DiskNumber == Id) {
  698. return Disk;
  699. }
  700. Next = Next->Flink;
  701. }
  702. // If not Found, go ahead and add it.
  703. //
  704. Disk = malloc(sizeof(TDISK_RECORD));
  705. if (Disk == NULL) {
  706. return FALSE;
  707. }
  708. InitDiskRecord(Disk, Id);
  709. Disk->DiskNumber = Id;
  710. InsertHeadList( Head, &Disk->Entry );
  711. return Disk;
  712. }
  713. PTDISK_RECORD
  714. FindProcessDiskById(
  715. PPROCESS_RECORD pProcess,
  716. ULONG Id
  717. )
  718. {
  719. PLIST_ENTRY Next, Head;
  720. PTDISK_RECORD Disk = NULL;
  721. if (pProcess == NULL)
  722. return NULL;
  723. Head = &pProcess->DiskListHead;
  724. Next = Head->Flink;
  725. while (Next != Head) {
  726. Disk = CONTAINING_RECORD( Next, TDISK_RECORD, Entry );
  727. if (Disk->DiskNumber == Id) {
  728. return Disk;
  729. }
  730. Next = Next->Flink;
  731. }
  732. // If not Found, go ahead and add it.
  733. //
  734. Disk = malloc(sizeof(TDISK_RECORD));
  735. if (Disk == NULL) {
  736. return FALSE;
  737. }
  738. InitDiskRecord(Disk, Id);
  739. Disk->DiskNumber = Id;
  740. InsertHeadList( &pProcess->DiskListHead, &Disk->Entry );
  741. return Disk;
  742. }
  743. PPROCESS_RECORD
  744. FindDiskProcessById(
  745. PTDISK_RECORD pDisk,
  746. ULONG Id
  747. )
  748. {
  749. PLIST_ENTRY Next, Head;
  750. PPROCESS_RECORD Process = NULL;
  751. PPROCESS_RECORD gProcess = NULL;
  752. if (pDisk == NULL)
  753. return NULL;
  754. Head = &pDisk->ProcessListHead;
  755. Next = Head->Flink;
  756. while (Next != Head) {
  757. Process = CONTAINING_RECORD( Next, PROCESS_RECORD, Entry );
  758. if (Process->PID == Id) {
  759. return Process;
  760. }
  761. Next = Next->Flink;
  762. }
  763. // If not Found, go ahead and add it.
  764. //
  765. Process = malloc(sizeof(PROCESS_RECORD));
  766. if (Process == NULL) {
  767. return FALSE;
  768. }
  769. InitProcessRecord(Process);
  770. Process->PID = Id;
  771. // Find the global Process Record and copy the UserName and Image.
  772. //
  773. gProcess = FindProcessById(Id, FALSE);
  774. if (gProcess != NULL) {
  775. if ( ! IsEmpty( gProcess->UserName ) ) {
  776. ASSIGN_STRING( Process->UserName, gProcess->UserName );
  777. if( NULL == Process->UserName ) {
  778. free(Process);
  779. return NULL;
  780. }
  781. }
  782. if (! IsEmpty( gProcess->ImageName ) ) {
  783. ASSIGN_STRING( Process->ImageName, gProcess->ImageName );
  784. if (Process->UserName == NULL) {
  785. free(Process);
  786. return NULL;
  787. }
  788. }
  789. }
  790. InsertHeadList( &pDisk->ProcessListHead, &Process->Entry );
  791. return Process;
  792. }
  793. PTDISK_RECORD
  794. FindGlobalDiskById(
  795. ULONG Id
  796. )
  797. {
  798. PLIST_ENTRY Next, Head;
  799. PTDISK_RECORD Disk = NULL;
  800. Head = &CurrentSystem.GlobalDiskListHead;
  801. Next = Head->Flink;
  802. while (Next != Head) {
  803. Disk = CONTAINING_RECORD( Next, TDISK_RECORD, Entry );
  804. if (Disk->DiskNumber == Id) {
  805. return Disk;
  806. }
  807. Next = Next->Flink;
  808. }
  809. return NULL;
  810. }
  811. VOID
  812. DeleteMofVersion(
  813. PMOF_VERSION pMofVersion,
  814. FILE* file
  815. )
  816. {
  817. PLIST_ENTRY Next, Head;
  818. PITEM_DESC pMofItem;
  819. //
  820. // Traverse through the MOF_VERSION list and
  821. // delete each one
  822. //
  823. if (pMofVersion == NULL)
  824. return;
  825. if( NULL != file ){
  826. fwprintf( file, L" %s (Type:%d Level:%d Version:%d)\n",
  827. pMofVersion->strType ? pMofVersion->strType : L"Default",
  828. pMofVersion->TypeIndex,
  829. pMofVersion->Level,
  830. pMofVersion->Version
  831. );
  832. }
  833. Head = &pMofVersion->ItemHeader;
  834. Next = Head->Flink;
  835. while (Head != Next) {
  836. pMofItem = CONTAINING_RECORD(Next, ITEM_DESC, Entry);
  837. Next = Next->Flink;
  838. RemoveEntryList( &pMofItem->Entry );
  839. if (pMofItem->strDescription != NULL){
  840. if( NULL != file ){
  841. fwprintf( file, L" %s\n",
  842. pMofItem->strDescription
  843. );
  844. }
  845. free (pMofItem->strDescription);
  846. }
  847. free (pMofItem);
  848. }
  849. }
  850. VOID
  851. DeleteMofInfo(
  852. PMOF_INFO pMofInfo,
  853. FILE* f
  854. )
  855. {
  856. PLIST_ENTRY Next, Head;
  857. PMOF_VERSION pMofVersion;
  858. //
  859. // Traverse through the MOF_VERSION list and
  860. // delete each one
  861. //
  862. if (pMofInfo == NULL){
  863. return;
  864. }
  865. if( NULL != f ){
  866. WCHAR buffer[MAXSTR];
  867. fwprintf( f, L"%s\n",
  868. pMofInfo->strDescription ? pMofInfo->strDescription : CpdiGuidToString( buffer, &pMofInfo->Guid ) );
  869. }
  870. Head = &pMofInfo->VersionHeader;
  871. Next = Head->Flink;
  872. while (Head != Next) {
  873. pMofVersion = CONTAINING_RECORD(Next, MOF_VERSION, Entry);
  874. Next = Next->Flink;
  875. RemoveEntryList( &pMofVersion->Entry );
  876. DeleteMofVersion( pMofVersion, f );
  877. }
  878. //
  879. // Delete any strings allocated for this structure
  880. //
  881. if (pMofInfo->strDescription){
  882. free(pMofInfo->strDescription);
  883. }
  884. //
  885. // Finally delete the object
  886. //
  887. free(pMofInfo);
  888. }
  889. VOID
  890. Cleanup()
  891. {
  892. PTDISK_RECORD Disk;
  893. PTHREAD_RECORD Thread;
  894. PPROCESS_RECORD Process;
  895. PFILE_RECORD FileRec;
  896. PWORKLOAD_RECORD pWorkload;
  897. PMODULE_RECORD pModule;
  898. PMOF_INFO pMofInfo;
  899. PJOB_RECORD pJob;
  900. PLIST_ENTRY Next, Head;
  901. PLIST_ENTRY EventListHead;
  902. FILE* f = NULL;
  903. // Clean up the Global Disk List for now.
  904. //
  905. EventListHead = &CurrentSystem.EventListHead;
  906. Head = EventListHead;
  907. Next = Head->Flink;
  908. if( (TraceContext->Flags & TRACE_INTERPRET) && NULL != TraceContext->CompFileName ){
  909. f = _wfopen( TraceContext->CompFileName, L"w" );
  910. }
  911. while (Head != Next) {
  912. pMofInfo = CONTAINING_RECORD(Next, MOF_INFO, Entry);
  913. Next = Next->Flink;
  914. RemoveEntryList( &pMofInfo->Entry );
  915. DeleteMofInfo(pMofInfo, f);
  916. }
  917. if( NULL != f ){
  918. fclose( f );
  919. f = NULL;
  920. }
  921. Head = &CurrentSystem.GlobalDiskListHead;
  922. Next = Head->Flink;
  923. while (Next != Head) {
  924. Disk = CONTAINING_RECORD( Next, TDISK_RECORD, Entry );
  925. Next = Next->Flink;
  926. DeleteDisk( Disk );
  927. }
  928. // Clean up the Global Thread List for now.
  929. //
  930. Head = &CurrentSystem.GlobalThreadListHead;
  931. Next = Head->Flink;
  932. while (Next != Head) {
  933. Thread = CONTAINING_RECORD( Next, THREAD_RECORD, Entry );
  934. Next = Next->Flink;
  935. DeleteThread( Thread );
  936. }
  937. Head = &CurrentSystem.ProcessListHead;
  938. Next = Head->Flink;
  939. while (Next != Head) {
  940. Process = CONTAINING_RECORD( Next, PROCESS_RECORD, Entry );
  941. Next = Next->Flink;
  942. DeleteProcess( Process );
  943. }
  944. Head = &CurrentSystem.GlobalModuleListHead;
  945. Next = Head->Flink;
  946. while (Next != Head)
  947. {
  948. pModule = CONTAINING_RECORD(Next, MODULE_RECORD, Entry);
  949. Next = Next->Flink;
  950. RemoveEntryList(& pModule->Entry);
  951. if(pModule->strModuleName)
  952. {
  953. free(pModule->strModuleName);
  954. }
  955. free(pModule);
  956. }
  957. Head = &CurrentSystem.HotFileListHead;
  958. Next = Head->Flink;
  959. while (Next != Head) {
  960. FileRec = CONTAINING_RECORD( Next, FILE_RECORD, Entry );
  961. Next = Next->Flink;
  962. DeleteFileRecord( FileRec );
  963. }
  964. // Cleanup workload structures
  965. //
  966. Head = &CurrentSystem.WorkloadListHead;
  967. Next = Head->Flink;
  968. while (Next != Head) {
  969. pWorkload = CONTAINING_RECORD( Next, WORKLOAD_RECORD, Entry );
  970. Next = Next->Flink;
  971. DeleteWorkloadRecord( pWorkload );
  972. }
  973. //
  974. // Cleanup the Job List structures
  975. //
  976. Head = &CurrentSystem.JobListHead;
  977. Next = Head->Flink;
  978. while (Next != Head) {
  979. pJob = CONTAINING_RECORD( Next, JOB_RECORD, Entry );
  980. Next = Next->Flink;
  981. DeleteJobRecord (pJob, FALSE);
  982. }
  983. }
  984. BOOLEAN
  985. AddFile(
  986. WCHAR* fileName,
  987. PFILE_RECORD *ReturnedFile
  988. )
  989. {
  990. PFILE_RECORD fileRec;
  991. if (fileName == NULL)
  992. return FALSE;
  993. fileRec = malloc(sizeof(FILE_RECORD));
  994. if (fileRec == NULL) {
  995. return FALSE;
  996. }
  997. InitFileRecord( fileRec );
  998. fileRec->FileName = malloc( (lstrlenW(fileName)+ 1) * sizeof(WCHAR));
  999. if (fileRec->FileName != NULL) {
  1000. wcscpy(fileRec->FileName, fileName);
  1001. }
  1002. EnterTracelibCritSection();
  1003. InsertHeadList( &CurrentSystem.HotFileListHead, &fileRec->Entry );
  1004. LeaveTracelibCritSection();
  1005. *ReturnedFile = fileRec;
  1006. return TRUE;
  1007. }
  1008. BOOLEAN
  1009. DeleteFileRecord(
  1010. PFILE_RECORD fileRec
  1011. )
  1012. {
  1013. PLIST_ENTRY Next, Head;
  1014. PPROTO_PROCESS_RECORD pProto;
  1015. if (fileRec == NULL)
  1016. return FALSE;
  1017. EnterTracelibCritSection();
  1018. RemoveEntryList( &fileRec->Entry );
  1019. LeaveTracelibCritSection();
  1020. if (fileRec->FileName != NULL)
  1021. free(fileRec->FileName);
  1022. Head = &fileRec->ProtoProcessListHead;
  1023. Next = Head->Flink;
  1024. while (Head != Next) {
  1025. pProto = CONTAINING_RECORD( Next, PROTO_PROCESS_RECORD, Entry);
  1026. Next = Next->Flink;
  1027. RemoveEntryList( &pProto->Entry );
  1028. free(pProto);
  1029. }
  1030. free( fileRec );
  1031. return TRUE;
  1032. }
  1033. PFILE_RECORD
  1034. FindFileRecordByName(
  1035. WCHAR* fileName
  1036. )
  1037. {
  1038. PLIST_ENTRY Next, Head;
  1039. PFILE_RECORD fileRec = NULL;
  1040. if (fileName == NULL)
  1041. return NULL;
  1042. EnterTracelibCritSection();
  1043. Head = &CurrentSystem.HotFileListHead;
  1044. Next = Head->Flink;
  1045. while (Next != Head) {
  1046. fileRec = CONTAINING_RECORD( Next, FILE_RECORD, Entry );
  1047. if (!wcscmp(fileName, fileRec->FileName)) {
  1048. LeaveTracelibCritSection();
  1049. return fileRec;
  1050. }
  1051. Next = Next->Flink;
  1052. }
  1053. LeaveTracelibCritSection();
  1054. return NULL;
  1055. }
  1056. PFILE_RECORD
  1057. FindFileInProcess(
  1058. PPROCESS_RECORD pProcess,
  1059. WCHAR* fileName
  1060. )
  1061. {
  1062. PLIST_ENTRY Next, Head;
  1063. PFILE_RECORD fileRec = NULL;
  1064. if (pProcess == NULL || fileName == NULL)
  1065. return NULL;
  1066. EnterTracelibCritSection();
  1067. Head = &pProcess->FileListHead;
  1068. Next = Head->Flink;
  1069. while (Next != Head) {
  1070. fileRec = CONTAINING_RECORD( Next, FILE_RECORD, Entry );
  1071. if (!wcscmp(fileName, fileRec->FileName)) {
  1072. //ReleaseMutex(CurrentSystem.HotFileListMutex);
  1073. LeaveTracelibCritSection();
  1074. return fileRec;
  1075. }
  1076. Next = Next->Flink;
  1077. }
  1078. LeaveTracelibCritSection();
  1079. return NULL;
  1080. }
  1081. VOID
  1082. AssignClass(
  1083. PPROCESS_RECORD pProcess,
  1084. PTHREAD_RECORD pThread
  1085. )
  1086. {
  1087. UNREFERENCED_PARAMETER(pProcess);
  1088. pThread->ClassNumber = 1; // For the Time being make it single class.
  1089. }
  1090. VOID
  1091. Classify()
  1092. {
  1093. // Assign Class to each Thread or Process.
  1094. //
  1095. PLIST_ENTRY Head, Next;
  1096. PTHREAD_RECORD pThread;
  1097. Head = &CurrentSystem.GlobalThreadListHead;
  1098. Next = Head->Flink;
  1099. while (Next != Head) {
  1100. pThread = CONTAINING_RECORD( Next, THREAD_RECORD, Entry );
  1101. AssignClass(NULL, pThread);
  1102. Aggregate(pThread);
  1103. Next = Next->Flink;
  1104. }
  1105. }
  1106. // Given the number of classes this routine
  1107. // creates and initializes the workload object
  1108. //
  1109. VOID
  1110. InitClass()
  1111. {
  1112. PWORKLOAD_RECORD pWorkload;
  1113. ULONG nclass;
  1114. ULONG i;
  1115. // Create the Class records here.
  1116. //
  1117. nclass = 1;
  1118. CurrentSystem.NumberOfWorkloads = 1;
  1119. for (i = 1; i <= nclass; i++) {
  1120. pWorkload = malloc(sizeof(WORKLOAD_RECORD));
  1121. if (pWorkload == NULL) {
  1122. return;
  1123. }
  1124. InitWorkloadRecord( pWorkload );
  1125. pWorkload->ClassNumber = i;
  1126. InsertHeadList( &CurrentSystem.WorkloadListHead, &pWorkload->Entry );
  1127. }
  1128. }
  1129. PTDISK_RECORD
  1130. FindDiskInList(
  1131. IN PLIST_ENTRY Head,
  1132. IN ULONG Id
  1133. )
  1134. {
  1135. PLIST_ENTRY Next;
  1136. PTDISK_RECORD pDisk = NULL;
  1137. if (Head != NULL) {
  1138. Next = Head->Flink;
  1139. while (Next != Head) {
  1140. pDisk = CONTAINING_RECORD ( Next, TDISK_RECORD, Entry );
  1141. if (pDisk->DiskNumber == Id) {
  1142. return pDisk;
  1143. }
  1144. Next = Next->Flink;
  1145. }
  1146. pDisk = malloc(sizeof(TDISK_RECORD));
  1147. if (pDisk == NULL) {
  1148. return NULL;
  1149. }
  1150. InitDiskRecord( pDisk, Id );
  1151. InsertHeadList( Head, &pDisk->Entry );
  1152. }
  1153. return pDisk;
  1154. }
  1155. VOID
  1156. Aggregate(
  1157. IN PTHREAD_RECORD pThread
  1158. )
  1159. {
  1160. PWORKLOAD_RECORD pWorkload;
  1161. PTDISK_RECORD pDisk, pClassDisk;
  1162. PLIST_ENTRY Next, Head;
  1163. // Aggregate the metrics over each class.
  1164. //
  1165. if ((pWorkload = FindWorkloadById(pThread->ClassNumber)) != NULL) {
  1166. pWorkload->UserCPU += (pThread->UCPUEnd - pThread->UCPUStart)
  1167. * CurrentSystem.TimerResolution;
  1168. pWorkload->KernelCPU += (pThread->KCPUEnd - pThread->KCPUStart)
  1169. * CurrentSystem.TimerResolution;
  1170. //
  1171. // Walk through the Thread Disk records and aggregate them
  1172. // to the class
  1173. Head = &pThread->DiskListHead;
  1174. Next = Head->Flink;
  1175. while (Next != Head) {
  1176. pDisk = CONTAINING_RECORD( Next, TDISK_RECORD, Entry );
  1177. Next = Next->Flink;
  1178. pClassDisk = FindDiskInList(&pWorkload->DiskListHead,
  1179. pDisk->DiskNumber) ;
  1180. if (pClassDisk != NULL) {
  1181. pClassDisk->ReadCount += pDisk->ReadCount;
  1182. pClassDisk->WriteCount += pDisk->WriteCount;
  1183. pClassDisk->ReadSize += (pDisk->ReadCount * pDisk->ReadSize);
  1184. pClassDisk->WriteSize += (pDisk->WriteCount * pDisk->WriteSize);
  1185. pWorkload->ReadCount += pDisk->ReadCount;
  1186. }
  1187. }
  1188. }
  1189. }
  1190. ULONGLONG
  1191. CalculateProcessLifeTime(
  1192. PPROCESS_RECORD pProcess
  1193. )
  1194. {
  1195. BOOLEAN fFirst = TRUE;
  1196. ULONGLONG TimeStart = 0;
  1197. ULONGLONG TimeEnd = 0;
  1198. PLIST_ENTRY pHead = &pProcess->ThreadListHead;
  1199. PLIST_ENTRY pNext = pHead->Flink;
  1200. PTHREAD_RECORD pThread;
  1201. while (pNext != pHead)
  1202. {
  1203. pThread = CONTAINING_RECORD(pNext, THREAD_RECORD, Entry);
  1204. pNext = pNext->Flink;
  1205. if (fFirst)
  1206. {
  1207. TimeStart = pThread->TimeStart;
  1208. TimeEnd = pThread->TimeEnd;
  1209. fFirst = FALSE;
  1210. }
  1211. else if (pThread->TimeStart < TimeStart)
  1212. {
  1213. TimeStart = pThread->TimeStart;
  1214. }
  1215. else if (pThread->TimeEnd > TimeEnd)
  1216. {
  1217. TimeEnd = pThread->TimeEnd;
  1218. }
  1219. }
  1220. return (TimeEnd - TimeStart);
  1221. }
  1222. ULONG
  1223. CalculateProcessKCPU(
  1224. PPROCESS_RECORD pProcess
  1225. )
  1226. {
  1227. ULONG KCPUTotal = 0;
  1228. ULONG KCPUMissing = 0;
  1229. PLIST_ENTRY pHead = &pProcess->ThreadListHead;
  1230. PLIST_ENTRY pNext = pHead->Flink;
  1231. PTHREAD_RECORD pThread;
  1232. while (pNext != pHead)
  1233. {
  1234. pThread = CONTAINING_RECORD(pNext, THREAD_RECORD, Entry);
  1235. pNext = pNext->Flink;
  1236. if (pThread->KCPUEnd > pThread->KCPUStart)
  1237. {
  1238. if ((pProcess->PID != 0) ||
  1239. ((pProcess->PID == 0) && (pThread->TID == 0)) ){
  1240. KCPUTotal += pThread->KCPUEnd - pThread->KCPUStart;
  1241. }
  1242. else {
  1243. KCPUMissing += pThread->KCPUEnd - pThread->KCPUStart;
  1244. }
  1245. }
  1246. }
  1247. return (ULONG) (KCPUTotal * CurrentSystem.TimerResolution);
  1248. }
  1249. ULONG
  1250. CalculateProcessUCPU(
  1251. PPROCESS_RECORD pProcess
  1252. )
  1253. {
  1254. ULONG UCPUTotal = 0;
  1255. ULONG UCPUMissing = 0;
  1256. PLIST_ENTRY pHead = &pProcess->ThreadListHead;
  1257. PLIST_ENTRY pNext = pHead->Flink;
  1258. PTHREAD_RECORD pThread;
  1259. while (pNext != pHead)
  1260. {
  1261. pThread = CONTAINING_RECORD(pNext, THREAD_RECORD, Entry);
  1262. pNext = pNext->Flink;
  1263. if (pThread->UCPUEnd > pThread->UCPUStart)
  1264. {
  1265. if ((pProcess->PID != 0) ||
  1266. ((pProcess->PID == 0) && (pThread->TID == 0)) ) {
  1267. UCPUTotal += pThread->UCPUEnd - pThread->UCPUStart;
  1268. }
  1269. else {
  1270. UCPUMissing += pThread->UCPUEnd - pThread->UCPUStart;
  1271. }
  1272. }
  1273. }
  1274. return (ULONG) (UCPUTotal * CurrentSystem.TimerResolution);
  1275. }
  1276. PJOB_RECORD FindJobRecord(
  1277. ULONG JobId
  1278. )
  1279. {
  1280. PLIST_ENTRY Head, Next;
  1281. PJOB_RECORD pJob;
  1282. EnterTracelibCritSection();
  1283. Head = &CurrentSystem.JobListHead;
  1284. Next = Head->Flink;
  1285. while (Next != Head) {
  1286. pJob = CONTAINING_RECORD ( Next, JOB_RECORD, Entry );
  1287. if (pJob->JobId == JobId) {
  1288. LeaveTracelibCritSection();
  1289. return pJob;
  1290. }
  1291. Next = Next->Flink;
  1292. }
  1293. LeaveTracelibCritSection();
  1294. return NULL;
  1295. }
  1296. //
  1297. // A New Job with a JobId has been found. This routine will create a
  1298. // new job record to track it through various threads in the system
  1299. //
  1300. PJOB_RECORD
  1301. AddJobRecord(
  1302. ULONG JobId
  1303. )
  1304. {
  1305. PJOB_RECORD pJob;
  1306. pJob = malloc(sizeof(JOB_RECORD));
  1307. if (pJob == NULL) {
  1308. SetLastError( ERROR_OUTOFMEMORY);
  1309. return NULL;
  1310. }
  1311. RtlZeroMemory(pJob, sizeof(JOB_RECORD));
  1312. pJob->JobId = JobId;
  1313. EnterTracelibCritSection();
  1314. InsertHeadList( &CurrentSystem.JobListHead, &pJob->Entry );
  1315. LeaveTracelibCritSection();
  1316. return pJob;
  1317. }
  1318. //
  1319. // Deletes a Job record with the JobId. Before deleting the contents
  1320. // of the job record is dumped to a temp file for later reporting.
  1321. //
  1322. ULONG
  1323. DeleteJobRecord(
  1324. PJOB_RECORD pJob,
  1325. ULONG bSave
  1326. )
  1327. {
  1328. if (pJob == NULL)
  1329. return ERROR_INVALID_PARAMETER;
  1330. //
  1331. // Print the Contents of pJob to file.
  1332. //
  1333. // If the -spooler option isn't given to the reducer this fprintf causes the
  1334. // program to crash. Maybe TRACE_SPOOLER should alway be set.
  1335. if (CurrentSystem.TempFile != NULL) {
  1336. fprintf(CurrentSystem.TempFile, "%d, %d, %d, %d, %I64u, %I64u, %I64u, %I64u, %d, %d, %d, %d, %d, %hd, %d, %hd, %hd, %hd, %hd, %hd, %hd, %d\n",
  1337. pJob->JobId, pJob->KCPUTime, pJob->UCPUTime, pJob->ReadIO, pJob->StartTime, pJob->EndTime,
  1338. (pJob->ResponseTime - pJob->PauseTime), pJob->PrintJobTime, pJob->WriteIO,
  1339. pJob->DataType, pJob->JobSize, pJob->Pages, pJob->PagesPerSide,
  1340. pJob->FilesOpened, pJob->GdiJobSize, pJob->Color, pJob->XRes, pJob->YRes,
  1341. pJob->Quality, pJob->Copies, pJob->TTOption,
  1342. pJob->NumberOfThreads);
  1343. }
  1344. EnterTracelibCritSection();
  1345. RemoveEntryList( &pJob->Entry );
  1346. LeaveTracelibCritSection();
  1347. free (pJob);
  1348. return ERROR_SUCCESS;
  1349. }