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