ea8aa4cec7c45b4cf1438a692904134d497c287e
[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 *
7 * PROGRAMMERS: Alex Ionescu
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #define NDEBUG
13 #include <ntoskrnl.h>
14 #include <internal/debug.h>
15
16 /* This structure is a *GUESS* -- Alex */
17 typedef struct _EPROFILE {
18 PEPROCESS Process;
19 PVOID ImageBase;
20 ULONG ImageSize;
21 ULONG BucketSize;
22 PVOID Buffer;
23 ULONG BufferSize;
24 PKPROFILE KeProfile;
25 KPROFILE_SOURCE ProfileSource;
26 KAFFINITY Affinity;
27 PMDL Mdl;
28 PVOID LockedBuffer;
29 } EPROFILE, *PEPROFILE;
30
31 /* GLOBALS *******************************************************************/
32
33 POBJECT_TYPE EXPORTED ExProfileObjectType = NULL;
34
35 static KMUTEX ExpProfileMutex;
36
37 #define PROFILE_CONTROL 1
38
39 static GENERIC_MAPPING ExpProfileMapping = {
40 STANDARD_RIGHTS_READ | PROFILE_CONTROL,
41 STANDARD_RIGHTS_WRITE | PROFILE_CONTROL,
42 STANDARD_RIGHTS_EXECUTE | PROFILE_CONTROL,
43 STANDARD_RIGHTS_ALL};
44
45 VOID
46 STDCALL
47 ExpDeleteProfile(PVOID ObjectBody)
48 {
49 PEPROFILE Profile;
50
51 /* Typecast the Object */
52 Profile = (PEPROFILE)ObjectBody;
53
54 /* Check if there if the Profile was started */
55 if (Profile->LockedBuffer) {
56
57 /* Stop the Profile */
58 KeStopProfile(Profile->KeProfile);
59
60 /* Unmap the Locked Buffer */
61 MmUnmapLockedPages(Profile->LockedBuffer, Profile->Mdl);
62 MmUnlockPages(Profile->Mdl);
63 ExFreePool(Profile->Mdl);
64 }
65
66 /* Check if a Process is associated */
67 if (Profile->Process != NULL) {
68
69 /* Dereference it */
70 ObDereferenceObject(Profile->Process);
71 Profile->Process = NULL;
72 }
73 }
74
75 VOID
76 INIT_FUNCTION
77 STDCALL
78 ExpInitializeProfileImplementation(VOID)
79 {
80 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
81 UNICODE_STRING Name;
82
83 /* Initialize the Mutex to lock the States */
84 KeInitializeMutex(&ExpProfileMutex, 0x40);
85
86 DPRINT("Creating Profile Object Type\n");
87
88 /* Create the Event Pair Object Type */
89 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
90 RtlInitUnicodeString(&Name, L"Profile");
91 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
92 ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(KPROFILE);
93 ObjectTypeInitializer.GenericMapping = ExpProfileMapping;
94 ObjectTypeInitializer.PoolType = NonPagedPool;
95 ObjectTypeInitializer.DeleteProcedure = ExpDeleteProfile;
96 ObjectTypeInitializer.ValidAccessMask = STANDARD_RIGHTS_ALL;
97 ObpCreateTypeObject(&ObjectTypeInitializer, &Name, &ExProfileObjectType);
98 }
99
100 NTSTATUS
101 STDCALL
102 NtCreateProfile(OUT PHANDLE ProfileHandle,
103 IN HANDLE Process OPTIONAL,
104 IN PVOID ImageBase,
105 IN ULONG ImageSize,
106 IN ULONG BucketSize,
107 IN PVOID Buffer,
108 IN ULONG BufferSize,
109 IN KPROFILE_SOURCE ProfileSource,
110 IN KAFFINITY Affinity)
111 {
112 HANDLE hProfile;
113 PEPROFILE Profile;
114 PEPROCESS pProcess;
115 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
116 OBJECT_ATTRIBUTES ObjectAttributes;
117 NTSTATUS Status = STATUS_SUCCESS;
118
119 PAGED_CODE();
120
121 /* Easy way out */
122 if(BufferSize == 0) return STATUS_INVALID_PARAMETER_7;
123
124 /* Check the Parameters for validity */
125 if(PreviousMode != KernelMode) {
126
127 _SEH_TRY {
128
129 ProbeForWrite(ProfileHandle,
130 sizeof(HANDLE),
131 sizeof(ULONG));
132
133 ProbeForWrite(Buffer,
134 BufferSize,
135 sizeof(ULONG));
136 } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) {
137
138 Status = _SEH_GetExceptionCode();
139 } _SEH_END;
140
141 if(!NT_SUCCESS(Status)) return Status;
142 }
143
144 /* Check if a process was specified */
145 if (Process) {
146
147 /* Reference it */
148 Status = ObReferenceObjectByHandle(Process,
149 PROCESS_QUERY_INFORMATION,
150 PsProcessType,
151 PreviousMode,
152 (PVOID*)&pProcess,
153 NULL);
154 if (!NT_SUCCESS(Status)) return(Status);
155
156 } else {
157
158 /* No process was specified, which means a System-Wide Profile */
159 pProcess = NULL;
160
161 /* For this, we need to check the Privilege */
162 if(!SeSinglePrivilegeCheck(SeSystemProfilePrivilege, PreviousMode)) {
163
164 DPRINT1("NtCreateProfile: Caller requires the SeSystemProfilePrivilege privilege!\n");
165 return STATUS_PRIVILEGE_NOT_HELD;
166 }
167 }
168
169 /* Create the object */
170 InitializeObjectAttributes(&ObjectAttributes,
171 NULL,
172 0,
173 NULL,
174 NULL);
175 Status = ObCreateObject(KernelMode,
176 ExProfileObjectType,
177 &ObjectAttributes,
178 PreviousMode,
179 NULL,
180 sizeof(EPROFILE),
181 0,
182 0,
183 (PVOID*)&Profile);
184 if (!NT_SUCCESS(Status)) return(Status);
185
186 /* Initialize it */
187 Profile->ImageBase = ImageBase;
188 Profile->ImageSize = ImageSize;
189 Profile->Buffer = Buffer;
190 Profile->BufferSize = BufferSize;
191 Profile->BucketSize = BucketSize;
192 Profile->LockedBuffer = NULL;
193 Profile->Affinity = Affinity;
194 Profile->Process = pProcess;
195
196 /* Insert into the Object Tree */
197 Status = ObInsertObject ((PVOID)Profile,
198 NULL,
199 PROFILE_CONTROL,
200 0,
201 NULL,
202 &hProfile);
203 ObDereferenceObject(Profile);
204
205 /* Check for Success */
206 if (!NT_SUCCESS(Status)) {
207
208 /* Dereference Process on failure */
209 if (pProcess) ObDereferenceObject(pProcess);
210 return Status;
211 }
212
213 /* Copy the created handle back to the caller*/
214 _SEH_TRY {
215
216 *ProfileHandle = hProfile;
217
218 } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) {
219
220 Status = _SEH_GetExceptionCode();
221 } _SEH_END;
222
223 /* Return Status */
224 return Status;
225 }
226
227 NTSTATUS
228 STDCALL
229 NtQueryPerformanceCounter(OUT PLARGE_INTEGER PerformanceCounter,
230 OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL)
231 {
232 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
233 LARGE_INTEGER PerfFrequency;
234 NTSTATUS Status = STATUS_SUCCESS;
235
236 /* Check the Parameters for validity */
237 if(PreviousMode != KernelMode) {
238
239 _SEH_TRY {
240
241 ProbeForWrite(PerformanceCounter,
242 sizeof(LARGE_INTEGER),
243 sizeof(ULONG));
244
245 ProbeForWrite(PerformanceFrequency,
246 sizeof(LARGE_INTEGER),
247 sizeof(ULONG));
248 } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) {
249
250 Status = _SEH_GetExceptionCode();
251 } _SEH_END;
252
253 if(!NT_SUCCESS(Status)) return Status;
254 }
255
256 _SEH_TRY {
257
258 /* Query the Kernel */
259 *PerformanceCounter = KeQueryPerformanceCounter(&PerfFrequency);
260
261 /* Return Frequency if requested */
262 if(PerformanceFrequency) {
263
264 *PerformanceFrequency = PerfFrequency;
265 }
266 } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) {
267
268 Status = _SEH_GetExceptionCode();
269
270 } _SEH_END;
271
272 return Status;
273 }
274
275 NTSTATUS
276 STDCALL
277 NtStartProfile(IN HANDLE ProfileHandle)
278 {
279 PEPROFILE Profile;
280 PKPROFILE KeProfile;
281 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
282 PVOID TempLockedBuffer;
283 NTSTATUS Status;
284
285 PAGED_CODE();
286
287 /* Get the Object */
288 Status = ObReferenceObjectByHandle(ProfileHandle,
289 PROFILE_CONTROL,
290 ExProfileObjectType,
291 PreviousMode,
292 (PVOID*)&Profile,
293 NULL);
294 if (!NT_SUCCESS(Status)) return(Status);
295
296 /* To avoid a Race, wait on the Mutex */
297 KeWaitForSingleObject(&ExpProfileMutex,
298 Executive,
299 KernelMode,
300 FALSE,
301 NULL);
302
303 /* The Profile can still be enabled though, so handle that */
304 if (Profile->LockedBuffer) {
305
306 /* Release our lock, dereference and return */
307 KeReleaseMutex(&ExpProfileMutex, FALSE);
308 ObDereferenceObject(Profile);
309 return STATUS_PROFILING_NOT_STOPPED;
310 }
311
312 /* Allocate a Kernel Profile Object. */
313 KeProfile = ExAllocatePoolWithTag(NonPagedPool,
314 sizeof(EPROFILE),
315 TAG('P', 'r', 'o', 'f'));
316
317 /* Allocate the Mdl Structure */
318 Profile->Mdl = MmCreateMdl(NULL, Profile->Buffer, Profile->BufferSize);
319
320 /* Probe and Lock for Write Access */
321 MmProbeAndLockPages(Profile->Mdl, PreviousMode, IoWriteAccess);
322
323 /* Map the pages */
324 TempLockedBuffer = MmMapLockedPages(Profile->Mdl, KernelMode);
325
326 /* Initialize the Kernel Profile Object */
327 Profile->KeProfile = KeProfile;
328 KeInitializeProfile(KeProfile,
329 (PKPROCESS)Profile->Process,
330 Profile->ImageBase,
331 Profile->ImageSize,
332 Profile->BucketSize,
333 Profile->ProfileSource,
334 Profile->Affinity);
335
336 /* Start the Profiling */
337 KeStartProfile(KeProfile, TempLockedBuffer);
338
339 /* Now it's safe to save this */
340 Profile->LockedBuffer = TempLockedBuffer;
341
342 /* Release mutex, dereference and return */
343 KeReleaseMutex(&ExpProfileMutex, FALSE);
344 ObDereferenceObject(Profile);
345 return STATUS_SUCCESS;
346 }
347
348 NTSTATUS
349 STDCALL
350 NtStopProfile(IN HANDLE ProfileHandle)
351 {
352 PEPROFILE Profile;
353 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
354 NTSTATUS Status;
355
356 PAGED_CODE();
357
358 /* Get the Object */
359 Status = ObReferenceObjectByHandle(ProfileHandle,
360 PROFILE_CONTROL,
361 ExProfileObjectType,
362 PreviousMode,
363 (PVOID*)&Profile,
364 NULL);
365 if (!NT_SUCCESS(Status)) return(Status);
366
367 /* Get the Mutex */
368 KeWaitForSingleObject(&ExpProfileMutex,
369 Executive,
370 KernelMode,
371 FALSE,
372 NULL);
373
374 /* Make sure the Profile Object is really Started */
375 if (!Profile->LockedBuffer) {
376
377 Status = STATUS_PROFILING_NOT_STARTED;
378 goto Exit;
379 }
380
381 /* Stop the Profile */
382 KeStopProfile(Profile->KeProfile);
383
384 /* Unlock the Buffer */
385 MmUnmapLockedPages(Profile->LockedBuffer, Profile->Mdl);
386 MmUnlockPages(Profile->Mdl);
387 ExFreePool(Profile->KeProfile);
388
389 /* Clear the Locked Buffer pointer, meaning the Object is Stopped */
390 Profile->LockedBuffer = NULL;
391
392 Exit:
393 /* Release Mutex, Dereference and Return */
394 KeReleaseMutex(&ExpProfileMutex, FALSE);
395 ObDereferenceObject(Profile);
396 return Status;
397 }
398
399 NTSTATUS
400 STDCALL
401 NtQueryIntervalProfile(IN KPROFILE_SOURCE ProfileSource,
402 OUT PULONG Interval)
403 {
404 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
405 ULONG ReturnInterval;
406 NTSTATUS Status = STATUS_SUCCESS;
407
408 PAGED_CODE();
409
410 /* Check the Parameters for validity */
411 if(PreviousMode != KernelMode) {
412
413 _SEH_TRY {
414
415 ProbeForWrite(Interval,
416 sizeof(ULONG),
417 sizeof(ULONG));
418
419 } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) {
420
421 Status = _SEH_GetExceptionCode();
422 } _SEH_END;
423
424 if(!NT_SUCCESS(Status)) return Status;
425 }
426
427 /* Query the Interval */
428 ReturnInterval = KeQueryIntervalProfile(ProfileSource);
429
430 /* Return the data */
431 _SEH_TRY {
432
433 *Interval = ReturnInterval;
434
435 } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) {
436
437 Status = _SEH_GetExceptionCode();
438
439 } _SEH_END;
440
441 /* Return Success */
442 return STATUS_SUCCESS;
443 }
444
445 NTSTATUS
446 STDCALL
447 NtSetIntervalProfile(IN ULONG Interval,
448 IN KPROFILE_SOURCE Source)
449 {
450 /* Let the Kernel do the job */
451 KeSetIntervalProfile(Interval, Source);
452
453 /* Nothing can go wrong */
454 return STATUS_SUCCESS;
455 }