use inlined probing macros for basic types
[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 ProbeForWriteHandle(ProfileHandle);
130
131 ProbeForWrite(Buffer,
132 BufferSize,
133 sizeof(ULONG));
134 } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) {
135
136 Status = _SEH_GetExceptionCode();
137 } _SEH_END;
138
139 if(!NT_SUCCESS(Status)) return Status;
140 }
141
142 /* Check if a process was specified */
143 if (Process) {
144
145 /* Reference it */
146 Status = ObReferenceObjectByHandle(Process,
147 PROCESS_QUERY_INFORMATION,
148 PsProcessType,
149 PreviousMode,
150 (PVOID*)&pProcess,
151 NULL);
152 if (!NT_SUCCESS(Status)) return(Status);
153
154 } else {
155
156 /* No process was specified, which means a System-Wide Profile */
157 pProcess = NULL;
158
159 /* For this, we need to check the Privilege */
160 if(!SeSinglePrivilegeCheck(SeSystemProfilePrivilege, PreviousMode)) {
161
162 DPRINT1("NtCreateProfile: Caller requires the SeSystemProfilePrivilege privilege!\n");
163 return STATUS_PRIVILEGE_NOT_HELD;
164 }
165 }
166
167 /* Create the object */
168 InitializeObjectAttributes(&ObjectAttributes,
169 NULL,
170 0,
171 NULL,
172 NULL);
173 Status = ObCreateObject(KernelMode,
174 ExProfileObjectType,
175 &ObjectAttributes,
176 PreviousMode,
177 NULL,
178 sizeof(EPROFILE),
179 0,
180 0,
181 (PVOID*)&Profile);
182 if (!NT_SUCCESS(Status)) return(Status);
183
184 /* Initialize it */
185 Profile->ImageBase = ImageBase;
186 Profile->ImageSize = ImageSize;
187 Profile->Buffer = Buffer;
188 Profile->BufferSize = BufferSize;
189 Profile->BucketSize = BucketSize;
190 Profile->LockedBuffer = NULL;
191 Profile->Affinity = Affinity;
192 Profile->Process = pProcess;
193
194 /* Insert into the Object Tree */
195 Status = ObInsertObject ((PVOID)Profile,
196 NULL,
197 PROFILE_CONTROL,
198 0,
199 NULL,
200 &hProfile);
201 ObDereferenceObject(Profile);
202
203 /* Check for Success */
204 if (!NT_SUCCESS(Status)) {
205
206 /* Dereference Process on failure */
207 if (pProcess) ObDereferenceObject(pProcess);
208 return Status;
209 }
210
211 /* Copy the created handle back to the caller*/
212 _SEH_TRY {
213
214 *ProfileHandle = hProfile;
215
216 } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) {
217
218 Status = _SEH_GetExceptionCode();
219 } _SEH_END;
220
221 /* Return Status */
222 return Status;
223 }
224
225 NTSTATUS
226 STDCALL
227 NtQueryPerformanceCounter(OUT PLARGE_INTEGER PerformanceCounter,
228 OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL)
229 {
230 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
231 LARGE_INTEGER PerfFrequency;
232 NTSTATUS Status = STATUS_SUCCESS;
233
234 /* Check the Parameters for validity */
235 if(PreviousMode != KernelMode) {
236
237 _SEH_TRY {
238
239 ProbeForWriteLargeInteger(PerformanceCounter);
240
241 ProbeForWriteLargeInteger(PerformanceFrequency);
242 } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) {
243
244 Status = _SEH_GetExceptionCode();
245 } _SEH_END;
246
247 if(!NT_SUCCESS(Status)) return Status;
248 }
249
250 _SEH_TRY {
251
252 /* Query the Kernel */
253 *PerformanceCounter = KeQueryPerformanceCounter(&PerfFrequency);
254
255 /* Return Frequency if requested */
256 if(PerformanceFrequency) {
257
258 *PerformanceFrequency = PerfFrequency;
259 }
260 } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) {
261
262 Status = _SEH_GetExceptionCode();
263
264 } _SEH_END;
265
266 return Status;
267 }
268
269 NTSTATUS
270 STDCALL
271 NtStartProfile(IN HANDLE ProfileHandle)
272 {
273 PEPROFILE Profile;
274 PKPROFILE KeProfile;
275 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
276 PVOID TempLockedBuffer;
277 NTSTATUS Status;
278
279 PAGED_CODE();
280
281 /* Get the Object */
282 Status = ObReferenceObjectByHandle(ProfileHandle,
283 PROFILE_CONTROL,
284 ExProfileObjectType,
285 PreviousMode,
286 (PVOID*)&Profile,
287 NULL);
288 if (!NT_SUCCESS(Status)) return(Status);
289
290 /* To avoid a Race, wait on the Mutex */
291 KeWaitForSingleObject(&ExpProfileMutex,
292 Executive,
293 KernelMode,
294 FALSE,
295 NULL);
296
297 /* The Profile can still be enabled though, so handle that */
298 if (Profile->LockedBuffer) {
299
300 /* Release our lock, dereference and return */
301 KeReleaseMutex(&ExpProfileMutex, FALSE);
302 ObDereferenceObject(Profile);
303 return STATUS_PROFILING_NOT_STOPPED;
304 }
305
306 /* Allocate a Kernel Profile Object. */
307 KeProfile = ExAllocatePoolWithTag(NonPagedPool,
308 sizeof(EPROFILE),
309 TAG('P', 'r', 'o', 'f'));
310
311 /* Allocate the Mdl Structure */
312 Profile->Mdl = MmCreateMdl(NULL, Profile->Buffer, Profile->BufferSize);
313
314 /* Probe and Lock for Write Access */
315 MmProbeAndLockPages(Profile->Mdl, PreviousMode, IoWriteAccess);
316
317 /* Map the pages */
318 TempLockedBuffer = MmMapLockedPages(Profile->Mdl, KernelMode);
319
320 /* Initialize the Kernel Profile Object */
321 Profile->KeProfile = KeProfile;
322 KeInitializeProfile(KeProfile,
323 (PKPROCESS)Profile->Process,
324 Profile->ImageBase,
325 Profile->ImageSize,
326 Profile->BucketSize,
327 Profile->ProfileSource,
328 Profile->Affinity);
329
330 /* Start the Profiling */
331 KeStartProfile(KeProfile, TempLockedBuffer);
332
333 /* Now it's safe to save this */
334 Profile->LockedBuffer = TempLockedBuffer;
335
336 /* Release mutex, dereference and return */
337 KeReleaseMutex(&ExpProfileMutex, FALSE);
338 ObDereferenceObject(Profile);
339 return STATUS_SUCCESS;
340 }
341
342 NTSTATUS
343 STDCALL
344 NtStopProfile(IN HANDLE ProfileHandle)
345 {
346 PEPROFILE Profile;
347 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
348 NTSTATUS Status;
349
350 PAGED_CODE();
351
352 /* Get the Object */
353 Status = ObReferenceObjectByHandle(ProfileHandle,
354 PROFILE_CONTROL,
355 ExProfileObjectType,
356 PreviousMode,
357 (PVOID*)&Profile,
358 NULL);
359 if (!NT_SUCCESS(Status)) return(Status);
360
361 /* Get the Mutex */
362 KeWaitForSingleObject(&ExpProfileMutex,
363 Executive,
364 KernelMode,
365 FALSE,
366 NULL);
367
368 /* Make sure the Profile Object is really Started */
369 if (!Profile->LockedBuffer) {
370
371 Status = STATUS_PROFILING_NOT_STARTED;
372 goto Exit;
373 }
374
375 /* Stop the Profile */
376 KeStopProfile(Profile->KeProfile);
377
378 /* Unlock the Buffer */
379 MmUnmapLockedPages(Profile->LockedBuffer, Profile->Mdl);
380 MmUnlockPages(Profile->Mdl);
381 ExFreePool(Profile->KeProfile);
382
383 /* Clear the Locked Buffer pointer, meaning the Object is Stopped */
384 Profile->LockedBuffer = NULL;
385
386 Exit:
387 /* Release Mutex, Dereference and Return */
388 KeReleaseMutex(&ExpProfileMutex, FALSE);
389 ObDereferenceObject(Profile);
390 return Status;
391 }
392
393 NTSTATUS
394 STDCALL
395 NtQueryIntervalProfile(IN KPROFILE_SOURCE ProfileSource,
396 OUT PULONG Interval)
397 {
398 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
399 ULONG ReturnInterval;
400 NTSTATUS Status = STATUS_SUCCESS;
401
402 PAGED_CODE();
403
404 /* Check the Parameters for validity */
405 if(PreviousMode != KernelMode) {
406
407 _SEH_TRY {
408
409 ProbeForWriteUlong(Interval);
410
411 } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) {
412
413 Status = _SEH_GetExceptionCode();
414 } _SEH_END;
415
416 if(!NT_SUCCESS(Status)) return Status;
417 }
418
419 /* Query the Interval */
420 ReturnInterval = KeQueryIntervalProfile(ProfileSource);
421
422 /* Return the data */
423 _SEH_TRY {
424
425 *Interval = ReturnInterval;
426
427 } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) {
428
429 Status = _SEH_GetExceptionCode();
430
431 } _SEH_END;
432
433 /* Return Success */
434 return STATUS_SUCCESS;
435 }
436
437 NTSTATUS
438 STDCALL
439 NtSetIntervalProfile(IN ULONG Interval,
440 IN KPROFILE_SOURCE Source)
441 {
442 /* Let the Kernel do the job */
443 KeSetIntervalProfile(Interval, Source);
444
445 /* Nothing can go wrong */
446 return STATUS_SUCCESS;
447 }