Merge trunk head (r43756)
[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;
106 ULONG Log2 = 0;
107 ULONG_PTR Segment = 0;
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 = (ULONG_PTR)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(EXCEPTION_EXECUTE_HANDLER)
168 {
169 /* Return the exception code */
170 _SEH2_YIELD(return _SEH2_GetExceptionCode());
171 }
172 _SEH2_END;
173 }
174
175 /* Check if a process was specified */
176 if (Process)
177 {
178 /* Reference it */
179 Status = ObReferenceObjectByHandle(Process,
180 PROCESS_QUERY_INFORMATION,
181 PsProcessType,
182 PreviousMode,
183 (PVOID*)&pProcess,
184 NULL);
185 if (!NT_SUCCESS(Status)) return(Status);
186 }
187 else
188 {
189 /* Segmented profile objects cannot be used system-wide */
190 if (Segment) return STATUS_INVALID_PARAMETER;
191
192 /* No process was specified, which means a System-Wide Profile */
193 pProcess = NULL;
194
195 /* For this, we need to check the Privilege */
196 if(!SeSinglePrivilegeCheck(SeSystemProfilePrivilege, PreviousMode))
197 {
198 DPRINT1("NtCreateProfile: Caller requires the SeSystemProfilePrivilege privilege!\n");
199 return STATUS_PRIVILEGE_NOT_HELD;
200 }
201 }
202
203 /* Create the object */
204 InitializeObjectAttributes(&ObjectAttributes,
205 NULL,
206 0,
207 NULL,
208 NULL);
209 Status = ObCreateObject(KernelMode,
210 ExProfileObjectType,
211 &ObjectAttributes,
212 PreviousMode,
213 NULL,
214 sizeof(EPROFILE),
215 0,
216 sizeof(EPROFILE) + sizeof(KPROFILE),
217 (PVOID*)&Profile);
218 if (!NT_SUCCESS(Status))
219 {
220 /* Dereference the process object if it was specified */
221 if (pProcess) ObDereferenceObject(pProcess);
222
223 /* Return Status */
224 return Status;
225 }
226
227 /* Initialize it */
228 Profile->RangeBase = RangeBase;
229 Profile->RangeSize = RangeSize;
230 Profile->Buffer = Buffer;
231 Profile->BufferSize = BufferSize;
232 Profile->BucketSize = BucketSize;
233 Profile->LockedBufferAddress = NULL;
234 Profile->Segment = Segment;
235 Profile->ProfileSource = ProfileSource;
236 Profile->Affinity = Affinity;
237 Profile->Process = pProcess;
238
239 /* Insert into the Object Tree */
240 Status = ObInsertObject ((PVOID)Profile,
241 NULL,
242 PROFILE_CONTROL,
243 0,
244 NULL,
245 &hProfile);
246 ObDereferenceObject(Profile);
247
248 /* Check for Success */
249 if (!NT_SUCCESS(Status))
250 {
251 /* Dereference Process on failure */
252 if (pProcess) ObDereferenceObject(pProcess);
253 return Status;
254 }
255
256 /* Enter SEH */
257 _SEH2_TRY
258 {
259 /* Copy the created handle back to the caller*/
260 *ProfileHandle = hProfile;
261 }
262 _SEH2_EXCEPT(ExSystemExceptionFilter())
263 {
264 Status = _SEH2_GetExceptionCode();
265 }
266 _SEH2_END;
267
268 /* Return Status */
269 return Status;
270 }
271
272 NTSTATUS
273 NTAPI
274 NtQueryPerformanceCounter(OUT PLARGE_INTEGER PerformanceCounter,
275 OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL)
276 {
277 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
278 LARGE_INTEGER PerfFrequency;
279 NTSTATUS Status = STATUS_SUCCESS;
280
281 /* Check if we were called from user-mode */
282 if (PreviousMode != KernelMode)
283 {
284 /* Entry SEH Block */
285 _SEH2_TRY
286 {
287 /* Make sure the counter and frequency are valid */
288 ProbeForWriteLargeInteger(PerformanceCounter);
289 if (PerformanceFrequency)
290 {
291 ProbeForWriteLargeInteger(PerformanceFrequency);
292 }
293 }
294 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
295 {
296 /* Return the exception code */
297 _SEH2_YIELD(return _SEH2_GetExceptionCode());
298 }
299 _SEH2_END;
300 }
301
302 /* Enter a new SEH Block */
303 _SEH2_TRY
304 {
305 /* Query the Kernel */
306 *PerformanceCounter = KeQueryPerformanceCounter(&PerfFrequency);
307
308 /* Return Frequency if requested */
309 if (PerformanceFrequency) *PerformanceFrequency = PerfFrequency;
310 }
311 _SEH2_EXCEPT(ExSystemExceptionFilter())
312 {
313 /* Get the exception code */
314 Status = _SEH2_GetExceptionCode();
315 }
316 _SEH2_END;
317
318 /* Return status to caller */
319 return Status;
320 }
321
322 NTSTATUS
323 NTAPI
324 NtStartProfile(IN HANDLE ProfileHandle)
325 {
326 PEPROFILE Profile;
327 PKPROFILE ProfileObject;
328 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
329 PVOID TempLockedBufferAddress;
330 NTSTATUS Status;
331 PAGED_CODE();
332
333 /* Get the Object */
334 Status = ObReferenceObjectByHandle(ProfileHandle,
335 PROFILE_CONTROL,
336 ExProfileObjectType,
337 PreviousMode,
338 (PVOID*)&Profile,
339 NULL);
340 if (!NT_SUCCESS(Status)) return(Status);
341
342 /* To avoid a Race, wait on the Mutex */
343 KeWaitForSingleObject(&ExpProfileMutex,
344 Executive,
345 KernelMode,
346 FALSE,
347 NULL);
348
349 /* The Profile can still be enabled though, so handle that */
350 if (Profile->LockedBufferAddress)
351 {
352 /* Release our lock, dereference and return */
353 KeReleaseMutex(&ExpProfileMutex, FALSE);
354 ObDereferenceObject(Profile);
355 return STATUS_PROFILING_NOT_STOPPED;
356 }
357
358 /* Allocate a Kernel Profile Object. */
359 ProfileObject = ExAllocatePoolWithTag(NonPagedPool,
360 sizeof(EPROFILE),
361 TAG_PROFILE);
362 if (!ProfileObject)
363 {
364 /* Out of memory, fail */
365 KeReleaseMutex(&ExpProfileMutex, FALSE);
366 ObDereferenceObject(Profile);
367 return STATUS_INSUFFICIENT_RESOURCES;
368 }
369
370 /* Allocate the Mdl Structure */
371 Profile->Mdl = MmCreateMdl(NULL, Profile->Buffer, Profile->BufferSize);
372
373 /* Protect this in SEH as we might raise an exception */
374 _SEH2_TRY
375 {
376 /* Probe and Lock for Write Access */
377 MmProbeAndLockPages(Profile->Mdl, PreviousMode, IoWriteAccess);
378 }
379 _SEH2_EXCEPT(ExSystemExceptionFilter())
380 {
381 /* Release our lock, free the buffer, dereference and return */
382 KeReleaseMutex(&ExpProfileMutex, FALSE);
383 ObDereferenceObject(Profile);
384 ExFreePool(ProfileObject);
385 _SEH2_YIELD(return _SEH2_GetExceptionCode());
386 }
387 _SEH2_END;
388
389 /* Map the pages */
390 TempLockedBufferAddress = MmMapLockedPages(Profile->Mdl, KernelMode);
391
392 /* Initialize the Kernel Profile Object */
393 Profile->ProfileObject = ProfileObject;
394 KeInitializeProfile(ProfileObject,
395 (PKPROCESS)Profile->Process,
396 Profile->RangeBase,
397 Profile->RangeSize,
398 Profile->BucketSize,
399 Profile->ProfileSource,
400 Profile->Affinity);
401
402 /* Start the Profiling */
403 KeStartProfile(ProfileObject, TempLockedBufferAddress);
404
405 /* Now it's safe to save this */
406 Profile->LockedBufferAddress = TempLockedBufferAddress;
407
408 /* Release mutex, dereference and return */
409 KeReleaseMutex(&ExpProfileMutex, FALSE);
410 ObDereferenceObject(Profile);
411 return STATUS_SUCCESS;
412 }
413
414 NTSTATUS
415 NTAPI
416 NtStopProfile(IN HANDLE ProfileHandle)
417 {
418 PEPROFILE Profile;
419 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
420 NTSTATUS Status;
421 PAGED_CODE();
422
423 /* Get the Object */
424 Status = ObReferenceObjectByHandle(ProfileHandle,
425 PROFILE_CONTROL,
426 ExProfileObjectType,
427 PreviousMode,
428 (PVOID*)&Profile,
429 NULL);
430 if (!NT_SUCCESS(Status)) return(Status);
431
432 /* Get the Mutex */
433 KeWaitForSingleObject(&ExpProfileMutex,
434 Executive,
435 KernelMode,
436 FALSE,
437 NULL);
438
439 /* Make sure the Profile Object is really Started */
440 if (!Profile->LockedBufferAddress)
441 {
442 Status = STATUS_PROFILING_NOT_STARTED;
443 goto Exit;
444 }
445
446 /* Stop the Profile */
447 KeStopProfile(Profile->ProfileObject);
448
449 /* Unlock the Buffer */
450 MmUnmapLockedPages(Profile->LockedBufferAddress, Profile->Mdl);
451 MmUnlockPages(Profile->Mdl);
452 ExFreePool(Profile->ProfileObject);
453
454 /* Clear the Locked Buffer pointer, meaning the Object is Stopped */
455 Profile->LockedBufferAddress = NULL;
456
457 Exit:
458 /* Release Mutex, Dereference and Return */
459 KeReleaseMutex(&ExpProfileMutex, FALSE);
460 ObDereferenceObject(Profile);
461 return Status;
462 }
463
464 NTSTATUS
465 NTAPI
466 NtQueryIntervalProfile(IN KPROFILE_SOURCE ProfileSource,
467 OUT PULONG Interval)
468 {
469 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
470 ULONG ReturnInterval;
471 NTSTATUS Status = STATUS_SUCCESS;
472 PAGED_CODE();
473
474 /* Check if we were called from user-mode */
475 if (PreviousMode != KernelMode)
476 {
477 /* Enter SEH Block */
478 _SEH2_TRY
479 {
480 /* Validate interval */
481 ProbeForWriteUlong(Interval);
482 }
483 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
484 {
485 /* Return the exception code */
486 _SEH2_YIELD(return _SEH2_GetExceptionCode());
487 }
488 _SEH2_END;
489 }
490
491 /* Query the Interval */
492 ReturnInterval = KeQueryIntervalProfile(ProfileSource);
493
494 /* Enter SEH block for return */
495 _SEH2_TRY
496 {
497 /* Return the data */
498 *Interval = ReturnInterval;
499 }
500 _SEH2_EXCEPT(ExSystemExceptionFilter())
501 {
502 /* Get the exception code */
503 Status = _SEH2_GetExceptionCode();
504 }
505 _SEH2_END;
506
507 /* Return Success */
508 return Status;
509 }
510
511 NTSTATUS
512 NTAPI
513 NtSetIntervalProfile(IN ULONG Interval,
514 IN KPROFILE_SOURCE Source)
515 {
516 /* Let the Kernel do the job */
517 KeSetIntervalProfile(Interval, Source);
518
519 /* Nothing can go wrong */
520 return STATUS_SUCCESS;
521 }