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