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 /* GLOBALS *******************************************************************/ 17 18 POBJECT_TYPE ExProfileObjectType = NULL; 19 KMUTEX ExpProfileMutex; 20 21 GENERIC_MAPPING ExpProfileMapping = 22 { 23 STANDARD_RIGHTS_READ | PROFILE_CONTROL, 24 STANDARD_RIGHTS_WRITE | PROFILE_CONTROL, 25 STANDARD_RIGHTS_EXECUTE | PROFILE_CONTROL, 26 PROFILE_ALL_ACCESS 27 }; 28 29 /* FUNCTIONS *****************************************************************/ 30 31 VOID 32 NTAPI 33 ExpDeleteProfile(PVOID ObjectBody) 34 { 35 PEPROFILE Profile; 36 ULONG State; 37 38 /* Typecast the Object */ 39 Profile = ObjectBody; 40 41 /* Check if there if the Profile was started */ 42 if (Profile->LockedBufferAddress) 43 { 44 /* Stop the Profile */ 45 State = KeStopProfile(Profile->ProfileObject); 46 ASSERT(State != FALSE); 47 48 /* Unmap the Locked Buffer */ 49 MmUnmapLockedPages(Profile->LockedBufferAddress, Profile->Mdl); 50 MmUnlockPages(Profile->Mdl); 51 IoFreeMdl(Profile->Mdl); 52 ExFreePoolWithTag(Profile->ProfileObject, TAG_PROFILE); 53 } 54 55 /* Check if a Process is associated and reference it */ 56 if (Profile->Process) ObDereferenceObject(Profile->Process); 57 } 58 59 CODE_SEG("INIT") 60 BOOLEAN 61 NTAPI 62 ExpInitializeProfileImplementation(VOID) 63 { 64 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer; 65 UNICODE_STRING Name; 66 NTSTATUS Status; 67 DPRINT("Creating Profile Object Type\n"); 68 69 /* Initialize the Mutex to lock the States */ 70 KeInitializeMutex(&ExpProfileMutex, 64); 71 72 /* Create the Event Pair Object Type */ 73 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer)); 74 RtlInitUnicodeString(&Name, L"Profile"); 75 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer); 76 ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(KPROFILE); 77 ObjectTypeInitializer.GenericMapping = ExpProfileMapping; 78 ObjectTypeInitializer.PoolType = NonPagedPool; 79 ObjectTypeInitializer.DeleteProcedure = ExpDeleteProfile; 80 ObjectTypeInitializer.ValidAccessMask = PROFILE_ALL_ACCESS; 81 ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK; 82 Status = ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &ExProfileObjectType); 83 if (!NT_SUCCESS(Status)) return FALSE; 84 return TRUE; 85 } 86 87 NTSTATUS 88 NTAPI 89 NtCreateProfile(OUT PHANDLE ProfileHandle, 90 IN HANDLE Process OPTIONAL, 91 IN PVOID RangeBase, 92 IN SIZE_T RangeSize, 93 IN ULONG BucketSize, 94 IN PVOID Buffer, 95 IN ULONG BufferSize, 96 IN KPROFILE_SOURCE ProfileSource, 97 IN KAFFINITY Affinity) 98 { 99 HANDLE hProfile; 100 PEPROFILE Profile; 101 PEPROCESS pProcess; 102 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 103 OBJECT_ATTRIBUTES ObjectAttributes; 104 NTSTATUS Status; 105 ULONG Log2 = 0; 106 ULONG_PTR Segment = 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_PTR)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 _SEH2_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 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 167 { 168 /* Return the exception code */ 169 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 170 } 171 _SEH2_END; 172 } 173 174 /* Check if a process was specified */ 175 if (Process) 176 { 177 /* Reference it */ 178 Status = ObReferenceObjectByHandle(Process, 179 PROCESS_QUERY_INFORMATION, 180 PsProcessType, 181 PreviousMode, 182 (PVOID*)&pProcess, 183 NULL); 184 if (!NT_SUCCESS(Status)) return(Status); 185 } 186 else 187 { 188 /* Segmented profile objects cannot be used system-wide */ 189 if (Segment) return STATUS_INVALID_PARAMETER; 190 191 /* No process was specified, which means a System-Wide Profile */ 192 pProcess = NULL; 193 194 /* For this, we need to check the Privilege */ 195 if(!SeSinglePrivilegeCheck(SeSystemProfilePrivilege, PreviousMode)) 196 { 197 DPRINT1("NtCreateProfile: Caller requires the SeSystemProfilePrivilege privilege!\n"); 198 return STATUS_PRIVILEGE_NOT_HELD; 199 } 200 } 201 202 /* Create the object */ 203 InitializeObjectAttributes(&ObjectAttributes, 204 NULL, 205 0, 206 NULL, 207 NULL); 208 Status = ObCreateObject(KernelMode, 209 ExProfileObjectType, 210 &ObjectAttributes, 211 PreviousMode, 212 NULL, 213 sizeof(EPROFILE), 214 0, 215 sizeof(EPROFILE) + sizeof(KPROFILE), 216 (PVOID*)&Profile); 217 if (!NT_SUCCESS(Status)) 218 { 219 /* Dereference the process object if it was specified */ 220 if (pProcess) ObDereferenceObject(pProcess); 221 222 /* Return Status */ 223 return Status; 224 } 225 226 /* Initialize it */ 227 Profile->RangeBase = RangeBase; 228 Profile->RangeSize = RangeSize; 229 Profile->Buffer = Buffer; 230 Profile->BufferSize = BufferSize; 231 Profile->BucketSize = BucketSize; 232 Profile->LockedBufferAddress = NULL; 233 Profile->Segment = Segment; 234 Profile->ProfileSource = ProfileSource; 235 Profile->Affinity = Affinity; 236 Profile->Process = pProcess; 237 238 /* Insert into the Object Tree */ 239 Status = ObInsertObject ((PVOID)Profile, 240 NULL, 241 PROFILE_CONTROL, 242 0, 243 NULL, 244 &hProfile); 245 246 /* Check for Success */ 247 if (!NT_SUCCESS(Status)) 248 { 249 /* Dereference Process on failure */ 250 if (pProcess) ObDereferenceObject(pProcess); 251 return Status; 252 } 253 254 /* Enter SEH */ 255 _SEH2_TRY 256 { 257 /* Copy the created handle back to the caller*/ 258 *ProfileHandle = hProfile; 259 } 260 _SEH2_EXCEPT(ExSystemExceptionFilter()) 261 { 262 Status = _SEH2_GetExceptionCode(); 263 } 264 _SEH2_END; 265 266 /* Return Status */ 267 return Status; 268 } 269 270 NTSTATUS 271 NTAPI 272 NtQueryPerformanceCounter(OUT PLARGE_INTEGER PerformanceCounter, 273 OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL) 274 { 275 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 276 LARGE_INTEGER PerfFrequency; 277 NTSTATUS Status = STATUS_SUCCESS; 278 279 /* Check if we were called from user-mode */ 280 if (PreviousMode != KernelMode) 281 { 282 /* Entry SEH Block */ 283 _SEH2_TRY 284 { 285 /* Make sure the counter and frequency are valid */ 286 ProbeForWriteLargeInteger(PerformanceCounter); 287 if (PerformanceFrequency) 288 { 289 ProbeForWriteLargeInteger(PerformanceFrequency); 290 } 291 } 292 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 293 { 294 /* Return the exception code */ 295 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 296 } 297 _SEH2_END; 298 } 299 300 /* Enter a new SEH Block */ 301 _SEH2_TRY 302 { 303 /* Query the Kernel */ 304 *PerformanceCounter = KeQueryPerformanceCounter(&PerfFrequency); 305 306 /* Return Frequency if requested */ 307 if (PerformanceFrequency) *PerformanceFrequency = PerfFrequency; 308 } 309 _SEH2_EXCEPT(ExSystemExceptionFilter()) 310 { 311 /* Get the exception code */ 312 Status = _SEH2_GetExceptionCode(); 313 } 314 _SEH2_END; 315 316 /* Return status to caller */ 317 return Status; 318 } 319 320 NTSTATUS 321 NTAPI 322 NtStartProfile(IN HANDLE ProfileHandle) 323 { 324 PEPROFILE Profile; 325 PKPROFILE ProfileObject; 326 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 327 PVOID TempLockedBufferAddress; 328 NTSTATUS Status; 329 PAGED_CODE(); 330 331 /* Get the Object */ 332 Status = ObReferenceObjectByHandle(ProfileHandle, 333 PROFILE_CONTROL, 334 ExProfileObjectType, 335 PreviousMode, 336 (PVOID*)&Profile, 337 NULL); 338 if (!NT_SUCCESS(Status)) return(Status); 339 340 /* To avoid a Race, wait on the Mutex */ 341 KeWaitForSingleObject(&ExpProfileMutex, 342 Executive, 343 KernelMode, 344 FALSE, 345 NULL); 346 347 /* The Profile can still be enabled though, so handle that */ 348 if (Profile->LockedBufferAddress) 349 { 350 /* Release our lock, dereference and return */ 351 KeReleaseMutex(&ExpProfileMutex, FALSE); 352 ObDereferenceObject(Profile); 353 return STATUS_PROFILING_NOT_STOPPED; 354 } 355 356 /* Allocate a Kernel Profile Object. */ 357 ProfileObject = ExAllocatePoolWithTag(NonPagedPool, 358 sizeof(*ProfileObject), 359 TAG_PROFILE); 360 if (!ProfileObject) 361 { 362 /* Out of memory, fail */ 363 KeReleaseMutex(&ExpProfileMutex, FALSE); 364 ObDereferenceObject(Profile); 365 return STATUS_INSUFFICIENT_RESOURCES; 366 } 367 368 /* Allocate the Mdl Structure */ 369 Profile->Mdl = IoAllocateMdl(Profile->Buffer, Profile->BufferSize, FALSE, FALSE, NULL); 370 371 /* Protect this in SEH as we might raise an exception */ 372 _SEH2_TRY 373 { 374 /* Probe and Lock for Write Access */ 375 MmProbeAndLockPages(Profile->Mdl, PreviousMode, IoWriteAccess); 376 } 377 _SEH2_EXCEPT(ExSystemExceptionFilter()) 378 { 379 /* Release our lock, free the buffer, dereference and return */ 380 KeReleaseMutex(&ExpProfileMutex, FALSE); 381 ObDereferenceObject(Profile); 382 ExFreePoolWithTag(ProfileObject, TAG_PROFILE); 383 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 384 } 385 _SEH2_END; 386 387 /* Map the pages */ 388 TempLockedBufferAddress = MmMapLockedPages(Profile->Mdl, KernelMode); 389 390 /* Initialize the Kernel Profile Object */ 391 Profile->ProfileObject = ProfileObject; 392 KeInitializeProfile(ProfileObject, 393 &Profile->Process->Pcb, 394 Profile->RangeBase, 395 Profile->RangeSize, 396 Profile->BucketSize, 397 Profile->ProfileSource, 398 Profile->Affinity); 399 400 /* Start the Profiling */ 401 KeStartProfile(ProfileObject, TempLockedBufferAddress); 402 403 /* Now it's safe to save this */ 404 Profile->LockedBufferAddress = TempLockedBufferAddress; 405 406 /* Release mutex, dereference and return */ 407 KeReleaseMutex(&ExpProfileMutex, FALSE); 408 ObDereferenceObject(Profile); 409 return STATUS_SUCCESS; 410 } 411 412 NTSTATUS 413 NTAPI 414 NtStopProfile(IN HANDLE ProfileHandle) 415 { 416 PEPROFILE Profile; 417 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 418 NTSTATUS Status; 419 PAGED_CODE(); 420 421 /* Get the Object */ 422 Status = ObReferenceObjectByHandle(ProfileHandle, 423 PROFILE_CONTROL, 424 ExProfileObjectType, 425 PreviousMode, 426 (PVOID*)&Profile, 427 NULL); 428 if (!NT_SUCCESS(Status)) return(Status); 429 430 /* Get the Mutex */ 431 KeWaitForSingleObject(&ExpProfileMutex, 432 Executive, 433 KernelMode, 434 FALSE, 435 NULL); 436 437 /* Make sure the Profile Object is really Started */ 438 if (!Profile->LockedBufferAddress) 439 { 440 Status = STATUS_PROFILING_NOT_STARTED; 441 goto Exit; 442 } 443 444 /* Stop the Profile */ 445 KeStopProfile(Profile->ProfileObject); 446 447 /* Unlock the Buffer */ 448 MmUnmapLockedPages(Profile->LockedBufferAddress, Profile->Mdl); 449 MmUnlockPages(Profile->Mdl); 450 IoFreeMdl(Profile->Mdl); 451 ExFreePoolWithTag(Profile->ProfileObject, TAG_PROFILE); 452 453 /* Clear the Locked Buffer pointer, meaning the Object is Stopped */ 454 Profile->LockedBufferAddress = NULL; 455 456 Exit: 457 /* Release Mutex, Dereference and Return */ 458 KeReleaseMutex(&ExpProfileMutex, FALSE); 459 ObDereferenceObject(Profile); 460 return Status; 461 } 462 463 NTSTATUS 464 NTAPI 465 NtQueryIntervalProfile(IN KPROFILE_SOURCE ProfileSource, 466 OUT PULONG Interval) 467 { 468 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 469 ULONG ReturnInterval; 470 NTSTATUS Status = STATUS_SUCCESS; 471 PAGED_CODE(); 472 473 /* Check if we were called from user-mode */ 474 if (PreviousMode != KernelMode) 475 { 476 /* Enter SEH Block */ 477 _SEH2_TRY 478 { 479 /* Validate interval */ 480 ProbeForWriteUlong(Interval); 481 } 482 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 483 { 484 /* Return the exception code */ 485 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 486 } 487 _SEH2_END; 488 } 489 490 /* Query the Interval */ 491 ReturnInterval = (ULONG)KeQueryIntervalProfile(ProfileSource); 492 493 /* Enter SEH block for return */ 494 _SEH2_TRY 495 { 496 /* Return the data */ 497 *Interval = ReturnInterval; 498 } 499 _SEH2_EXCEPT(ExSystemExceptionFilter()) 500 { 501 /* Get the exception code */ 502 Status = _SEH2_GetExceptionCode(); 503 } 504 _SEH2_END; 505 506 /* Return Success */ 507 return Status; 508 } 509 510 NTSTATUS 511 NTAPI 512 NtSetIntervalProfile(IN ULONG Interval, 513 IN KPROFILE_SOURCE Source) 514 { 515 /* Let the Kernel do the job */ 516 KeSetIntervalProfile(Interval, Source); 517 518 /* Nothing can go wrong */ 519 return STATUS_SUCCESS; 520 } 521