fixed prototypes of NtCreateProfile() and NtQueryIntervalProfile()
[reactos.git] / reactos / ntoskrnl / nt / profile.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
4 *
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.
9 *
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.
14 *
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.
18 */
19 /*
20 * PROJECT: ReactOS kernel
21 * FILE: ntoskrnl/nt/profile.c
22 * PURPOSE: Support for profiling
23 * PROGRAMMER: Nobody
24 * UPDATE HISTORY:
25 *
26 */
27
28 /* INCLUDES *****************************************************************/
29
30 #include <ntoskrnl.h>
31 #include <internal/debug.h>
32
33 /* TYPES ********************************************************************/
34
35 typedef struct _KPROCESS_PROFILE
36 /*
37 * List of the profile data structures associated with a process.
38 */
39 {
40 LIST_ENTRY ProfileListHead;
41 LIST_ENTRY ListEntry;
42 HANDLE Pid;
43 } KPROCESS_PROFILE, *PKPROCESS_PROFILE;
44
45 typedef struct _KPROFILE
46 /*
47 * Describes a contiguous region of process memory that is being profiled.
48 */
49 {
50 CSHORT Type;
51 CSHORT Name;
52
53 /* Entry in the list of profile data structures for this process. */
54 LIST_ENTRY ListEntry;
55
56 /* Base of the region being profiled. */
57 PVOID Base;
58
59 /* Size of the region being profiled. */
60 ULONG Size;
61
62 /* Shift of offsets from the region to buckets in the profiling buffer. */
63 ULONG BucketShift;
64
65 /* MDL which described the buffer that receives profiling data. */
66 PMDL BufferMdl;
67
68 /* System alias for the profiling buffer. */
69 PULONG Buffer;
70
71 /* Size of the buffer for profiling data. */
72 ULONG BufferSize;
73
74 /*
75 * Mask of processors for which profiling data should be collected.
76 * Currently unused.
77 */
78 ULONG ProcessorMask;
79
80 /* TRUE if profiling has been started for this region. */
81 BOOLEAN Started;
82
83 /* Pointer (and reference) to the process which is being profiled. */
84 PEPROCESS Process;
85 } KPROFILE, *PKPROFILE;
86
87 /* GLOBALS *******************************************************************/
88
89 POBJECT_TYPE EXPORTED ExProfileObjectType = NULL;
90
91 static GENERIC_MAPPING ExpProfileMapping = {
92 STANDARD_RIGHTS_READ,
93 STANDARD_RIGHTS_WRITE,
94 STANDARD_RIGHTS_EXECUTE,
95 STANDARD_RIGHTS_ALL};
96
97 /*
98 * Size of the profile hash table.
99 */
100 #define PROFILE_HASH_TABLE_SIZE (32)
101
102 /*
103 * Table of lists of per-process profiling data structures hashed by PID.
104 */
105 static LIST_ENTRY ProcessProfileListHashTable[PROFILE_HASH_TABLE_SIZE];
106
107 /*
108 * Head of the list of profile data structures for the kernel.
109 */
110 static LIST_ENTRY SystemProfileList;
111
112 /*
113 * Lock that protects the profiling data structures.
114 */
115 static KSPIN_LOCK ProfileListLock;
116
117 /*
118 * Timer interrupts happen before we have initialized the profiling
119 * data structures so just ignore them before that.
120 */
121 static BOOLEAN ProfileInitDone = FALSE;
122
123 /* FUNCTIONS *****************************************************************/
124
125 VOID STATIC
126 KiAddProfileEventToProcess(PLIST_ENTRY ListHead, PVOID Eip)
127 /*
128 * Add a profile event to the profile objects for a particular process
129 * or the system
130 */
131 {
132 PKPROFILE current;
133 PLIST_ENTRY current_entry;
134
135 current_entry = ListHead->Flink;
136 while (current_entry != ListHead)
137 {
138 current = CONTAINING_RECORD(current_entry, KPROFILE, ListEntry);
139
140 if (current->Base > Eip)
141 {
142 return;
143 }
144
145 if (current->Base <= Eip && ((char*)current->Base + current->Size) > (char*)Eip &&
146 current->Started)
147 {
148 ULONG Bucket;
149
150 Bucket = ((ULONG)((char*)Eip - (char*)current->Base)) >> current->BucketShift;
151
152 if ((Bucket*4) < current->BufferSize)
153 {
154 current->Buffer[Bucket]++;
155 }
156 }
157
158 current_entry = current_entry->Flink;
159 }
160 }
161
162 VOID
163 KiAddProfileEvent(KPROFILE_SOURCE Source, ULONG Eip)
164 /*
165 * Add a profile event
166 */
167 {
168 HANDLE Pid;
169 PKPROCESS_PROFILE current;
170 PLIST_ENTRY current_entry;
171 PLIST_ENTRY ListHead;
172
173 if (!ProfileInitDone)
174 {
175 return;
176 }
177
178 Pid = PsGetCurrentProcessId();
179 ListHead =
180 ProcessProfileListHashTable[(ULONG)Pid % PROFILE_HASH_TABLE_SIZE].Flink;
181
182 KeAcquireSpinLockAtDpcLevel(&ProfileListLock);
183
184 current_entry = ListHead;
185 while (current_entry != ListHead)
186 {
187 current = CONTAINING_RECORD(current_entry, KPROCESS_PROFILE, ListEntry);
188
189 if (current->Pid == Pid)
190 {
191 KiAddProfileEventToProcess(&current->ProfileListHead, (PVOID)Eip);
192 break;
193 }
194
195 current_entry = current_entry->Flink;
196 }
197
198 KiAddProfileEventToProcess(&SystemProfileList, (PVOID)Eip);
199
200 KeReleaseSpinLockFromDpcLevel(&ProfileListLock);
201 }
202
203 VOID
204 KiInsertProfileIntoProcess(PLIST_ENTRY ListHead, PKPROFILE Profile)
205 /*
206 * Insert a profile object into the list for a process or the system
207 */
208 {
209 PKPROFILE current;
210 PLIST_ENTRY current_entry;
211
212 current_entry = ListHead;
213 while (current_entry != ListHead)
214 {
215 current = CONTAINING_RECORD(current_entry, KPROFILE, ListEntry);
216
217 if (current->Base > Profile->Base)
218 {
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;
223 return;
224 }
225
226 current_entry = current_entry->Flink;
227 }
228 InsertTailList(ListHead, &Profile->ListEntry);
229 }
230
231 VOID
232 KiInsertProfile(PKPROFILE Profile)
233 /*
234 * Insert a profile into the relevant data structures
235 */
236 {
237 KIRQL oldIrql;
238
239 KeAcquireSpinLock(&ProfileListLock, &oldIrql);
240
241 if (Profile->Process == NULL)
242 {
243 KiInsertProfileIntoProcess(&SystemProfileList, Profile);
244 }
245 else
246 {
247 ULONG Pid;
248 PKPROCESS_PROFILE current;
249 PLIST_ENTRY current_entry;
250 PLIST_ENTRY ListHead;
251
252 Pid = Profile->Process->UniqueProcessId;
253 ListHead = &ProcessProfileListHashTable[Pid % PROFILE_HASH_TABLE_SIZE];
254
255 current_entry = ListHead;
256 while(current_entry != ListHead)
257 {
258 current = CONTAINING_RECORD(current_entry, KPROCESS_PROFILE,
259 ListEntry);
260
261 if (current->Pid == (HANDLE)Pid)
262 {
263 KiInsertProfileIntoProcess(&current->ProfileListHead, Profile);
264 KeReleaseSpinLock(&ProfileListLock, oldIrql);
265 return;
266 }
267
268 current_entry = current_entry->Flink;
269 }
270
271 current = ExAllocatePool(NonPagedPool, sizeof(KPROCESS_PROFILE));
272
273 current->Pid = (HANDLE)Pid;
274 InitializeListHead(&current->ProfileListHead);
275 InsertTailList(ListHead, &current->ListEntry);
276
277 KiInsertProfileIntoProcess(&current->ProfileListHead, Profile);
278 }
279
280 KeReleaseSpinLock(&ProfileListLock, oldIrql);
281 }
282
283 VOID KiRemoveProfile(PKPROFILE Profile)
284 {
285 KIRQL oldIrql;
286
287 KeAcquireSpinLock(&ProfileListLock, &oldIrql);
288
289 if (Profile->Process == NULL)
290 {
291 RemoveEntryList(&Profile->ListEntry);
292 }
293 else
294 {
295 ULONG Pid;
296 PLIST_ENTRY ListHead;
297 PKPROCESS_PROFILE current;
298 PLIST_ENTRY current_entry;
299
300 RemoveEntryList(&Profile->ListEntry);
301
302 Pid = Profile->Process->UniqueProcessId;
303 ListHead = &ProcessProfileListHashTable[Pid % PROFILE_HASH_TABLE_SIZE];
304
305 current_entry = ListHead;
306 while(current_entry != ListHead)
307 {
308 current = CONTAINING_RECORD(current_entry, KPROCESS_PROFILE,
309 ListEntry);
310
311 if (current->Pid == (HANDLE)Pid)
312 {
313 if (IsListEmpty(&current->ProfileListHead))
314 {
315 RemoveEntryList(&current->ListEntry);
316 ExFreePool(current);
317 }
318 KeReleaseSpinLock(&ProfileListLock, oldIrql);
319 return;
320 }
321
322 current_entry = current_entry->Flink;
323 }
324 KEBUGCHECK(0);
325 }
326
327 KeReleaseSpinLock(&ProfileListLock, oldIrql);
328 }
329
330 VOID STDCALL
331 KiDeleteProfile(PVOID ObjectBody)
332 {
333 PKPROFILE Profile;
334
335 Profile = (PKPROFILE)ObjectBody;
336
337 KiRemoveProfile(Profile);
338 if (Profile->Process != NULL)
339 {
340 ObDereferenceObject(Profile->Process);
341 Profile->Process = NULL;
342 }
343
344 if (Profile->BufferMdl->MappedSystemVa != NULL)
345 {
346 MmUnmapLockedPages(Profile->BufferMdl->MappedSystemVa,
347 Profile->BufferMdl);
348 }
349 MmUnlockPages(Profile->BufferMdl);
350 ExFreePool(Profile->BufferMdl);
351 Profile->BufferMdl = NULL;
352 }
353
354 VOID INIT_FUNCTION
355 NtInitializeProfileImplementation(VOID)
356 {
357 ULONG i;
358
359 InitializeListHead(&SystemProfileList);
360
361 for (i = 0; i < PROFILE_HASH_TABLE_SIZE; i++)
362 {
363 InitializeListHead(&ProcessProfileListHashTable[i]);
364 }
365
366 KeInitializeSpinLock(&ProfileListLock);
367 ProfileInitDone = TRUE;
368
369 ExProfileObjectType = ExAllocatePool(NonPagedPool,sizeof(OBJECT_TYPE));
370
371 RtlCreateUnicodeString(&ExProfileObjectType->TypeName, L"Profile");
372
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;
390
391 ObpCreateTypeObject(ExProfileObjectType);
392 }
393
394 NTSTATUS STDCALL
395 NtCreateProfile(OUT PHANDLE ProfileHandle,
396 IN HANDLE Process OPTIONAL,
397 IN PVOID ImageBase,
398 IN ULONG ImageSize,
399 IN ULONG BucketSize,
400 IN PVOID Buffer,
401 IN ULONG BufferSize,
402 IN KPROFILE_SOURCE ProfileSource,
403 IN KAFFINITY Affinity)
404 {
405 HANDLE SafeProfileHandle;
406 NTSTATUS Status;
407 PKPROFILE Profile;
408 PEPROCESS pProcess;
409
410 /*
411 * Reference the associated process
412 */
413 if (Process != NULL)
414 {
415 Status = ObReferenceObjectByHandle(Process,
416 PROCESS_QUERY_INFORMATION,
417 PsProcessType,
418 UserMode,
419 (PVOID*)&pProcess,
420 NULL);
421 if (!NT_SUCCESS(Status))
422 {
423 return(Status);
424 }
425 }
426 else
427 {
428 pProcess = NULL;
429 /* FIXME: Check privilege. */
430 }
431
432 /*
433 * Check the parameters
434 */
435 if ((pProcess == NULL && ImageBase < (PVOID)KERNEL_BASE) ||
436 (pProcess != NULL && ImageBase >= (PVOID)KERNEL_BASE))
437 {
438 return(STATUS_INVALID_PARAMETER_3);
439 }
440 if (((ImageSize >> BucketSize) * 4) >= BufferSize)
441 {
442 return(STATUS_BUFFER_TOO_SMALL);
443 }
444 if (ProfileSource != ProfileTime)
445 {
446 return(STATUS_INVALID_PARAMETER_9);
447 }
448 if (Affinity != 0)
449 {
450 return(STATUS_INVALID_PARAMETER_10);
451 }
452
453 /*
454 * Create the object
455 */
456 Status = ObCreateObject(ExGetPreviousMode(),
457 ExProfileObjectType,
458 NULL,
459 ExGetPreviousMode(),
460 NULL,
461 sizeof(KPROFILE),
462 0,
463 0,
464 (PVOID*)&Profile);
465 if (!NT_SUCCESS(Status))
466 {
467 return(Status);
468 }
469
470 /*
471 * Initialize it
472 */
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);
480 }
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;
487
488 /*
489 * Insert the profile into the profile list data structures
490 */
491 KiInsertProfile(Profile);
492
493 Status = ObInsertObject ((PVOID)Profile,
494 NULL,
495 STANDARD_RIGHTS_ALL,
496 0,
497 NULL,
498 &SafeProfileHandle);
499 if (!NT_SUCCESS(Status))
500 {
501 ObDereferenceObject (Profile);
502 return Status;
503 }
504
505 /*
506 * Copy the created handle back to the caller
507 */
508 Status = MmCopyToCaller(ProfileHandle, &SafeProfileHandle, sizeof(HANDLE));
509 if (!NT_SUCCESS(Status))
510 {
511 ObDereferenceObject(Profile);
512 ZwClose(ProfileHandle);
513 return(Status);
514 }
515
516 ObDereferenceObject(Profile);
517
518 return(STATUS_SUCCESS);
519 }
520
521 NTSTATUS STDCALL
522 NtQueryIntervalProfile(IN KPROFILE_SOURCE ProfileSource,
523 OUT PULONG Interval)
524 {
525 NTSTATUS Status;
526
527 if (ProfileSource == ProfileTime)
528 {
529 ULONG SafeInterval;
530
531 /* FIXME: What units does this use, for now nanoseconds */
532 SafeInterval = 100;
533 Status = MmCopyToCaller(Interval, &SafeInterval, sizeof(ULONG));
534 if (!NT_SUCCESS(Status))
535 {
536 return(Status);
537 }
538 return(STATUS_SUCCESS);
539 }
540 return(STATUS_INVALID_PARAMETER_2);
541 }
542
543 NTSTATUS STDCALL
544 NtSetIntervalProfile(IN ULONG Interval,
545 IN KPROFILE_SOURCE Source)
546 {
547 return(STATUS_NOT_IMPLEMENTED);
548 }
549
550 NTSTATUS STDCALL
551 NtStartProfile(IN HANDLE ProfileHandle)
552 {
553 NTSTATUS Status;
554 PKPROFILE Profile;
555
556 Status = ObReferenceObjectByHandle(ProfileHandle,
557 STANDARD_RIGHTS_ALL,
558 ExProfileObjectType,
559 UserMode,
560 (PVOID*)&Profile,
561 NULL);
562 if (!NT_SUCCESS(Status))
563 {
564 return(Status);
565 }
566 Profile->Started = TRUE;
567 ObDereferenceObject(Profile);
568 return(STATUS_SUCCESS);
569 }
570
571 NTSTATUS STDCALL
572 NtStopProfile(IN HANDLE ProfileHandle)
573 {
574 NTSTATUS Status;
575 PKPROFILE Profile;
576
577 Status = ObReferenceObjectByHandle(ProfileHandle,
578 STANDARD_RIGHTS_ALL,
579 ExProfileObjectType,
580 UserMode,
581 (PVOID*)&Profile,
582 NULL);
583 if (!NT_SUCCESS(Status))
584 {
585 return(Status);
586 }
587 Profile->Started = FALSE;
588 ObDereferenceObject(Profile);
589 return(STATUS_SUCCESS);
590 }
591
592