3 * Copyright (C) 1998-2003 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 * PROJECT: ReactOS kernel
22 * FILE: ntoskrnl/dbg/profile.c
23 * PURPOSE: Kernel profiling
24 * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
29 /* INCLUDES *****************************************************************/
35 #include <internal/debug.h>
37 /* FUNCTIONS *****************************************************************/
39 #define PROFILE_SESSION_LENGTH 30 /* Session length in seconds */
41 typedef struct _PROFILE_DATABASE_ENTRY
44 } PROFILE_DATABASE_ENTRY
, *PPROFILE_DATABASE_ENTRY
;
46 #define PDE_BLOCK_ENTRIES ((PAGE_SIZE - (sizeof(LIST_ENTRY) + sizeof(ULONG))) / sizeof(PROFILE_DATABASE_ENTRY))
48 typedef struct _PROFILE_DATABASE_BLOCK
52 PROFILE_DATABASE_ENTRY Entries
[PDE_BLOCK_ENTRIES
];
53 } PROFILE_DATABASE_BLOCK
, *PPROFILE_DATABASE_BLOCK
;
55 typedef struct _PROFILE_DATABASE
58 } PROFILE_DATABASE
, *PPROFILE_DATABASE
;
60 typedef struct _SAMPLE_GROUP_INFO
64 CHAR Description
[128];
66 } SAMPLE_GROUP_INFO
, *PSAMPLE_GROUP_INFO
;
68 static volatile BOOLEAN KdbProfilingInitialized
= FALSE
;
69 static volatile BOOLEAN KdbProfilingEnabled
= FALSE
;
70 static volatile BOOLEAN KdbProfilingSuspended
= FALSE
;
71 static PPROFILE_DATABASE KdbProfileDatabase
= NULL
;
72 static KDPC KdbProfilerCollectorDpc
;
73 static HANDLE KdbProfilerThreadHandle
;
74 static CLIENT_ID KdbProfilerThreadCid
;
75 static HANDLE KdbProfilerLogFile
;
76 static KTIMER KdbProfilerTimer
;
77 static KMUTEX KdbProfilerLock
;
78 static BOOLEAN KdbEnableProfiler
= FALSE
;
81 KdbDeleteProfileDatabase(PPROFILE_DATABASE ProfileDatabase
)
83 PLIST_ENTRY current
= NULL
;
85 current
= RemoveHeadList(&ProfileDatabase
->ListHead
);
86 while (current
!= &ProfileDatabase
->ListHead
)
88 PPROFILE_DATABASE_BLOCK block
= CONTAINING_RECORD(
89 current
, PROFILE_DATABASE_BLOCK
, ListEntry
);
91 current
= RemoveHeadList(&ProfileDatabase
->ListHead
);
96 KdbAddEntryToProfileDatabase(PPROFILE_DATABASE ProfileDatabase
, ULONG_PTR Address
)
98 PPROFILE_DATABASE_BLOCK block
;
100 if (IsListEmpty(&ProfileDatabase
->ListHead
))
102 block
= ExAllocatePool(NonPagedPool
, sizeof(PROFILE_DATABASE_BLOCK
));
104 block
->UsedEntries
= 0;
105 InsertTailList(&ProfileDatabase
->ListHead
, &block
->ListEntry
);
106 block
->Entries
[block
->UsedEntries
++].Address
= Address
;
110 block
= CONTAINING_RECORD(ProfileDatabase
->ListHead
.Blink
, PROFILE_DATABASE_BLOCK
, ListEntry
);
111 if (block
->UsedEntries
>= PDE_BLOCK_ENTRIES
)
113 block
= ExAllocatePool(NonPagedPool
, sizeof(PROFILE_DATABASE_BLOCK
));
115 block
->UsedEntries
= 0;
116 InsertTailList(&ProfileDatabase
->ListHead
, &block
->ListEntry
);
118 block
->Entries
[block
->UsedEntries
++].Address
= Address
;
124 KdbEnableProfiler
= TRUE
;
130 if (KdbEnableProfiler
)
132 KdbEnableProfiling();
133 KdbProfilingInitialized
= TRUE
;
138 KdbSuspendProfiling()
140 KdbProfilingSuspended
= TRUE
;
146 KdbProfilingSuspended
= FALSE
;
150 KdbProfilerGetSymbolInfo(PVOID address
, OUT PCH NameBuffer
)
152 PLIST_ENTRY current_entry
;
153 MODULE_TEXT_SECTION
* current
;
154 extern LIST_ENTRY ModuleTextListHead
;
155 ULONG_PTR RelativeAddress
;
159 CHAR FunctionName
[256];
161 current_entry
= ModuleTextListHead
.Flink
;
163 while (current_entry
!= &ModuleTextListHead
&&
164 current_entry
!= NULL
)
167 CONTAINING_RECORD(current_entry
, MODULE_TEXT_SECTION
, ListEntry
);
169 if (address
>= (PVOID
)current
->Base
&&
170 address
< (PVOID
)(current
->Base
+ current
->Length
))
172 RelativeAddress
= (ULONG_PTR
) address
- current
->Base
;
173 Status
= KdbSymGetAddressInformation(¤t
->SymbolInfo
,
178 if (NT_SUCCESS(Status
))
180 sprintf(NameBuffer
, "%s (%s)", FileName
, FunctionName
);
185 current_entry
= current_entry
->Flink
;
191 KdbProfilerLargestSampleGroup(PLIST_ENTRY SamplesListHead
)
198 largest
= SamplesListHead
->Flink
;
199 current
= SamplesListHead
->Flink
;
200 while (current
!= SamplesListHead
)
202 PSAMPLE_GROUP_INFO sgi
= CONTAINING_RECORD(
203 current
, SAMPLE_GROUP_INFO
, ListEntry
);
205 if (sgi
->Count
> count
)
211 current
= current
->Flink
;
221 KdbProfilerWriteString(PCH String
)
223 IO_STATUS_BLOCK Iosb
;
227 Length
= strlen(String
);
228 Status
= NtWriteFile(KdbProfilerLogFile
,
238 if (!NT_SUCCESS(Status
))
240 DPRINT1("NtWriteFile() failed with status 0x%.08x\n", Status
);
245 KdbProfilerWriteSampleGroups(PLIST_ENTRY SamplesListHead
)
248 PLIST_ENTRY current
= NULL
;
251 KdbProfilerWriteString("\r\n\r\n");
252 KdbProfilerWriteString("Count Symbol\n");
253 KdbProfilerWriteString("--------------------------------------------------\r\n");
255 current
= SamplesListHead
->Flink
;
256 while (current
!= SamplesListHead
)
258 Largest
= KdbProfilerLargestSampleGroup(SamplesListHead
);
261 PSAMPLE_GROUP_INFO sgi
= CONTAINING_RECORD(
262 Largest
, SAMPLE_GROUP_INFO
, ListEntry
);
264 //DbgPrint("%.08lu %s\n", sgi->Count, sgi->Description);
266 sprintf(Buffer
, "%.08lu %s\r\n", sgi
->Count
, sgi
->Description
);
267 KdbProfilerWriteString(Buffer
);
269 RemoveEntryList(Largest
);
277 current
= SamplesListHead
->Flink
;
280 return STATUS_SUCCESS
;
284 KdbProfilerKeyCompare(IN PVOID Key1
,
287 int value
= strcmp(Key1
, Key2
);
292 return (value
< 0) ? -1 : 1;
297 KdbProfilerAnalyzeSamples()
299 CHAR NameBuffer
[512];
301 PLIST_ENTRY current
= NULL
;
302 HASH_TABLE Hashtable
;
303 LIST_ENTRY SamplesListHead
;
307 if (!ExInitializeHashTable(&Hashtable
, 17, KdbProfilerKeyCompare
, TRUE
))
309 DPRINT1("ExInitializeHashTable() failed.");
313 InitializeListHead(&SamplesListHead
);
315 current
= RemoveHeadList(&KdbProfileDatabase
->ListHead
);
316 while (current
!= &KdbProfileDatabase
->ListHead
)
318 PPROFILE_DATABASE_BLOCK block
;
320 block
= CONTAINING_RECORD(current
, PROFILE_DATABASE_BLOCK
, ListEntry
);
322 for (Index
= 0; Index
< block
->UsedEntries
; Index
++)
324 PSAMPLE_GROUP_INFO sgi
;
325 Address
= block
->Entries
[Index
].Address
;
326 if (KdbProfilerGetSymbolInfo((PVOID
) Address
, (PCH
) &NameBuffer
))
331 sprintf(NameBuffer
, "(0x%.08lx)", (ULONG
) Address
);
334 KeyLength
= strlen(NameBuffer
);
335 if (!ExSearchHashTable(&Hashtable
, (PVOID
) NameBuffer
, KeyLength
, (PVOID
*) &sgi
))
337 sgi
= ExAllocatePool(NonPagedPool
, sizeof(SAMPLE_GROUP_INFO
));
339 sgi
->Address
= Address
;
341 strcpy(sgi
->Description
, NameBuffer
);
342 InsertTailList(&SamplesListHead
, &sgi
->ListEntry
);
343 ExInsertHashTable(&Hashtable
, sgi
->Description
, KeyLength
, (PVOID
) sgi
);
353 current
= RemoveHeadList(&KdbProfileDatabase
->ListHead
);
356 KdbProfilerWriteSampleGroups(&SamplesListHead
);
358 ExDeleteHashTable(&Hashtable
);
360 KdbDeleteProfileDatabase(KdbProfileDatabase
);
362 return STATUS_SUCCESS
;
366 KdbProfilerThreadMain(PVOID Context
)
370 KeWaitForSingleObject(&KdbProfilerTimer
, Executive
, KernelMode
, TRUE
, NULL
);
372 KeWaitForSingleObject(&KdbProfilerLock
, Executive
, KernelMode
, FALSE
, NULL
);
374 KdbSuspendProfiling();
376 KdbProfilerAnalyzeSamples();
378 KdbResumeProfiling();
380 KeReleaseMutex(&KdbProfilerLock
, FALSE
);
385 KdbDisableProfiling()
387 if (KdbProfilingEnabled
== TRUE
)
389 /* FIXME: Implement */
391 KdbProfilingEnabled
= FALSE
;
394 if (KdbProfileDatabase
!= NULL
)
396 KdbDeleteProfileDatabase(KdbProfileDatabase
);
397 ExFreePool(KdbProfileDatabase
);
398 KdbProfileDatabase
= NULL
;
405 * SystemArgument1 = EIP
408 KdbProfilerCollectorDpcRoutine(PKDPC Dpc
, PVOID DeferredContext
,
409 PVOID SystemArgument1
, PVOID SystemArgument2
)
411 ULONG_PTR address
= (ULONG_PTR
) SystemArgument1
;
413 KdbAddEntryToProfileDatabase(KdbProfileDatabase
, address
);
419 if (KdbProfilingEnabled
== FALSE
)
422 OBJECT_ATTRIBUTES ObjectAttributes
;
423 UNICODE_STRING FileName
;
424 IO_STATUS_BLOCK Iosb
;
425 LARGE_INTEGER DueTime
;
427 RtlInitUnicodeString(&FileName
, L
"\\SystemRoot\\profiler.log");
428 InitializeObjectAttributes(&ObjectAttributes
,
434 Status
= NtCreateFile(&KdbProfilerLogFile
,
439 FILE_ATTRIBUTE_NORMAL
,
442 FILE_WRITE_THROUGH
| FILE_SYNCHRONOUS_IO_NONALERT
,
445 if (!NT_SUCCESS(Status
))
447 DPRINT1("Failed to create profiler log file\n");
451 Status
= PsCreateSystemThread(&KdbProfilerThreadHandle
,
455 &KdbProfilerThreadCid
,
456 KdbProfilerThreadMain
,
458 if (!NT_SUCCESS(Status
))
460 DPRINT1("Failed to create profiler thread\n");
464 KeInitializeMutex(&KdbProfilerLock
, 0);
466 KdbProfileDatabase
= ExAllocatePool(NonPagedPool
, sizeof(PROFILE_DATABASE
));
467 ASSERT(KdbProfileDatabase
);
468 InitializeListHead(&KdbProfileDatabase
->ListHead
);
469 KeInitializeDpc(&KdbProfilerCollectorDpc
, KdbProfilerCollectorDpcRoutine
, NULL
);
471 /* Initialize our periodic timer and its associated DPC object. When the timer
472 expires, the KdbProfilerSessionEndDpc deferred procedure call (DPC) is queued */
473 KeInitializeTimerEx(&KdbProfilerTimer
, SynchronizationTimer
);
475 /* Start the periodic timer with an initial and periodic
476 relative expiration time of PROFILE_SESSION_LENGTH seconds */
477 DueTime
.QuadPart
= -(LONGLONG
) PROFILE_SESSION_LENGTH
* 1000 * 10000;
478 KeSetTimerEx(&KdbProfilerTimer
, DueTime
, PROFILE_SESSION_LENGTH
* 1000, NULL
);
480 KdbProfilingEnabled
= TRUE
;
485 KdbProfileInterrupt(ULONG_PTR Address
)
487 ASSERT(KeGetCurrentIrql() == PROFILE_LEVEL
);
489 if (KdbProfilingInitialized
!= TRUE
)
494 if ((KdbProfilingEnabled
) && (!KdbProfilingSuspended
))
496 (BOOLEAN
) KeInsertQueueDpc(&KdbProfilerCollectorDpc
, (PVOID
) Address
, NULL
);