- Get rid of TAG() from the kernel
[reactos.git] / reactos / ntoskrnl / ex / profile.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel
4 * FILE: ntoskrnl/ex/profile.c
5 * PURPOSE: Support for Executive Profile Objects
6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
7 * Thomas Weidenmueller
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 #if defined (ALLOC_PRAGMA)
17 #pragma alloc_text(INIT, ExpInitializeProfileImplementation)
18 #endif
19
20 #define TAG_PROFILE 'forP'
21
22 /* GLOBALS *******************************************************************/
23
24 POBJECT_TYPE ExProfileObjectType = NULL;
25 KMUTEX ExpProfileMutex;
26
27 GENERIC_MAPPING ExpProfileMapping =
28 {
29 STANDARD_RIGHTS_READ | PROFILE_CONTROL,
30 STANDARD_RIGHTS_WRITE | PROFILE_CONTROL,
31 STANDARD_RIGHTS_EXECUTE | PROFILE_CONTROL,
32 PROFILE_ALL_ACCESS
33 };
34
35 /* FUNCTIONS *****************************************************************/
36
37 VOID
38 NTAPI
39 ExpDeleteProfile(PVOID ObjectBody)
40 {
41 PEPROFILE Profile;
42 ULONG State;
43
44 /* Typecast the Object */
45 Profile = (PEPROFILE)ObjectBody;
46
47 /* Check if there if the Profile was started */
48 if (Profile->LockedBufferAddress)
49 {
50 /* Stop the Profile */
51 State = KeStopProfile(Profile->ProfileObject);
52 ASSERT(State != FALSE);
53
54 /* Unmap the Locked Buffer */
55 MmUnmapLockedPages(Profile->LockedBufferAddress, Profile->Mdl);
56 MmUnlockPages(Profile->Mdl);
57 ExFreePool(Profile->Mdl);
58 }
59
60 /* Check if a Process is associated and reference it */
61 if (Profile->Process) ObDereferenceObject(Profile->Process);
62 }
63
64 VOID
65 INIT_FUNCTION
66 NTAPI
67 ExpInitializeProfileImplementation(VOID)
68 {
69 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
70 UNICODE_STRING Name;
71 DPRINT("Creating Profile Object Type\n");
72
73 /* Initialize the Mutex to lock the States */
74 KeInitializeMutex(&ExpProfileMutex, 64);
75
76 /* Create the Event Pair Object Type */
77 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
78 RtlInitUnicodeString(&Name, L"Profile");
79 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
80 ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(KPROFILE);
81 ObjectTypeInitializer.GenericMapping = ExpProfileMapping;
82 ObjectTypeInitializer.PoolType = NonPagedPool;
83 ObjectTypeInitializer.DeleteProcedure = ExpDeleteProfile;
84 ObjectTypeInitializer.ValidAccessMask = PROFILE_ALL_ACCESS;
85 ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &ExProfileObjectType);
86 }
87
88 NTSTATUS
89 NTAPI
90 NtCreateProfile(OUT PHANDLE ProfileHandle,
91 IN HANDLE Process OPTIONAL,
92 IN PVOID RangeBase,
93 IN ULONG RangeSize,
94 IN ULONG BucketSize,
95 IN PVOID Buffer,
96 IN ULONG BufferSize,
97 IN KPROFILE_SOURCE ProfileSource,
98 IN KAFFINITY Affinity)
99 {
100 HANDLE hProfile;
101 PEPROFILE Profile;
102 PEPROCESS pProcess;
103 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
104 OBJECT_ATTRIBUTES ObjectAttributes;
105 NTSTATUS Status = STATUS_SUCCESS;
106 ULONG Log2 = 0;
107 PVOID Segment = NULL;
108 PAGED_CODE();
109
110 /* Easy way out */
111 if(!BufferSize) return STATUS_INVALID_PARAMETER_7;
112
113 /* Check if this is a low-memory profile */
114 if ((!BucketSize) && (RangeBase < (PVOID)(0x10000)))
115 {
116 /* Validate size */
117 if (BufferSize < sizeof(ULONG)) return STATUS_INVALID_PARAMETER_7;
118
119 /* This will become a segmented profile object */
120 Segment = RangeBase;
121 RangeBase = 0;
122
123 /* Recalculate the bucket size */
124 BucketSize = RangeSize / (BufferSize / sizeof(ULONG));
125
126 /* Convert it to log2 */
127 BucketSize--;
128 while (BucketSize >>= 1) Log2++;
129 BucketSize += Log2 + 1;
130 }
131
132 /* Validate bucket size */
133 if ((BucketSize > 31) || (BucketSize < 2))
134 {
135 DPRINT1("Bucket size invalid\n");
136 return STATUS_INVALID_PARAMETER;
137 }
138
139 /* Make sure that the buckets can map the range */
140 if ((RangeSize >> (BucketSize - 2)) > BufferSize)
141 {
142 DPRINT1("Bucket size too small\n");
143 return STATUS_BUFFER_TOO_SMALL;
144 }
145
146 /* Make sure that the range isn't too gigantic */
147 if (((ULONG_PTR)RangeBase + RangeSize) < RangeSize)
148 {
149 DPRINT1("Range too big\n");
150 return STATUS_BUFFER_OVERFLOW;
151 }
152
153 /* Check if we were called from user-mode */
154 if(PreviousMode != KernelMode)
155 {
156 /* Entry SEH */
157 _SEH2_TRY
158 {
159 /* Make sure that the handle pointer is valid */
160 ProbeForWriteHandle(ProfileHandle);
161
162 /* Check if the buffer is valid */
163 ProbeForWrite(Buffer,
164 BufferSize,
165 sizeof(ULONG));
166 }
167 _SEH2_EXCEPT(ExSystemExceptionFilter())
168 {
169 Status = _SEH2_GetExceptionCode();
170 }
171 _SEH2_END;
172
173 /* Bail out if we failed */
174 if(!NT_SUCCESS(Status)) return Status;
175 }
176
177 /* Check if a process was specified */
178 if (Process)
179 {
180 /* Reference it */
181 Status = ObReferenceObjectByHandle(Process,
182 PROCESS_QUERY_INFORMATION,
183 PsProcessType,
184 PreviousMode,
185 (PVOID*)&pProcess,
186 NULL);
187 if (!NT_SUCCESS(Status)) return(Status);
188 }
189 else
190 {
191 /* Segmented profile objects cannot be used system-wide */
192 if (Segment) return STATUS_INVALID_PARAMETER;
193
194 /* No process was specified, which means a System-Wide Profile */
195 pProcess = NULL;
196
197 /* For this, we need to check the Privilege */
198 if(!SeSinglePrivilegeCheck(SeSystemProfilePrivilege, PreviousMode))
199 {
200 DPRINT1("NtCreateProfile: Caller requires the SeSystemProfilePrivilege privilege!\n");
201 return STATUS_PRIVILEGE_NOT_HELD;
202 }
203 }
204
205 /* Create the object */
206 InitializeObjectAttributes(&ObjectAttributes,
207 NULL,
208 0,
209 NULL,
210 NULL);
211 Status = ObCreateObject(KernelMode,
212 ExProfileObjectType,
213 &ObjectAttributes,
214 PreviousMode,
215 NULL,
216 sizeof(EPROFILE),
217 0,
218 sizeof(EPROFILE) + sizeof(KPROFILE),
219 (PVOID*)&Profile);
220 if (!NT_SUCCESS(Status))
221 {
222 /* Dereference the process object if it was specified */
223 if (pProcess) ObDereferenceObject(pProcess);
224
225 /* Return Status */
226 return Status;
227 }
228
229 /* Initialize it */
230 Profile->RangeBase = RangeBase;
231 Profile->RangeSize = RangeSize;
232 Profile->Buffer = Buffer;
233 Profile->BufferSize = BufferSize;
234 Profile->BucketSize = BucketSize;
235 Profile->LockedBufferAddress = NULL;
236 Profile->Segment = Segment;
237 Profile->ProfileSource = ProfileSource;
238 Profile->Affinity = Affinity;
239 Profile->Process = pProcess;
240
241 /* Insert into the Object Tree */
242 Status = ObInsertObject ((PVOID)Profile,
243 NULL,
244 PROFILE_CONTROL,
245 0,
246 NULL,
247 &hProfile);
248 ObDereferenceObject(Profile);
249
250 /* Check for Success */
251 if (!NT_SUCCESS(Status))
252 {
253 /* Dereference Process on failure */
254 if (pProcess) ObDereferenceObject(pProcess);
255 return Status;
256 }
257
258 /* Enter SEH */
259 _SEH2_TRY
260 {
261 /* Copy the created handle back to the caller*/
262 *ProfileHandle = hProfile;
263 }
264 _SEH2_EXCEPT(ExSystemExceptionFilter())
265 {
266 Status = _SEH2_GetExceptionCode();
267 }
268 _SEH2_END;
269
270 /* Return Status */
271 return Status;
272 }
273
274 NTSTATUS
275 NTAPI
276 NtQueryPerformanceCounter(OUT PLARGE_INTEGER PerformanceCounter,
277 OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL)
278 {
279 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
280 LARGE_INTEGER PerfFrequency;
281 NTSTATUS Status = STATUS_SUCCESS;
282
283 /* Check if we were called from user-mode */
284 if(PreviousMode != KernelMode)
285 {
286 /* Entry SEH Block */
287 _SEH2_TRY
288 {
289 /* Make sure the counter and frequency are valid */
290 ProbeForWriteLargeInteger(PerformanceCounter);
291 if (PerformanceFrequency)
292 {
293 ProbeForWriteLargeInteger(PerformanceFrequency);
294 }
295 }
296 _SEH2_EXCEPT(ExSystemExceptionFilter())
297 {
298 Status = _SEH2_GetExceptionCode();
299 }
300 _SEH2_END;
301
302 /* If the pointers are invalid, bail out */
303 if(!NT_SUCCESS(Status)) return Status;
304 }
305
306 /* Enter a new SEH Block */
307 _SEH2_TRY
308 {
309 /* Query the Kernel */
310 *PerformanceCounter = KeQueryPerformanceCounter(&PerfFrequency);
311
312 /* Return Frequency if requested */
313 if(PerformanceFrequency) *PerformanceFrequency = PerfFrequency;
314 }
315 _SEH2_EXCEPT(ExSystemExceptionFilter())
316 {
317 Status = _SEH2_GetExceptionCode();
318 }
319 _SEH2_END;
320
321 /* Return status to caller */
322 return Status;
323 }
324
325 NTSTATUS
326 NTAPI
327 NtStartProfile(IN HANDLE ProfileHandle)
328 {
329 PEPROFILE Profile;
330 PKPROFILE ProfileObject;
331 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
332 PVOID TempLockedBufferAddress;
333 NTSTATUS Status;
334 PAGED_CODE();
335
336 /* Get the Object */
337 Status = ObReferenceObjectByHandle(ProfileHandle,
338 PROFILE_CONTROL,
339 ExProfileObjectType,
340 PreviousMode,
341 (PVOID*)&Profile,
342 NULL);
343 if (!NT_SUCCESS(Status)) return(Status);
344
345 /* To avoid a Race, wait on the Mutex */
346 KeWaitForSingleObject(&ExpProfileMutex,
347 Executive,
348 KernelMode,
349 FALSE,
350 NULL);
351
352 /* The Profile can still be enabled though, so handle that */
353 if (Profile->LockedBufferAddress)
354 {
355 /* Release our lock, dereference and return */
356 KeReleaseMutex(&ExpProfileMutex, FALSE);
357 ObDereferenceObject(Profile);
358 return STATUS_PROFILING_NOT_STOPPED;
359 }
360
361 /* Allocate a Kernel Profile Object. */
362 ProfileObject = ExAllocatePoolWithTag(NonPagedPool,
363 sizeof(EPROFILE),
364 TAG_PROFILE);
365 if (!ProfileObject)
366 {
367 /* Out of memory, fail */
368 KeReleaseMutex(&ExpProfileMutex, FALSE);
369 ObDereferenceObject(Profile);
370 return STATUS_INSUFFICIENT_RESOURCES;
371 }
372
373 /* Allocate the Mdl Structure */
374 Profile->Mdl = MmCreateMdl(NULL, Profile->Buffer, Profile->BufferSize);
375
376 /* Protect this in SEH as we might raise an exception */
377 _SEH2_TRY
378 {
379 /* Probe and Lock for Write Access */
380 MmProbeAndLockPages(Profile->Mdl, PreviousMode, IoWriteAccess);
381 }
382 _SEH2_EXCEPT(ExSystemExceptionFilter())
383 {
384 /* Get the exception code */
385 Status = _SEH2_GetExceptionCode();
386 }
387 _SEH2_END;
388
389 /* Fail if we raised an exception */
390 if (!NT_SUCCESS(Status))
391 {
392 /* Release our lock, free the buffer, dereference and return */
393 KeReleaseMutex(&ExpProfileMutex, FALSE);
394 ObDereferenceObject(Profile);
395 ExFreePool(ProfileObject);
396 return Status;
397 }
398
399 /* Map the pages */
400 TempLockedBufferAddress = MmMapLockedPages(Profile->Mdl, KernelMode);
401
402 /* Initialize the Kernel Profile Object */
403 Profile->ProfileObject = ProfileObject;
404 KeInitializeProfile(ProfileObject,
405 (PKPROCESS)Profile->Process,
406 Profile->RangeBase,
407 Profile->RangeSize,
408 Profile->BucketSize,
409 Profile->ProfileSource,
410 Profile->Affinity);
411
412 /* Start the Profiling */
413 KeStartProfile(ProfileObject, TempLockedBufferAddress);
414
415 /* Now it's safe to save this */
416 Profile->LockedBufferAddress = TempLockedBufferAddress;
417
418 /* Release mutex, dereference and return */
419 KeReleaseMutex(&ExpProfileMutex, FALSE);
420 ObDereferenceObject(Profile);
421 return STATUS_SUCCESS;
422 }
423
424 NTSTATUS
425 NTAPI
426 NtStopProfile(IN HANDLE ProfileHandle)
427 {
428 PEPROFILE Profile;
429 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
430 NTSTATUS Status;
431 PAGED_CODE();
432
433 /* Get the Object */
434 Status = ObReferenceObjectByHandle(ProfileHandle,
435 PROFILE_CONTROL,
436 ExProfileObjectType,
437 PreviousMode,
438 (PVOID*)&Profile,
439 NULL);
440 if (!NT_SUCCESS(Status)) return(Status);
441
442 /* Get the Mutex */
443 KeWaitForSingleObject(&ExpProfileMutex,
444 Executive,
445 KernelMode,
446 FALSE,
447 NULL);
448
449 /* Make sure the Profile Object is really Started */
450 if (!Profile->LockedBufferAddress)
451 {
452 Status = STATUS_PROFILING_NOT_STARTED;
453 goto Exit;
454 }
455
456 /* Stop the Profile */
457 KeStopProfile(Profile->ProfileObject);
458
459 /* Unlock the Buffer */
460 MmUnmapLockedPages(Profile->LockedBufferAddress, Profile->Mdl);
461 MmUnlockPages(Profile->Mdl);
462 ExFreePool(Profile->ProfileObject);
463
464 /* Clear the Locked Buffer pointer, meaning the Object is Stopped */
465 Profile->LockedBufferAddress = NULL;
466
467 Exit:
468 /* Release Mutex, Dereference and Return */
469 KeReleaseMutex(&ExpProfileMutex, FALSE);
470 ObDereferenceObject(Profile);
471 return Status;
472 }
473
474 NTSTATUS
475 NTAPI
476 NtQueryIntervalProfile(IN KPROFILE_SOURCE ProfileSource,
477 OUT PULONG Interval)
478 {
479 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
480 ULONG ReturnInterval;
481 NTSTATUS Status = STATUS_SUCCESS;
482 PAGED_CODE();
483
484 /* Check if we were called from user-mode */
485 if(PreviousMode != KernelMode)
486 {
487 /* Enter SEH Block */
488 _SEH2_TRY
489 {
490 /* Validate interval */
491 ProbeForWriteUlong(Interval);
492 }
493 _SEH2_EXCEPT(ExSystemExceptionFilter())
494 {
495 Status = _SEH2_GetExceptionCode();
496 }
497 _SEH2_END;
498
499 /* If pointer was invalid, bail out */
500 if(!NT_SUCCESS(Status)) return Status;
501 }
502
503 /* Query the Interval */
504 ReturnInterval = KeQueryIntervalProfile(ProfileSource);
505
506 /* Enter SEH block for return */
507 _SEH2_TRY
508 {
509 /* Return the data */
510 *Interval = ReturnInterval;
511 }
512 _SEH2_EXCEPT(ExSystemExceptionFilter())
513 {
514 Status = _SEH2_GetExceptionCode();
515 }
516 _SEH2_END;
517
518 /* Return Success */
519 return STATUS_SUCCESS;
520 }
521
522 NTSTATUS
523 NTAPI
524 NtSetIntervalProfile(IN ULONG Interval,
525 IN KPROFILE_SOURCE Source)
526 {
527 /* Let the Kernel do the job */
528 KeSetIntervalProfile(Interval, Source);
529
530 /* Nothing can go wrong */
531 return STATUS_SUCCESS;
532 }