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 *****************************************************************/
31 #include <internal/debug.h>
33 /* TYPES ********************************************************************/
35 typedef struct _KPROCESS_PROFILE
37 * List of the profile data structures associated with a process.
40 LIST_ENTRY ProfileListHead
;
43 } KPROCESS_PROFILE
, *PKPROCESS_PROFILE
;
45 typedef struct _KPROFILE
47 * Describes a contiguous region of process memory that is being profiled.
53 /* Entry in the list of profile data structures for this process. */
56 /* Base of the region being profiled. */
59 /* Size of the region being profiled. */
62 /* Shift of offsets from the region to buckets in the profiling buffer. */
65 /* MDL which described the buffer that receives profiling data. */
68 /* System alias for the profiling buffer. */
71 /* Size of the buffer for profiling data. */
75 * Mask of processors for which profiling data should be collected.
80 /* TRUE if profiling has been started for this region. */
83 /* Pointer (and reference) to the process which is being profiled. */
85 } KPROFILE
, *PKPROFILE
;
87 /* GLOBALS *******************************************************************/
89 POBJECT_TYPE EXPORTED ExProfileObjectType
= NULL
;
91 static GENERIC_MAPPING ExpProfileMapping
= {
93 STANDARD_RIGHTS_WRITE
,
94 STANDARD_RIGHTS_EXECUTE
,
98 * Size of the profile hash table.
100 #define PROFILE_HASH_TABLE_SIZE (32)
103 * Table of lists of per-process profiling data structures hashed by PID.
105 static LIST_ENTRY ProcessProfileListHashTable
[PROFILE_HASH_TABLE_SIZE
];
108 * Head of the list of profile data structures for the kernel.
110 static LIST_ENTRY SystemProfileList
;
113 * Lock that protects the profiling data structures.
115 static KSPIN_LOCK ProfileListLock
;
118 * Timer interrupts happen before we have initialized the profiling
119 * data structures so just ignore them before that.
121 static BOOLEAN ProfileInitDone
= FALSE
;
123 /* FUNCTIONS *****************************************************************/
126 KiAddProfileEventToProcess(PLIST_ENTRY ListHead
, PVOID Eip
)
128 * Add a profile event to the profile objects for a particular process
133 PLIST_ENTRY current_entry
;
135 current_entry
= ListHead
->Flink
;
136 while (current_entry
!= ListHead
)
138 current
= CONTAINING_RECORD(current_entry
, KPROFILE
, ListEntry
);
140 if (current
->Base
> Eip
)
145 if (current
->Base
<= Eip
&& ((char*)current
->Base
+ current
->Size
) > (char*)Eip
&&
150 Bucket
= ((ULONG
)((char*)Eip
- (char*)current
->Base
)) >> current
->BucketShift
;
152 if ((Bucket
*4) < current
->BufferSize
)
154 current
->Buffer
[Bucket
]++;
158 current_entry
= current_entry
->Flink
;
163 KiAddProfileEvent(KPROFILE_SOURCE Source
, ULONG Eip
)
165 * Add a profile event
169 PKPROCESS_PROFILE current
;
170 PLIST_ENTRY current_entry
;
171 PLIST_ENTRY ListHead
;
173 if (!ProfileInitDone
)
178 Pid
= PsGetCurrentProcessId();
180 ProcessProfileListHashTable
[(ULONG
)Pid
% PROFILE_HASH_TABLE_SIZE
].Flink
;
182 KeAcquireSpinLockAtDpcLevel(&ProfileListLock
);
184 current_entry
= ListHead
;
185 while (current_entry
!= ListHead
)
187 current
= CONTAINING_RECORD(current_entry
, KPROCESS_PROFILE
, ListEntry
);
189 if (current
->Pid
== Pid
)
191 KiAddProfileEventToProcess(¤t
->ProfileListHead
, (PVOID
)Eip
);
195 current_entry
= current_entry
->Flink
;
198 KiAddProfileEventToProcess(&SystemProfileList
, (PVOID
)Eip
);
200 KeReleaseSpinLockFromDpcLevel(&ProfileListLock
);
204 KiInsertProfileIntoProcess(PLIST_ENTRY ListHead
, PKPROFILE Profile
)
206 * Insert a profile object into the list for a process or the system
210 PLIST_ENTRY current_entry
;
212 current_entry
= ListHead
;
213 while (current_entry
!= ListHead
)
215 current
= CONTAINING_RECORD(current_entry
, KPROFILE
, ListEntry
);
217 if (current
->Base
> Profile
->Base
)
219 Profile
->ListEntry
.Flink
= current_entry
;
220 Profile
->ListEntry
.Blink
= current_entry
->Blink
;
221 current_entry
->Blink
->Flink
= &Profile
->ListEntry
;
222 current_entry
->Blink
= &Profile
->ListEntry
;
226 current_entry
= current_entry
->Flink
;
228 InsertTailList(ListHead
, &Profile
->ListEntry
);
232 KiInsertProfile(PKPROFILE Profile
)
234 * Insert a profile into the relevant data structures
239 KeAcquireSpinLock(&ProfileListLock
, &oldIrql
);
241 if (Profile
->Process
== NULL
)
243 KiInsertProfileIntoProcess(&SystemProfileList
, Profile
);
248 PKPROCESS_PROFILE current
;
249 PLIST_ENTRY current_entry
;
250 PLIST_ENTRY ListHead
;
252 Pid
= Profile
->Process
->UniqueProcessId
;
253 ListHead
= &ProcessProfileListHashTable
[Pid
% PROFILE_HASH_TABLE_SIZE
];
255 current_entry
= ListHead
;
256 while(current_entry
!= ListHead
)
258 current
= CONTAINING_RECORD(current_entry
, KPROCESS_PROFILE
,
261 if (current
->Pid
== (HANDLE
)Pid
)
263 KiInsertProfileIntoProcess(¤t
->ProfileListHead
, Profile
);
264 KeReleaseSpinLock(&ProfileListLock
, oldIrql
);
268 current_entry
= current_entry
->Flink
;
271 current
= ExAllocatePool(NonPagedPool
, sizeof(KPROCESS_PROFILE
));
273 current
->Pid
= (HANDLE
)Pid
;
274 InitializeListHead(¤t
->ProfileListHead
);
275 InsertTailList(ListHead
, ¤t
->ListEntry
);
277 KiInsertProfileIntoProcess(¤t
->ProfileListHead
, Profile
);
280 KeReleaseSpinLock(&ProfileListLock
, oldIrql
);
283 VOID
KiRemoveProfile(PKPROFILE Profile
)
287 KeAcquireSpinLock(&ProfileListLock
, &oldIrql
);
289 if (Profile
->Process
== NULL
)
291 RemoveEntryList(&Profile
->ListEntry
);
296 PLIST_ENTRY ListHead
;
297 PKPROCESS_PROFILE current
;
298 PLIST_ENTRY current_entry
;
300 RemoveEntryList(&Profile
->ListEntry
);
302 Pid
= Profile
->Process
->UniqueProcessId
;
303 ListHead
= &ProcessProfileListHashTable
[Pid
% PROFILE_HASH_TABLE_SIZE
];
305 current_entry
= ListHead
;
306 while(current_entry
!= ListHead
)
308 current
= CONTAINING_RECORD(current_entry
, KPROCESS_PROFILE
,
311 if (current
->Pid
== (HANDLE
)Pid
)
313 if (IsListEmpty(¤t
->ProfileListHead
))
315 RemoveEntryList(¤t
->ListEntry
);
318 KeReleaseSpinLock(&ProfileListLock
, oldIrql
);
322 current_entry
= current_entry
->Flink
;
327 KeReleaseSpinLock(&ProfileListLock
, oldIrql
);
331 KiDeleteProfile(PVOID ObjectBody
)
335 Profile
= (PKPROFILE
)ObjectBody
;
337 KiRemoveProfile(Profile
);
338 if (Profile
->Process
!= NULL
)
340 ObDereferenceObject(Profile
->Process
);
341 Profile
->Process
= NULL
;
344 if (Profile
->BufferMdl
->MappedSystemVa
!= NULL
)
346 MmUnmapLockedPages(Profile
->BufferMdl
->MappedSystemVa
,
349 MmUnlockPages(Profile
->BufferMdl
);
350 ExFreePool(Profile
->BufferMdl
);
351 Profile
->BufferMdl
= NULL
;
355 NtInitializeProfileImplementation(VOID
)
359 InitializeListHead(&SystemProfileList
);
361 for (i
= 0; i
< PROFILE_HASH_TABLE_SIZE
; i
++)
363 InitializeListHead(&ProcessProfileListHashTable
[i
]);
366 KeInitializeSpinLock(&ProfileListLock
);
367 ProfileInitDone
= TRUE
;
369 ExProfileObjectType
= ExAllocatePool(NonPagedPool
,sizeof(OBJECT_TYPE
));
371 RtlCreateUnicodeString(&ExProfileObjectType
->TypeName
, L
"Profile");
373 ExProfileObjectType
->Tag
= TAG('P', 'R', 'O', 'F');
374 ExProfileObjectType
->MaxObjects
= ULONG_MAX
;
375 ExProfileObjectType
->MaxHandles
= ULONG_MAX
;
376 ExProfileObjectType
->TotalObjects
= 0;
377 ExProfileObjectType
->TotalHandles
= 0;
378 ExProfileObjectType
->PagedPoolCharge
= 0;
379 ExProfileObjectType
->NonpagedPoolCharge
= sizeof(KPROFILE
);
380 ExProfileObjectType
->Mapping
= &ExpProfileMapping
;
381 ExProfileObjectType
->Dump
= NULL
;
382 ExProfileObjectType
->Open
= NULL
;
383 ExProfileObjectType
->Close
= NULL
;
384 ExProfileObjectType
->Delete
= KiDeleteProfile
;
385 ExProfileObjectType
->Parse
= NULL
;
386 ExProfileObjectType
->Security
= NULL
;
387 ExProfileObjectType
->QueryName
= NULL
;
388 ExProfileObjectType
->OkayToClose
= NULL
;
389 ExProfileObjectType
->Create
= NULL
;
391 ObpCreateTypeObject(ExProfileObjectType
);
395 NtCreateProfile(OUT PHANDLE ProfileHandle
,
396 IN HANDLE Process OPTIONAL
,
402 IN KPROFILE_SOURCE ProfileSource
,
403 IN KAFFINITY Affinity
)
405 HANDLE SafeProfileHandle
;
411 * Reference the associated process
415 Status
= ObReferenceObjectByHandle(Process
,
416 PROCESS_QUERY_INFORMATION
,
421 if (!NT_SUCCESS(Status
))
429 /* FIXME: Check privilege. */
433 * Check the parameters
435 if ((pProcess
== NULL
&& ImageBase
< (PVOID
)KERNEL_BASE
) ||
436 (pProcess
!= NULL
&& ImageBase
>= (PVOID
)KERNEL_BASE
))
438 return(STATUS_INVALID_PARAMETER_3
);
440 if (((ImageSize
>> BucketSize
) * 4) >= BufferSize
)
442 return(STATUS_BUFFER_TOO_SMALL
);
444 if (ProfileSource
!= ProfileTime
)
446 return(STATUS_INVALID_PARAMETER_9
);
450 return(STATUS_INVALID_PARAMETER_10
);
456 Status
= ObCreateObject(ExGetPreviousMode(),
465 if (!NT_SUCCESS(Status
))
473 Profile
->Base
= ImageBase
;
474 Profile
->Size
= ImageSize
;
475 Profile
->BucketShift
= BucketSize
;
476 Profile
->BufferMdl
= MmCreateMdl(NULL
, Buffer
, BufferSize
);
477 if(Profile
->BufferMdl
== NULL
) {
478 DPRINT("MmCreateMdl: Out of memory!");
479 return(STATUS_NO_MEMORY
);
481 MmProbeAndLockPages(Profile
->BufferMdl
, UserMode
, IoWriteAccess
);
482 Profile
->Buffer
= MmGetSystemAddressForMdl(Profile
->BufferMdl
);
483 Profile
->BufferSize
= BufferSize
;
484 Profile
->ProcessorMask
= Affinity
;
485 Profile
->Started
= FALSE
;
486 Profile
->Process
= pProcess
;
489 * Insert the profile into the profile list data structures
491 KiInsertProfile(Profile
);
493 Status
= ObInsertObject ((PVOID
)Profile
,
499 if (!NT_SUCCESS(Status
))
501 ObDereferenceObject (Profile
);
506 * Copy the created handle back to the caller
508 Status
= MmCopyToCaller(ProfileHandle
, &SafeProfileHandle
, sizeof(HANDLE
));
509 if (!NT_SUCCESS(Status
))
511 ObDereferenceObject(Profile
);
512 ZwClose(ProfileHandle
);
516 ObDereferenceObject(Profile
);
518 return(STATUS_SUCCESS
);
522 NtQueryIntervalProfile(IN KPROFILE_SOURCE ProfileSource
,
527 if (ProfileSource
== ProfileTime
)
531 /* FIXME: What units does this use, for now nanoseconds */
533 Status
= MmCopyToCaller(Interval
, &SafeInterval
, sizeof(ULONG
));
534 if (!NT_SUCCESS(Status
))
538 return(STATUS_SUCCESS
);
540 return(STATUS_INVALID_PARAMETER_2
);
544 NtSetIntervalProfile(IN ULONG Interval
,
545 IN KPROFILE_SOURCE Source
)
547 return(STATUS_NOT_IMPLEMENTED
);
551 NtStartProfile(IN HANDLE ProfileHandle
)
556 Status
= ObReferenceObjectByHandle(ProfileHandle
,
562 if (!NT_SUCCESS(Status
))
566 Profile
->Started
= TRUE
;
567 ObDereferenceObject(Profile
);
568 return(STATUS_SUCCESS
);
572 NtStopProfile(IN HANDLE ProfileHandle
)
577 Status
= ObReferenceObjectByHandle(ProfileHandle
,
583 if (!NT_SUCCESS(Status
))
587 Profile
->Started
= FALSE
;
588 ObDereferenceObject(Profile
);
589 return(STATUS_SUCCESS
);