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