3 * Copyright (C) 1998, 1999, 2000, 2001 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.
20 * PROJECT: ReactOS kernel
21 * FILE: ntoskrnl/nt/profile.c
22 * PURPOSE: Support for profiling
28 /* INCLUDES *****************************************************************/
30 #include <ddk/ntddk.h>
31 #include <internal/mm.h>
32 #include <internal/ps.h>
33 #include <internal/pool.h>
35 #include <internal/safe.h>
37 #include <internal/debug.h>
39 /* TYPES ********************************************************************/
41 typedef struct _KPROCESS_PROFILE
43 * List of the profile data structures associated with a process.
46 LIST_ENTRY ProfileListHead;
49 } KPROCESS_PROFILE, *PKPROCESS_PROFILE;
51 typedef struct _KPROFILE
53 * Describes a contiguous region of process memory that is being profiled.
59 /* Entry in the list of profile data structures for this process. */
62 /* Base of the region being profiled. */
65 /* Size of the region being profiled. */
68 /* Shift of offsets from the region to buckets in the profiling buffer. */
71 /* MDL which described the buffer that receives profiling data. */
74 /* System alias for the profiling buffer. */
77 /* Size of the buffer for profiling data. */
81 * Mask of processors for which profiling data should be collected.
86 /* TRUE if profiling has been started for this region. */
89 /* Pointer (and reference) to the process which is being profiled. */
91 } KPROFILE, *PKPROFILE;
93 /* GLOBALS *******************************************************************/
95 POBJECT_TYPE EXPORTED ExProfileObjectType = NULL;
97 static GENERIC_MAPPING ExpProfileMapping = {
99 STANDARD_RIGHTS_WRITE,
100 STANDARD_RIGHTS_EXECUTE,
101 STANDARD_RIGHTS_ALL};
104 * Size of the profile hash table.
106 #define PROFILE_HASH_TABLE_SIZE (32)
109 * Table of lists of per-process profiling data structures hashed by PID.
111 static LIST_ENTRY ProcessProfileListHashTable[PROFILE_HASH_TABLE_SIZE];
114 * Head of the list of profile data structures for the kernel.
116 static LIST_ENTRY SystemProfileList;
119 * Lock that protects the profiling data structures.
121 static KSPIN_LOCK ProfileListLock;
124 * Timer interrupts happen before we have initialized the profiling
125 * data structures so just ignore them before that.
127 static BOOLEAN ProfileInitDone = FALSE;
129 /* FUNCTIONS *****************************************************************/
132 KiAddProfileEventToProcess(PLIST_ENTRY ListHead, PVOID Eip)
134 * Add a profile event to the profile objects for a particular process
139 PLIST_ENTRY current_entry;
141 current_entry = ListHead->Flink;
142 while (current_entry != ListHead)
144 current = CONTAINING_RECORD(current_entry, KPROFILE, ListEntry);
146 if (current->Base > Eip)
151 if (current->Base <= Eip && (current->Base + current->Size) > Eip &&
156 Bucket = ((ULONG)(Eip - current->Base)) >> current->BucketShift;
158 if ((Bucket*4) < current->BufferSize)
160 current->Buffer[Bucket]++;
164 current_entry = current_entry->Flink;
169 KiAddProfileEvent(KPROFILE_SOURCE Source, ULONG Eip)
171 * Add a profile event
175 PKPROCESS_PROFILE current;
176 PLIST_ENTRY current_entry;
177 PLIST_ENTRY ListHead;
179 if (!ProfileInitDone)
184 Pid = PsGetCurrentProcessId();
186 ProcessProfileListHashTable[(ULONG)Pid % PROFILE_HASH_TABLE_SIZE].Flink;
188 KeAcquireSpinLockAtDpcLevel(&ProfileListLock);
190 current_entry = ListHead;
191 while (current_entry != ListHead)
193 current = CONTAINING_RECORD(current_entry, KPROCESS_PROFILE, ListEntry);
195 if (current->Pid == Pid)
197 KiAddProfileEventToProcess(¤t->ProfileListHead, (PVOID)Eip);
201 current_entry = current_entry->Flink;
204 KiAddProfileEventToProcess(&SystemProfileList, (PVOID)Eip);
206 KeReleaseSpinLockFromDpcLevel(&ProfileListLock);
210 KiInsertProfileIntoProcess(PLIST_ENTRY ListHead, PKPROFILE Profile)
212 * Insert a profile object into the list for a process or the system
216 PLIST_ENTRY current_entry;
218 current_entry = ListHead;
219 while (current_entry != ListHead)
221 current = CONTAINING_RECORD(current_entry, KPROFILE, ListEntry);
223 if (current->Base > Profile->Base)
225 Profile->ListEntry.Flink = current_entry;
226 Profile->ListEntry.Blink = current_entry->Blink;
227 current_entry->Blink->Flink = &Profile->ListEntry;
228 current_entry->Blink = &Profile->ListEntry;
232 current_entry = current_entry->Flink;
234 InsertTailList(ListHead, &Profile->ListEntry);
238 KiInsertProfile(PKPROFILE Profile)
240 * Insert a profile into the relevant data structures
245 KeAcquireSpinLock(&ProfileListLock, &oldIrql);
247 if (Profile->Process == NULL)
249 KiInsertProfileIntoProcess(&SystemProfileList, Profile);
254 PKPROCESS_PROFILE current;
255 PLIST_ENTRY current_entry;
256 PLIST_ENTRY ListHead;
258 Pid = Profile->Process->UniqueProcessId;
259 ListHead = &ProcessProfileListHashTable[Pid % PROFILE_HASH_TABLE_SIZE];
261 current_entry = ListHead;
262 while(current_entry != ListHead)
264 current = CONTAINING_RECORD(current_entry, KPROCESS_PROFILE,
267 if (current->Pid == (HANDLE)Pid)
269 KiInsertProfileIntoProcess(¤t->ProfileListHead, Profile);
270 KeReleaseSpinLock(&ProfileListLock, oldIrql);
274 current_entry = current_entry->Flink;
277 current = ExAllocatePool(NonPagedPool, sizeof(KPROCESS_PROFILE));
279 current->Pid = (HANDLE)Pid;
280 InitializeListHead(¤t->ProfileListHead);
281 InsertTailList(ListHead, ¤t->ListEntry);
283 KiInsertProfileIntoProcess(¤t->ProfileListHead, Profile);
286 KeReleaseSpinLock(&ProfileListLock, oldIrql);
289 VOID KiRemoveProfile(PKPROFILE Profile)
293 KeAcquireSpinLock(&ProfileListLock, &oldIrql);
295 if (Profile->Process == NULL)
297 RemoveEntryList(&Profile->ListEntry);
302 PLIST_ENTRY ListHead;
303 PKPROCESS_PROFILE current;
304 PLIST_ENTRY current_entry;
306 RemoveEntryList(&Profile->ListEntry);
308 Pid = Profile->Process->UniqueProcessId;
309 ListHead = &ProcessProfileListHashTable[Pid % PROFILE_HASH_TABLE_SIZE];
311 current_entry = ListHead;
312 while(current_entry != ListHead)
314 current = CONTAINING_RECORD(current_entry, KPROCESS_PROFILE,
317 if (current->Pid == (HANDLE)Pid)
319 if (IsListEmpty(¤t->ProfileListHead))
321 RemoveEntryList(¤t->ListEntry);
324 KeReleaseSpinLock(&ProfileListLock, oldIrql);
328 current_entry = current_entry->Flink;
333 KeReleaseSpinLock(&ProfileListLock, oldIrql);
337 KiDeleteProfile(PVOID ObjectBody)
341 Profile = (PKPROFILE)ObjectBody;
343 KiRemoveProfile(Profile);
344 if (Profile->Process != NULL)
346 ObDereferenceObject(Profile->Process);
347 Profile->Process = NULL;
350 if (Profile->BufferMdl->MappedSystemVa != NULL)
352 MmUnmapLockedPages(Profile->BufferMdl->MappedSystemVa,
355 MmUnlockPages(Profile->BufferMdl);
356 ExFreePool(Profile->BufferMdl);
357 Profile->BufferMdl = NULL;
361 NtInitializeProfileImplementation(VOID)
365 InitializeListHead(&SystemProfileList);
367 for (i = 0; i < PROFILE_HASH_TABLE_SIZE; i++)
369 InitializeListHead(&ProcessProfileListHashTable[i]);
372 KeInitializeSpinLock(&ProfileListLock);
373 ProfileInitDone = TRUE;
375 ExProfileObjectType = ExAllocatePool(NonPagedPool,sizeof(OBJECT_TYPE));
377 RtlCreateUnicodeString(&ExProfileObjectType->TypeName, L"Profile");
379 ExProfileObjectType->Tag = TAG('P', 'R', 'O', 'F');
380 ExProfileObjectType->MaxObjects = ULONG_MAX;
381 ExProfileObjectType->MaxHandles = ULONG_MAX;
382 ExProfileObjectType->TotalObjects = 0;
383 ExProfileObjectType->TotalHandles = 0;
384 ExProfileObjectType->PagedPoolCharge = 0;
385 ExProfileObjectType->NonpagedPoolCharge = sizeof(KPROFILE);
386 ExProfileObjectType->Mapping = &ExpProfileMapping;
387 ExProfileObjectType->Dump = NULL;
388 ExProfileObjectType->Open = NULL;
389 ExProfileObjectType->Close = NULL;
390 ExProfileObjectType->Delete = KiDeleteProfile;
391 ExProfileObjectType->Parse = NULL;
392 ExProfileObjectType->Security = NULL;
393 ExProfileObjectType->QueryName = NULL;
394 ExProfileObjectType->OkayToClose = NULL;
395 ExProfileObjectType->Create = NULL;
399 NtCreateProfile(OUT PHANDLE UnsafeProfileHandle,
400 IN HANDLE ProcessHandle,
403 IN ULONG Granularity,
406 IN KPROFILE_SOURCE Source,
407 IN ULONG ProcessorMask)
409 HANDLE ProfileHandle;
415 * Reference the associated process
417 if (ProcessHandle != NULL)
419 Status = ObReferenceObjectByHandle(ProcessHandle,
420 PROCESS_QUERY_INFORMATION,
425 if (!NT_SUCCESS(Status))
433 /* FIXME: Check privilege. */
437 * Check the parameters
439 if ((Process == NULL && ImageBase < (PVOID)KERNEL_BASE) ||
440 (Process != NULL && ImageBase >= (PVOID)KERNEL_BASE))
442 return(STATUS_INVALID_PARAMETER_3);
444 if (((ImageSize >> Granularity) * 4) >= BufferSize)
446 return(STATUS_BUFFER_TOO_SMALL);
448 if (Source != ProfileTime)
450 return(STATUS_INVALID_PARAMETER_9);
452 if (ProcessorMask != 0)
454 return(STATUS_INVALID_PARAMETER_10);
460 Status = ObCreateObject(&ProfileHandle,
465 if (!NT_SUCCESS(Status))
473 Profile->Base = ImageBase;
474 Profile->Size = ImageSize;
475 Profile->BucketShift = Granularity;
476 Profile->BufferMdl = MmCreateMdl(NULL, Buffer, BufferSize);
477 MmProbeAndLockPages(Profile->BufferMdl, UserMode, IoWriteAccess);
478 Profile->Buffer = MmGetSystemAddressForMdl(Profile->BufferMdl);
479 Profile->BufferSize = BufferSize;
480 Profile->ProcessorMask = ProcessorMask;
481 Profile->Started = FALSE;
482 Profile->Process = Process;
485 * Insert the profile into the profile list data structures
487 KiInsertProfile(Profile);
490 * Copy the created handle back to the caller
492 Status = MmCopyToCaller(UnsafeProfileHandle, &ProfileHandle, sizeof(HANDLE));
493 if (!NT_SUCCESS(Status))
495 ObDereferenceObject(Profile);
496 ZwClose(ProfileHandle);
500 ObDereferenceObject(Profile);
502 return(STATUS_SUCCESS);
506 NtQueryIntervalProfile(OUT PULONG UnsafeInterval,
507 OUT KPROFILE_SOURCE Source)
511 if (Source == ProfileTime)
515 /* FIXME: What units does this use, for now nanoseconds */
517 Status = MmCopyToCaller(UnsafeInterval, &Interval, sizeof(ULONG));
518 if (!NT_SUCCESS(Status))
522 return(STATUS_SUCCESS);
524 return(STATUS_INVALID_PARAMETER_2);
528 NtSetIntervalProfile(IN ULONG Interval,
529 IN KPROFILE_SOURCE Source)
531 return(STATUS_NOT_IMPLEMENTED);
535 NtStartProfile(IN HANDLE ProfileHandle)
540 Status = ObReferenceObjectByHandle(ProfileHandle,
546 if (!NT_SUCCESS(Status))
550 Profile->Started = TRUE;
551 ObDereferenceObject(Profile);
552 return(STATUS_SUCCESS);
556 NtStopProfile(IN HANDLE ProfileHandle)
561 Status = ObReferenceObjectByHandle(ProfileHandle,
567 if (!NT_SUCCESS(Status))
571 Profile->Started = FALSE;
572 ObDereferenceObject(Profile);
573 return(STATUS_SUCCESS);