1 /* 2 * COPYRIGHT: GPL - See COPYING in the top level directory 3 * PROJECT: ReactOS Kernel 4 * FILE: ntoskrnl/ke/profobj.c 5 * PURPOSE: Kernel Profiling 6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) 7 */ 8 9 /* INCLUDES *****************************************************************/ 10 11 #include <ntoskrnl.h> 12 #define NDEBUG 13 #include <debug.h> 14 15 /* GLOBALS *******************************************************************/ 16 17 KIRQL KiProfileIrql = PROFILE_LEVEL; 18 LIST_ENTRY KiProfileListHead; 19 LIST_ENTRY KiProfileSourceListHead; 20 KSPIN_LOCK KiProfileLock; 21 ULONG KiProfileTimeInterval = 78125; /* Default resolution 7.8ms (sysinternals) */ 22 ULONG KiProfileAlignmentFixupInterval; 23 24 /* FUNCTIONS *****************************************************************/ 25 26 VOID 27 NTAPI 28 KeInitializeProfile(PKPROFILE Profile, 29 PKPROCESS Process, 30 PVOID ImageBase, 31 SIZE_T ImageSize, 32 ULONG BucketSize, 33 KPROFILE_SOURCE ProfileSource, 34 KAFFINITY Affinity) 35 { 36 /* Initialize the Header */ 37 Profile->Type = ProfileObject; 38 Profile->Size = sizeof(KPROFILE); 39 40 /* Copy all the settings we were given */ 41 Profile->Process = Process; 42 Profile->RangeBase = ImageBase; 43 Profile->BucketShift = BucketSize - 2; /* See ntinternals.net -- Alex */ 44 Profile->RangeLimit = (PVOID)((ULONG_PTR)ImageBase + ImageSize); 45 Profile->Started = FALSE; 46 Profile->Source = ProfileSource; 47 Profile->Affinity = Affinity; 48 } 49 50 BOOLEAN 51 NTAPI 52 KeStartProfile(IN PKPROFILE Profile, 53 IN PVOID Buffer) 54 { 55 KIRQL OldIrql; 56 PKPROFILE_SOURCE_OBJECT SourceBuffer; 57 PKPROFILE_SOURCE_OBJECT CurrentSource; 58 BOOLEAN FreeBuffer = TRUE, SourceFound = FALSE, StartedProfile; 59 PKPROCESS ProfileProcess; 60 PLIST_ENTRY NextEntry; 61 62 /* Allocate a buffer first, before we raise IRQL */ 63 SourceBuffer = ExAllocatePoolWithTag(NonPagedPool, 64 sizeof(KPROFILE_SOURCE_OBJECT), 65 'forP'); 66 if (!SourceBuffer) return FALSE; 67 RtlZeroMemory(SourceBuffer, sizeof(KPROFILE_SOURCE_OBJECT)); 68 69 /* Raise to profile IRQL and acquire the profile lock */ 70 KeRaiseIrql(KiProfileIrql, &OldIrql); 71 KeAcquireSpinLockAtDpcLevel(&KiProfileLock); 72 73 /* Make sure it's not running */ 74 if (!Profile->Started) 75 { 76 /* Set it as Started */ 77 Profile->Buffer = Buffer; 78 Profile->Started = TRUE; 79 StartedProfile = TRUE; 80 81 /* Get the process, if any */ 82 ProfileProcess = Profile->Process; 83 84 /* Check where we should insert it */ 85 if (ProfileProcess) 86 { 87 /* Insert it into the Process List */ 88 InsertTailList(&ProfileProcess->ProfileListHead, &Profile->ProfileListEntry); 89 } 90 else 91 { 92 /* Insert it into the Global List */ 93 InsertTailList(&KiProfileListHead, &Profile->ProfileListEntry); 94 } 95 96 /* Start looping */ 97 for (NextEntry = KiProfileSourceListHead.Flink; 98 NextEntry != &KiProfileSourceListHead; 99 NextEntry = NextEntry->Flink) 100 { 101 /* Get the entry */ 102 CurrentSource = CONTAINING_RECORD(NextEntry, 103 KPROFILE_SOURCE_OBJECT, 104 ListEntry); 105 106 /* Check if it's the same as the one being requested now */ 107 if (CurrentSource->Source == Profile->Source) 108 { 109 /* It is, break out */ 110 SourceFound = TRUE; 111 break; 112 } 113 } 114 115 /* See if the loop found something */ 116 if (!SourceFound) 117 { 118 /* Nothing found, use our allocated buffer */ 119 CurrentSource = SourceBuffer; 120 121 /* Set up the Source Object */ 122 CurrentSource->Source = Profile->Source; 123 InsertHeadList(&KiProfileSourceListHead, &CurrentSource->ListEntry); 124 125 /* Don't free the pool later on */ 126 FreeBuffer = FALSE; 127 } 128 } 129 else 130 { 131 /* Already running so nothing to start */ 132 StartedProfile = FALSE; 133 } 134 135 /* Release the profile lock */ 136 KeReleaseSpinLockFromDpcLevel(&KiProfileLock); 137 138 /* Tell HAL to start the profile interrupt */ 139 HalStartProfileInterrupt(Profile->Source); 140 141 /* Lower back to original IRQL */ 142 KeLowerIrql(OldIrql); 143 144 /* Free the pool */ 145 if (FreeBuffer) ExFreePoolWithTag(SourceBuffer, 'forP'); 146 147 /* Return whether we could start the profile */ 148 return StartedProfile; 149 } 150 151 BOOLEAN 152 NTAPI 153 KeStopProfile(IN PKPROFILE Profile) 154 { 155 KIRQL OldIrql; 156 PKPROFILE_SOURCE_OBJECT CurrentSource = NULL; 157 PLIST_ENTRY NextEntry; 158 BOOLEAN SourceFound = FALSE, StoppedProfile; 159 160 /* Raise to profile IRQL and acquire the profile lock */ 161 KeRaiseIrql(KiProfileIrql, &OldIrql); 162 KeAcquireSpinLockAtDpcLevel(&KiProfileLock); 163 164 /* Make sure it's running */ 165 if (Profile->Started) 166 { 167 /* Remove it from the list and disable */ 168 RemoveEntryList(&Profile->ProfileListEntry); 169 Profile->Started = FALSE; 170 StoppedProfile = TRUE; 171 172 /* Start looping */ 173 for (NextEntry = KiProfileSourceListHead.Flink; 174 NextEntry != &KiProfileSourceListHead; 175 NextEntry = NextEntry->Flink) 176 { 177 /* Get the entry */ 178 CurrentSource = CONTAINING_RECORD(NextEntry, 179 KPROFILE_SOURCE_OBJECT, 180 ListEntry); 181 182 /* Check if this is the Source Object */ 183 if (CurrentSource->Source == Profile->Source) 184 { 185 /* Remember we found one */ 186 SourceFound = TRUE; 187 188 /* Remove it and break out */ 189 RemoveEntryList(&CurrentSource->ListEntry); 190 break; 191 } 192 } 193 194 } 195 else 196 { 197 /* It wasn't! */ 198 StoppedProfile = FALSE; 199 } 200 201 /* Release the profile lock */ 202 KeReleaseSpinLockFromDpcLevel(&KiProfileLock); 203 204 /* Stop the profile interrupt */ 205 HalStopProfileInterrupt(Profile->Source); 206 207 /* Lower back to original IRQL */ 208 KeLowerIrql(OldIrql); 209 210 /* Free the Source Object */ 211 if (SourceFound) ExFreePool(CurrentSource); 212 213 /* Return whether we could stop the profile */ 214 return StoppedProfile; 215 } 216 217 ULONG 218 NTAPI 219 KeQueryIntervalProfile(IN KPROFILE_SOURCE ProfileSource) 220 { 221 HAL_PROFILE_SOURCE_INFORMATION ProfileSourceInformation; 222 ULONG ReturnLength, Interval; 223 NTSTATUS Status; 224 225 /* Check what profile this is */ 226 if (ProfileSource == ProfileTime) 227 { 228 /* Return the time interval */ 229 Interval = KiProfileTimeInterval; 230 } 231 else if (ProfileSource == ProfileAlignmentFixup) 232 { 233 /* Return the alignment interval */ 234 Interval = KiProfileAlignmentFixupInterval; 235 } 236 else 237 { 238 /* Request it from HAL */ 239 ProfileSourceInformation.Source = ProfileSource; 240 Status = HalQuerySystemInformation(HalProfileSourceInformation, 241 sizeof(HAL_PROFILE_SOURCE_INFORMATION), 242 &ProfileSourceInformation, 243 &ReturnLength); 244 245 /* Check if HAL handled it and supports this profile */ 246 if (NT_SUCCESS(Status) && (ProfileSourceInformation.Supported)) 247 { 248 /* Get the interval */ 249 Interval = ProfileSourceInformation.Interval; 250 } 251 else 252 { 253 /* Unsupported or invalid source, fail */ 254 Interval = 0; 255 } 256 } 257 258 /* Return the interval we got */ 259 return Interval; 260 } 261 262 VOID 263 NTAPI 264 KeSetIntervalProfile(IN ULONG Interval, 265 IN KPROFILE_SOURCE ProfileSource) 266 { 267 HAL_PROFILE_SOURCE_INTERVAL ProfileSourceInterval; 268 269 /* Check what profile this is */ 270 if (ProfileSource == ProfileTime) 271 { 272 /* Set the interval through HAL */ 273 KiProfileTimeInterval = (ULONG)HalSetProfileInterval(Interval); 274 } 275 else if (ProfileSource == ProfileAlignmentFixup) 276 { 277 /* Set the alignment interval */ 278 KiProfileAlignmentFixupInterval = Interval; 279 } 280 else 281 { 282 /* HAL handles any other interval */ 283 ProfileSourceInterval.Source = ProfileSource; 284 ProfileSourceInterval.Interval = Interval; 285 HalSetSystemInformation(HalProfileSourceInterval, 286 sizeof(HAL_PROFILE_SOURCE_INTERVAL), 287 &ProfileSourceInterval); 288 } 289 } 290 291 /* 292 * @implemented 293 */ 294 VOID 295 NTAPI 296 KeProfileInterrupt(IN PKTRAP_FRAME TrapFrame) 297 { 298 /* Called from HAL for Timer Profiling */ 299 KeProfileInterruptWithSource(TrapFrame, ProfileTime); 300 } 301 302 VOID 303 NTAPI 304 KiParseProfileList(IN PKTRAP_FRAME TrapFrame, 305 IN KPROFILE_SOURCE Source, 306 IN PLIST_ENTRY ListHead) 307 { 308 PULONG BucketValue; 309 PKPROFILE Profile; 310 PLIST_ENTRY NextEntry; 311 ULONG_PTR ProgramCounter; 312 313 /* Get the Program Counter */ 314 ProgramCounter = KeGetTrapFramePc(TrapFrame); 315 316 /* Loop the List */ 317 for (NextEntry = ListHead->Flink; 318 NextEntry != ListHead; 319 NextEntry = NextEntry->Flink) 320 { 321 /* Get the entry */ 322 Profile = CONTAINING_RECORD(NextEntry, KPROFILE, ProfileListEntry); 323 324 /* Check if the source is good, and if it's within the range */ 325 if ((Profile->Source != Source) || 326 (ProgramCounter < (ULONG_PTR)Profile->RangeBase) || 327 (ProgramCounter > (ULONG_PTR)Profile->RangeLimit)) 328 { 329 continue; 330 } 331 332 /* Get the Pointer to the Bucket Value representing this Program Counter */ 333 BucketValue = (PULONG)((ULONG_PTR)Profile->Buffer + 334 (((ProgramCounter - (ULONG_PTR)Profile->RangeBase) 335 >> Profile->BucketShift) &~ 0x3)); 336 337 /* Increment the value */ 338 (*BucketValue)++; 339 } 340 } 341 342 /* 343 * @implemented 344 * 345 * Remarks: 346 * Called from HAL, this function looks up the process 347 * entries, finds the proper source object, verifies the 348 * ranges with the trapframe data, and inserts the information 349 * from the trap frame into the buffer, while using buckets and 350 * shifting like we specified. -- Alex 351 */ 352 VOID 353 NTAPI 354 KeProfileInterruptWithSource(IN PKTRAP_FRAME TrapFrame, 355 IN KPROFILE_SOURCE Source) 356 { 357 PKPROCESS Process = KeGetCurrentThread()->ApcState.Process; 358 359 /* We have to parse 2 lists. Per-Process and System-Wide */ 360 KiParseProfileList(TrapFrame, Source, &Process->ProfileListHead); 361 KiParseProfileList(TrapFrame, Source, &KiProfileListHead); 362 } 363 364 /* 365 * @implemented 366 */ 367 VOID 368 NTAPI 369 KeSetProfileIrql(IN KIRQL ProfileIrql) 370 { 371 /* Set the IRQL at which Profiling will run */ 372 KiProfileIrql = ProfileIrql; 373 } 374