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