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