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 *****************************************************************/
31 #define NTOS_MODE_KERNEL
33 #include <internal/ldr.h>
37 #include <internal/debug.h>
39 /* FUNCTIONS *****************************************************************/
41 #define PROFILE_SESSION_LENGTH 30 /* Session length in seconds */
43 typedef struct _PROFILE_DATABASE_ENTRY
46 } PROFILE_DATABASE_ENTRY, *PPROFILE_DATABASE_ENTRY;
48 #define PDE_BLOCK_ENTRIES ((PAGE_SIZE - (sizeof(LIST_ENTRY) + sizeof(ULONG))) / sizeof(PROFILE_DATABASE_ENTRY))
50 typedef struct _PROFILE_DATABASE_BLOCK
54 PROFILE_DATABASE_ENTRY Entries[PDE_BLOCK_ENTRIES];
55 } PROFILE_DATABASE_BLOCK, *PPROFILE_DATABASE_BLOCK;
57 typedef struct _PROFILE_DATABASE
60 } PROFILE_DATABASE, *PPROFILE_DATABASE;
62 typedef struct _SAMPLE_GROUP_INFO
66 CHAR Description[128];
68 } SAMPLE_GROUP_INFO, *PSAMPLE_GROUP_INFO;
70 static volatile BOOLEAN KdbProfilingInitialized = FALSE;
71 static volatile BOOLEAN KdbProfilingEnabled = FALSE;
72 static volatile BOOLEAN KdbProfilingSuspended = FALSE;
73 static PPROFILE_DATABASE KdbProfileDatabase = NULL;
74 static KDPC KdbProfilerCollectorDpc;
75 static HANDLE KdbProfilerThreadHandle;
76 static CLIENT_ID KdbProfilerThreadCid;
77 static HANDLE KdbProfilerLogFile;
78 static KTIMER KdbProfilerTimer;
79 static KMUTEX KdbProfilerLock;
80 static BOOLEAN KdbEnableProfiler = FALSE;
83 KdbDeleteProfileDatabase(PPROFILE_DATABASE ProfileDatabase)
85 PLIST_ENTRY current = NULL;
87 current = RemoveHeadList(&ProfileDatabase->ListHead);
88 while (current != &ProfileDatabase->ListHead)
90 PPROFILE_DATABASE_BLOCK block = CONTAINING_RECORD(
91 current, PROFILE_DATABASE_BLOCK, ListEntry);
93 current = RemoveHeadList(&ProfileDatabase->ListHead);
98 KdbAddEntryToProfileDatabase(PPROFILE_DATABASE ProfileDatabase, ULONG_PTR Address)
100 PPROFILE_DATABASE_BLOCK block;
102 if (IsListEmpty(&ProfileDatabase->ListHead))
104 block = ExAllocatePool(NonPagedPool, sizeof(PROFILE_DATABASE_BLOCK));
106 block->UsedEntries = 0;
107 InsertTailList(&ProfileDatabase->ListHead, &block->ListEntry);
108 block->Entries[block->UsedEntries++].Address = Address;
112 block = CONTAINING_RECORD(ProfileDatabase->ListHead.Blink, PROFILE_DATABASE_BLOCK, ListEntry);
113 if (block->UsedEntries >= PDE_BLOCK_ENTRIES)
115 block = ExAllocatePool(NonPagedPool, sizeof(PROFILE_DATABASE_BLOCK));
117 block->UsedEntries = 0;
118 InsertTailList(&ProfileDatabase->ListHead, &block->ListEntry);
120 block->Entries[block->UsedEntries++].Address = Address;
126 KdbEnableProfiler = TRUE;
132 if (KdbEnableProfiler)
134 KdbEnableProfiling();
135 KdbProfilingInitialized = TRUE;
140 KdbSuspendProfiling()
142 KdbProfilingSuspended = TRUE;
148 KdbProfilingSuspended = FALSE;
152 KdbProfilerGetSymbolInfo(PVOID address, OUT PCH NameBuffer)
154 PLIST_ENTRY current_entry;
155 MODULE_TEXT_SECTION* current;
156 extern LIST_ENTRY ModuleTextListHead;
157 ULONG_PTR RelativeAddress;
161 CHAR FunctionName[256];
163 current_entry = ModuleTextListHead.Flink;
165 while (current_entry != &ModuleTextListHead &&
166 current_entry != NULL)
169 CONTAINING_RECORD(current_entry, MODULE_TEXT_SECTION, ListEntry);
171 if (address >= (PVOID)current->Base &&
172 address < (PVOID)(current->Base + current->Length))
174 RelativeAddress = (ULONG_PTR) address - current->Base;
175 Status = LdrGetAddressInformation(¤t->SymbolInfo,
180 if (NT_SUCCESS(Status))
182 sprintf(NameBuffer, "%s (%s)", FileName, FunctionName);
187 current_entry = current_entry->Flink;
193 KdbProfilerLargestSampleGroup(PLIST_ENTRY SamplesListHead)
200 largest = SamplesListHead->Flink;
201 current = SamplesListHead->Flink;
202 while (current != SamplesListHead)
204 PSAMPLE_GROUP_INFO sgi = CONTAINING_RECORD(
205 current, SAMPLE_GROUP_INFO, ListEntry);
207 if (sgi->Count > count)
213 current = current->Flink;
223 KdbProfilerWriteString(PCH String)
225 IO_STATUS_BLOCK Iosb;
229 Length = strlen(String);
230 Status = NtWriteFile(KdbProfilerLogFile,
240 if (!NT_SUCCESS(Status))
242 DPRINT1("NtWriteFile() failed with status 0x%.08x\n", Status);
247 KdbProfilerWriteSampleGroups(PLIST_ENTRY SamplesListHead)
250 PLIST_ENTRY current = NULL;
253 KdbProfilerWriteString("\r\n\r\n");
254 KdbProfilerWriteString("Count Symbol\n");
255 KdbProfilerWriteString("--------------------------------------------------\r\n");
257 current = SamplesListHead->Flink;
258 while (current != SamplesListHead)
260 Largest = KdbProfilerLargestSampleGroup(SamplesListHead);
263 PSAMPLE_GROUP_INFO sgi = CONTAINING_RECORD(
264 Largest, SAMPLE_GROUP_INFO, ListEntry);
266 //DbgPrint("%.08lu %s\n", sgi->Count, sgi->Description);
268 sprintf(Buffer, "%.08lu %s\r\n", sgi->Count, sgi->Description);
269 KdbProfilerWriteString(Buffer);
271 RemoveEntryList(Largest);
279 current = SamplesListHead->Flink;
282 return STATUS_SUCCESS;
286 KdbProfilerKeyCompare(IN PVOID Key1,
289 int value = strcmp(Key1, Key2);
294 return (value < 0) ? -1 : 1;
299 KdbProfilerAnalyzeSamples()
301 CHAR NameBuffer[512];
303 PLIST_ENTRY current = NULL;
304 HASH_TABLE Hashtable;
305 LIST_ENTRY SamplesListHead;
309 if (!ExInitializeHashTable(&Hashtable, 17, KdbProfilerKeyCompare, TRUE))
311 DPRINT1("ExInitializeHashTable() failed.");
315 InitializeListHead(&SamplesListHead);
317 current = RemoveHeadList(&KdbProfileDatabase->ListHead);
318 while (current != &KdbProfileDatabase->ListHead)
320 PPROFILE_DATABASE_BLOCK block;
322 block = CONTAINING_RECORD(current, PROFILE_DATABASE_BLOCK, ListEntry);
324 for (Index = 0; Index < block->UsedEntries; Index++)
326 PSAMPLE_GROUP_INFO sgi;
327 Address = block->Entries[Index].Address;
328 if (KdbProfilerGetSymbolInfo((PVOID) Address, (PCH) &NameBuffer))
333 sprintf(NameBuffer, "(0x%.08lx)", (ULONG) Address);
336 KeyLength = strlen(NameBuffer);
337 if (!ExSearchHashTable(&Hashtable, (PVOID) NameBuffer, KeyLength, (PVOID *) &sgi))
339 sgi = ExAllocatePool(NonPagedPool, sizeof(SAMPLE_GROUP_INFO));
341 sgi->Address = Address;
343 strcpy(sgi->Description, NameBuffer);
344 InsertTailList(&SamplesListHead, &sgi->ListEntry);
345 ExInsertHashTable(&Hashtable, sgi->Description, KeyLength, (PVOID) sgi);
355 current = RemoveHeadList(&KdbProfileDatabase->ListHead);
358 KdbProfilerWriteSampleGroups(&SamplesListHead);
360 ExDeleteHashTable(&Hashtable);
362 KdbDeleteProfileDatabase(KdbProfileDatabase);
364 return STATUS_SUCCESS;
368 KdbProfilerThreadMain(PVOID Context)
372 KeWaitForSingleObject(&KdbProfilerTimer, Executive, KernelMode, TRUE, NULL);
374 KeWaitForSingleObject(&KdbProfilerLock, Executive, KernelMode, FALSE, NULL);
376 KdbSuspendProfiling();
378 KdbProfilerAnalyzeSamples();
380 KdbResumeProfiling();
382 KeReleaseMutex(&KdbProfilerLock, FALSE);
387 KdbDisableProfiling()
389 if (KdbProfilingEnabled == TRUE)
391 /* FIXME: Implement */
393 KdbProfilingEnabled = FALSE;
396 if (KdbProfileDatabase != NULL)
398 KdbDeleteProfileDatabase(KdbProfileDatabase);
399 ExFreePool(KdbProfileDatabase);
400 KdbProfileDatabase = NULL;
407 * SystemArgument1 = EIP
410 KdbProfilerCollectorDpcRoutine(PKDPC Dpc, PVOID DeferredContext,
411 PVOID SystemArgument1, PVOID SystemArgument2)
413 ULONG_PTR address = (ULONG_PTR) SystemArgument1;
415 KdbAddEntryToProfileDatabase(KdbProfileDatabase, address);
421 if (KdbProfilingEnabled == FALSE)
424 OBJECT_ATTRIBUTES ObjectAttributes;
425 UNICODE_STRING FileName;
426 IO_STATUS_BLOCK Iosb;
427 LARGE_INTEGER DueTime;
429 RtlInitUnicodeString(&FileName, L"\\SystemRoot\\profiler.log");
430 InitializeObjectAttributes(&ObjectAttributes,
436 Status = NtCreateFile(&KdbProfilerLogFile,
441 FILE_ATTRIBUTE_NORMAL,
444 FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_NONALERT,
447 if (!NT_SUCCESS(Status))
449 DPRINT1("Failed to create profiler log file\n");
453 Status = PsCreateSystemThread(&KdbProfilerThreadHandle,
457 &KdbProfilerThreadCid,
458 KdbProfilerThreadMain,
460 if (!NT_SUCCESS(Status))
462 DPRINT1("Failed to create profiler thread\n");
466 KeInitializeMutex(&KdbProfilerLock, 0);
468 KdbProfileDatabase = ExAllocatePool(NonPagedPool, sizeof(PROFILE_DATABASE));
469 assert(KdbProfileDatabase);
470 InitializeListHead(&KdbProfileDatabase->ListHead);
471 KeInitializeDpc(&KdbProfilerCollectorDpc, KdbProfilerCollectorDpcRoutine, NULL);
473 /* Initialize our periodic timer and its associated DPC object. When the timer
474 expires, the KdbProfilerSessionEndDpc deferred procedure call (DPC) is queued */
475 KeInitializeTimerEx(&KdbProfilerTimer, SynchronizationTimer);
477 /* Start the periodic timer with an initial and periodic
478 relative expiration time of PROFILE_SESSION_LENGTH seconds */
479 DueTime.QuadPart = -(LONGLONG) PROFILE_SESSION_LENGTH * 1000 * 10000;
480 KeSetTimerEx(&KdbProfilerTimer, DueTime, PROFILE_SESSION_LENGTH * 1000, NULL);
482 KdbProfilingEnabled = TRUE;
487 KdbProfileInterrupt(ULONG_PTR Address)
489 assert(KeGetCurrentIrql() == PROFILE_LEVEL);
491 if (KdbProfilingInitialized != TRUE)
496 if ((KdbProfilingEnabled) && (!KdbProfilingSuspended))
498 (BOOLEAN) KeInsertQueueDpc(&KdbProfilerCollectorDpc, (PVOID) Address, NULL);