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