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