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