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