3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/dbg/profile.c
6 * PURPOSE: Kernel profiling
8 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
11 /* INCLUDES *****************************************************************/
17 #include <internal/debug.h>
19 /* FUNCTIONS *****************************************************************/
21 #define PROFILE_SESSION_LENGTH 30 /* Session length in seconds */
23 typedef struct _PROFILE_DATABASE_ENTRY
26 } PROFILE_DATABASE_ENTRY
, *PPROFILE_DATABASE_ENTRY
;
28 #define PDE_BLOCK_ENTRIES ((PAGE_SIZE - (sizeof(LIST_ENTRY) + sizeof(ULONG))) / sizeof(PROFILE_DATABASE_ENTRY))
30 typedef struct _PROFILE_DATABASE_BLOCK
34 PROFILE_DATABASE_ENTRY Entries
[PDE_BLOCK_ENTRIES
];
35 } PROFILE_DATABASE_BLOCK
, *PPROFILE_DATABASE_BLOCK
;
37 typedef struct _PROFILE_DATABASE
40 } PROFILE_DATABASE
, *PPROFILE_DATABASE
;
42 typedef struct _SAMPLE_GROUP_INFO
46 CHAR Description
[128];
48 } SAMPLE_GROUP_INFO
, *PSAMPLE_GROUP_INFO
;
50 static volatile BOOLEAN KdbProfilingInitialized
= FALSE
;
51 static volatile BOOLEAN KdbProfilingEnabled
= FALSE
;
52 static volatile BOOLEAN KdbProfilingSuspended
= FALSE
;
53 static PPROFILE_DATABASE KdbProfileDatabase
= NULL
;
54 static KDPC KdbProfilerCollectorDpc
;
55 static HANDLE KdbProfilerThreadHandle
;
56 static CLIENT_ID KdbProfilerThreadCid
;
57 static HANDLE KdbProfilerLogFile
;
58 static KTIMER KdbProfilerTimer
;
59 static KMUTEX KdbProfilerLock
;
60 static BOOLEAN KdbEnableProfiler
= FALSE
;
63 KdbDeleteProfileDatabase(PPROFILE_DATABASE ProfileDatabase
)
65 PLIST_ENTRY current
= NULL
;
67 current
= RemoveHeadList(&ProfileDatabase
->ListHead
);
68 while (current
!= &ProfileDatabase
->ListHead
)
70 PPROFILE_DATABASE_BLOCK block
= CONTAINING_RECORD(
71 current
, PROFILE_DATABASE_BLOCK
, ListEntry
);
73 current
= RemoveHeadList(&ProfileDatabase
->ListHead
);
78 KdbAddEntryToProfileDatabase(PPROFILE_DATABASE ProfileDatabase
, ULONG_PTR Address
)
80 PPROFILE_DATABASE_BLOCK block
;
82 if (IsListEmpty(&ProfileDatabase
->ListHead
))
84 block
= ExAllocatePool(NonPagedPool
, sizeof(PROFILE_DATABASE_BLOCK
));
86 block
->UsedEntries
= 0;
87 InsertTailList(&ProfileDatabase
->ListHead
, &block
->ListEntry
);
88 block
->Entries
[block
->UsedEntries
++].Address
= Address
;
92 block
= CONTAINING_RECORD(ProfileDatabase
->ListHead
.Blink
, PROFILE_DATABASE_BLOCK
, ListEntry
);
93 if (block
->UsedEntries
>= PDE_BLOCK_ENTRIES
)
95 block
= ExAllocatePool(NonPagedPool
, sizeof(PROFILE_DATABASE_BLOCK
));
97 block
->UsedEntries
= 0;
98 InsertTailList(&ProfileDatabase
->ListHead
, &block
->ListEntry
);
100 block
->Entries
[block
->UsedEntries
++].Address
= Address
;
106 KdbEnableProfiler
= TRUE
;
112 if (KdbEnableProfiler
)
114 KdbEnableProfiling();
115 KdbProfilingInitialized
= TRUE
;
120 KdbSuspendProfiling()
122 KdbProfilingSuspended
= TRUE
;
128 KdbProfilingSuspended
= FALSE
;
132 KdbProfilerGetSymbolInfo(PVOID address
, OUT PCH NameBuffer
)
134 PLIST_ENTRY current_entry
;
135 MODULE_TEXT_SECTION
* current
;
136 extern LIST_ENTRY ModuleTextListHead
;
137 ULONG_PTR RelativeAddress
;
141 CHAR FunctionName
[256];
143 current_entry
= ModuleTextListHead
.Flink
;
145 while (current_entry
!= &ModuleTextListHead
&&
146 current_entry
!= NULL
)
149 CONTAINING_RECORD(current_entry
, MODULE_TEXT_SECTION
, ListEntry
);
151 if (address
>= (PVOID
)current
->Base
&&
152 address
< (PVOID
)(current
->Base
+ current
->Length
))
154 RelativeAddress
= (ULONG_PTR
) address
- current
->Base
;
155 Status
= KdbSymGetAddressInformation(¤t
->SymbolInfo
,
160 if (NT_SUCCESS(Status
))
162 sprintf(NameBuffer
, "%s (%s)", FileName
, FunctionName
);
167 current_entry
= current_entry
->Flink
;
173 KdbProfilerLargestSampleGroup(PLIST_ENTRY SamplesListHead
)
180 largest
= SamplesListHead
->Flink
;
181 current
= SamplesListHead
->Flink
;
182 while (current
!= SamplesListHead
)
184 PSAMPLE_GROUP_INFO sgi
= CONTAINING_RECORD(
185 current
, SAMPLE_GROUP_INFO
, ListEntry
);
187 if (sgi
->Count
> count
)
193 current
= current
->Flink
;
203 KdbProfilerWriteString(PCH String
)
205 IO_STATUS_BLOCK Iosb
;
209 Length
= strlen(String
);
210 Status
= NtWriteFile(KdbProfilerLogFile
,
220 if (!NT_SUCCESS(Status
))
222 DPRINT1("NtWriteFile() failed with status 0x%.08x\n", Status
);
227 KdbProfilerWriteSampleGroups(PLIST_ENTRY SamplesListHead
)
230 PLIST_ENTRY current
= NULL
;
233 KdbProfilerWriteString("\r\n\r\n");
234 KdbProfilerWriteString("Count Symbol\n");
235 KdbProfilerWriteString("--------------------------------------------------\r\n");
237 current
= SamplesListHead
->Flink
;
238 while (current
!= SamplesListHead
)
240 Largest
= KdbProfilerLargestSampleGroup(SamplesListHead
);
243 PSAMPLE_GROUP_INFO sgi
= CONTAINING_RECORD(
244 Largest
, SAMPLE_GROUP_INFO
, ListEntry
);
246 //DbgPrint("%.08lu %s\n", sgi->Count, sgi->Description);
248 sprintf(Buffer
, "%.08lu %s\r\n", sgi
->Count
, sgi
->Description
);
249 KdbProfilerWriteString(Buffer
);
251 RemoveEntryList(Largest
);
259 current
= SamplesListHead
->Flink
;
262 return STATUS_SUCCESS
;
266 KdbProfilerKeyCompare(IN PVOID Key1
,
269 int value
= strcmp(Key1
, Key2
);
274 return (value
< 0) ? -1 : 1;
279 KdbProfilerAnalyzeSamples()
281 CHAR NameBuffer
[512];
283 PLIST_ENTRY current
= NULL
;
284 HASH_TABLE Hashtable
;
285 LIST_ENTRY SamplesListHead
;
289 if (!ExInitializeHashTable(&Hashtable
, 17, KdbProfilerKeyCompare
, TRUE
))
291 DPRINT1("ExInitializeHashTable() failed.");
295 InitializeListHead(&SamplesListHead
);
297 current
= RemoveHeadList(&KdbProfileDatabase
->ListHead
);
298 while (current
!= &KdbProfileDatabase
->ListHead
)
300 PPROFILE_DATABASE_BLOCK block
;
302 block
= CONTAINING_RECORD(current
, PROFILE_DATABASE_BLOCK
, ListEntry
);
304 for (Index
= 0; Index
< block
->UsedEntries
; Index
++)
306 PSAMPLE_GROUP_INFO sgi
;
307 Address
= block
->Entries
[Index
].Address
;
308 if (KdbProfilerGetSymbolInfo((PVOID
) Address
, (PCH
) &NameBuffer
))
313 sprintf(NameBuffer
, "(0x%.08lx)", (ULONG
) Address
);
316 KeyLength
= strlen(NameBuffer
);
317 if (!ExSearchHashTable(&Hashtable
, (PVOID
) NameBuffer
, KeyLength
, (PVOID
*) &sgi
))
319 sgi
= ExAllocatePool(NonPagedPool
, sizeof(SAMPLE_GROUP_INFO
));
321 sgi
->Address
= Address
;
323 strcpy(sgi
->Description
, NameBuffer
);
324 InsertTailList(&SamplesListHead
, &sgi
->ListEntry
);
325 ExInsertHashTable(&Hashtable
, sgi
->Description
, KeyLength
, (PVOID
) sgi
);
335 current
= RemoveHeadList(&KdbProfileDatabase
->ListHead
);
338 KdbProfilerWriteSampleGroups(&SamplesListHead
);
340 ExDeleteHashTable(&Hashtable
);
342 KdbDeleteProfileDatabase(KdbProfileDatabase
);
344 return STATUS_SUCCESS
;
348 KdbProfilerThreadMain(PVOID Context
)
352 KeWaitForSingleObject(&KdbProfilerTimer
, Executive
, KernelMode
, TRUE
, NULL
);
354 KeWaitForSingleObject(&KdbProfilerLock
, Executive
, KernelMode
, FALSE
, NULL
);
356 KdbSuspendProfiling();
358 KdbProfilerAnalyzeSamples();
360 KdbResumeProfiling();
362 KeReleaseMutex(&KdbProfilerLock
, FALSE
);
367 KdbDisableProfiling()
369 if (KdbProfilingEnabled
== TRUE
)
371 /* FIXME: Implement */
373 KdbProfilingEnabled
= FALSE
;
376 if (KdbProfileDatabase
!= NULL
)
378 KdbDeleteProfileDatabase(KdbProfileDatabase
);
379 ExFreePool(KdbProfileDatabase
);
380 KdbProfileDatabase
= NULL
;
387 * SystemArgument1 = EIP
390 KdbProfilerCollectorDpcRoutine(PKDPC Dpc
, PVOID DeferredContext
,
391 PVOID SystemArgument1
, PVOID SystemArgument2
)
393 ULONG_PTR address
= (ULONG_PTR
) SystemArgument1
;
395 KdbAddEntryToProfileDatabase(KdbProfileDatabase
, address
);
401 if (KdbProfilingEnabled
== FALSE
)
404 OBJECT_ATTRIBUTES ObjectAttributes
;
405 UNICODE_STRING FileName
;
406 IO_STATUS_BLOCK Iosb
;
407 LARGE_INTEGER DueTime
;
409 RtlInitUnicodeString(&FileName
, L
"\\SystemRoot\\profiler.log");
410 InitializeObjectAttributes(&ObjectAttributes
,
416 Status
= NtCreateFile(&KdbProfilerLogFile
,
421 FILE_ATTRIBUTE_NORMAL
,
424 FILE_WRITE_THROUGH
| FILE_SYNCHRONOUS_IO_NONALERT
,
427 if (!NT_SUCCESS(Status
))
429 DPRINT1("Failed to create profiler log file\n");
433 Status
= PsCreateSystemThread(&KdbProfilerThreadHandle
,
437 &KdbProfilerThreadCid
,
438 KdbProfilerThreadMain
,
440 if (!NT_SUCCESS(Status
))
442 DPRINT1("Failed to create profiler thread\n");
446 KeInitializeMutex(&KdbProfilerLock
, 0);
448 KdbProfileDatabase
= ExAllocatePool(NonPagedPool
, sizeof(PROFILE_DATABASE
));
449 ASSERT(KdbProfileDatabase
);
450 InitializeListHead(&KdbProfileDatabase
->ListHead
);
451 KeInitializeDpc(&KdbProfilerCollectorDpc
, KdbProfilerCollectorDpcRoutine
, NULL
);
453 /* Initialize our periodic timer and its associated DPC object. When the timer
454 expires, the KdbProfilerSessionEndDpc deferred procedure call (DPC) is queued */
455 KeInitializeTimerEx(&KdbProfilerTimer
, SynchronizationTimer
);
457 /* Start the periodic timer with an initial and periodic
458 relative expiration time of PROFILE_SESSION_LENGTH seconds */
459 DueTime
.QuadPart
= -(LONGLONG
) PROFILE_SESSION_LENGTH
* 1000 * 10000;
460 KeSetTimerEx(&KdbProfilerTimer
, DueTime
, PROFILE_SESSION_LENGTH
* 1000, NULL
);
462 KdbProfilingEnabled
= TRUE
;
467 KdbProfileInterrupt(ULONG_PTR Address
)
469 ASSERT(KeGetCurrentIrql() == PROFILE_LEVEL
);
471 if (KdbProfilingInitialized
!= TRUE
)
476 if ((KdbProfilingEnabled
) && (!KdbProfilingSuspended
))
478 (BOOLEAN
) KeInsertQueueDpc(&KdbProfilerCollectorDpc
, (PVOID
) Address
, NULL
);