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 = (PEPROFILE)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 } 59 60 /* Check if a Process is associated and reference it */ 61 if (Profile->Process) ObDereferenceObject(Profile->Process); 62 } 63 64 BOOLEAN 65 INIT_FUNCTION 66 NTAPI 67 ExpInitializeProfileImplementation(VOID) 68 { 69 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer; 70 UNICODE_STRING Name; 71 NTSTATUS Status; 72 DPRINT("Creating Profile Object Type\n"); 73 74 /* Initialize the Mutex to lock the States */ 75 KeInitializeMutex(&ExpProfileMutex, 64); 76 77 /* Create the Event Pair Object Type */ 78 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer)); 79 RtlInitUnicodeString(&Name, L"Profile"); 80 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer); 81 ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(KPROFILE); 82 ObjectTypeInitializer.GenericMapping = ExpProfileMapping; 83 ObjectTypeInitializer.PoolType = NonPagedPool; 84 ObjectTypeInitializer.DeleteProcedure = ExpDeleteProfile; 85 ObjectTypeInitializer.ValidAccessMask = PROFILE_ALL_ACCESS; 86 ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK; 87 Status = ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &ExProfileObjectType); 88 if (!NT_SUCCESS(Status)) return FALSE; 89 return TRUE; 90 } 91 92 NTSTATUS 93 NTAPI 94 NtCreateProfile(OUT PHANDLE ProfileHandle, 95 IN HANDLE Process OPTIONAL, 96 IN PVOID RangeBase, 97 IN ULONG RangeSize, 98 IN ULONG BucketSize, 99 IN PVOID Buffer, 100 IN ULONG BufferSize, 101 IN KPROFILE_SOURCE ProfileSource, 102 IN KAFFINITY Affinity) 103 { 104 HANDLE hProfile; 105 PEPROFILE Profile; 106 PEPROCESS pProcess; 107 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 108 OBJECT_ATTRIBUTES ObjectAttributes; 109 NTSTATUS Status; 110 ULONG Log2 = 0; 111 ULONG_PTR Segment = 0; 112 PAGED_CODE(); 113 114 /* Easy way out */ 115 if(!BufferSize) return STATUS_INVALID_PARAMETER_7; 116 117 /* Check if this is a low-memory profile */ 118 if ((!BucketSize) && (RangeBase < (PVOID)(0x10000))) 119 { 120 /* Validate size */ 121 if (BufferSize < sizeof(ULONG)) return STATUS_INVALID_PARAMETER_7; 122 123 /* This will become a segmented profile object */ 124 Segment = (ULONG_PTR)RangeBase; 125 RangeBase = 0; 126 127 /* Recalculate the bucket size */ 128 BucketSize = RangeSize / (BufferSize / sizeof(ULONG)); 129 130 /* Convert it to log2 */ 131 BucketSize--; 132 while (BucketSize >>= 1) Log2++; 133 BucketSize += Log2 + 1; 134 } 135 136 /* Validate bucket size */ 137 if ((BucketSize > 31) || (BucketSize < 2)) 138 { 139 DPRINT1("Bucket size invalid\n"); 140 return STATUS_INVALID_PARAMETER; 141 } 142 143 /* Make sure that the buckets can map the range */ 144 if ((RangeSize >> (BucketSize - 2)) > BufferSize) 145 { 146 DPRINT1("Bucket size too small\n"); 147 return STATUS_BUFFER_TOO_SMALL; 148 } 149 150 /* Make sure that the range isn't too gigantic */ 151 if (((ULONG_PTR)RangeBase + RangeSize) < RangeSize) 152 { 153 DPRINT1("Range too big\n"); 154 return STATUS_BUFFER_OVERFLOW; 155 } 156 157 /* Check if we were called from user-mode */ 158 if(PreviousMode != KernelMode) 159 { 160 /* Entry SEH */ 161 _SEH2_TRY 162 { 163 /* Make sure that the handle pointer is valid */ 164 ProbeForWriteHandle(ProfileHandle); 165 166 /* Check if the buffer is valid */ 167 ProbeForWrite(Buffer, 168 BufferSize, 169 sizeof(ULONG)); 170 } 171 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 172 { 173 /* Return the exception code */ 174 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 175 } 176 _SEH2_END; 177 } 178 179 /* Check if a process was specified */ 180 if (Process) 181 { 182 /* Reference it */ 183 Status = ObReferenceObjectByHandle(Process, 184 PROCESS_QUERY_INFORMATION, 185 PsProcessType, 186 PreviousMode, 187 (PVOID*)&pProcess, 188 NULL); 189 if (!NT_SUCCESS(Status)) return(Status); 190 } 191 else 192 { 193 /* Segmented profile objects cannot be used system-wide */ 194 if (Segment) return STATUS_INVALID_PARAMETER; 195 196 /* No process was specified, which means a System-Wide Profile */ 197 pProcess = NULL; 198 199 /* For this, we need to check the Privilege */ 200 if(!SeSinglePrivilegeCheck(SeSystemProfilePrivilege, PreviousMode)) 201 { 202 DPRINT1("NtCreateProfile: Caller requires the SeSystemProfilePrivilege privilege!\n"); 203 return STATUS_PRIVILEGE_NOT_HELD; 204 } 205 } 206 207 /* Create the object */ 208 InitializeObjectAttributes(&ObjectAttributes, 209 NULL, 210 0, 211 NULL, 212 NULL); 213 Status = ObCreateObject(KernelMode, 214 ExProfileObjectType, 215 &ObjectAttributes, 216 PreviousMode, 217 NULL, 218 sizeof(EPROFILE), 219 0, 220 sizeof(EPROFILE) + sizeof(KPROFILE), 221 (PVOID*)&Profile); 222 if (!NT_SUCCESS(Status)) 223 { 224 /* Dereference the process object if it was specified */ 225 if (pProcess) ObDereferenceObject(pProcess); 226 227 /* Return Status */ 228 return Status; 229 } 230 231 /* Initialize it */ 232 Profile->RangeBase = RangeBase; 233 Profile->RangeSize = RangeSize; 234 Profile->Buffer = Buffer; 235 Profile->BufferSize = BufferSize; 236 Profile->BucketSize = BucketSize; 237 Profile->LockedBufferAddress = NULL; 238 Profile->Segment = Segment; 239 Profile->ProfileSource = ProfileSource; 240 Profile->Affinity = Affinity; 241 Profile->Process = pProcess; 242 243 /* Insert into the Object Tree */ 244 Status = ObInsertObject ((PVOID)Profile, 245 NULL, 246 PROFILE_CONTROL, 247 0, 248 NULL, 249 &hProfile); 250 ObDereferenceObject(Profile); 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(EPROFILE), 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 (PKPROCESS)Profile->Process, 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 ExFreePoolWithTag(Profile->ProfileObject, TAG_PROFILE); 457 458 /* Clear the Locked Buffer pointer, meaning the Object is Stopped */ 459 Profile->LockedBufferAddress = NULL; 460 461 Exit: 462 /* Release Mutex, Dereference and Return */ 463 KeReleaseMutex(&ExpProfileMutex, FALSE); 464 ObDereferenceObject(Profile); 465 return Status; 466 } 467 468 NTSTATUS 469 NTAPI 470 NtQueryIntervalProfile(IN KPROFILE_SOURCE ProfileSource, 471 OUT PULONG Interval) 472 { 473 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 474 ULONG ReturnInterval; 475 NTSTATUS Status = STATUS_SUCCESS; 476 PAGED_CODE(); 477 478 /* Check if we were called from user-mode */ 479 if (PreviousMode != KernelMode) 480 { 481 /* Enter SEH Block */ 482 _SEH2_TRY 483 { 484 /* Validate interval */ 485 ProbeForWriteUlong(Interval); 486 } 487 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 488 { 489 /* Return the exception code */ 490 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 491 } 492 _SEH2_END; 493 } 494 495 /* Query the Interval */ 496 ReturnInterval = (ULONG)KeQueryIntervalProfile(ProfileSource); 497 498 /* Enter SEH block for return */ 499 _SEH2_TRY 500 { 501 /* Return the data */ 502 *Interval = ReturnInterval; 503 } 504 _SEH2_EXCEPT(ExSystemExceptionFilter()) 505 { 506 /* Get the exception code */ 507 Status = _SEH2_GetExceptionCode(); 508 } 509 _SEH2_END; 510 511 /* Return Success */ 512 return Status; 513 } 514 515 NTSTATUS 516 NTAPI 517 NtSetIntervalProfile(IN ULONG Interval, 518 IN KPROFILE_SOURCE Source) 519 { 520 /* Let the Kernel do the job */ 521 KeSetIntervalProfile(Interval, Source); 522 523 /* Nothing can go wrong */ 524 return STATUS_SUCCESS; 525 } 526