HyperDbg Debugger
Loading...
Searching...
No Matches
Pt.c File Reference

Processor Trace (PT) tracing implementation for HyperTrace module. More...

#include "pch.h"

Functions

INT32 PtEngineSizeToTopaEncoding (UINT64 SizeInBytes)
 Convert a buffer size in bytes to the ToPA Size field encoding. Valid sizes are 4KB * 2^N for N = 0..15.
INT32 PtEngineQueryCapabilities (PT_CAPABILITIES *OutCaps)
 Probe Intel PT capabilities via CPUID leaf 7 / leaf 0x14.
VOID PtEngineInitDefaultConfig (PT_TRACE_CONFIG *Config)
 Initialize a PT_TRACE_CONFIG with sensible defaults. Trace user + kernel, branch + TSC packets, 2 MB output buffer.
INT32 PtEngineAllocateBuffers (PT_PER_CPU *Cpu, const PT_TRACE_CONFIG *Config)
 Allocate the ToPA table, output buffer, and overflow zone for one per-CPU PT context, then build the ToPA entries.
VOID PtEngineFreeBuffers (PT_PER_CPU *Cpu)
 Free all PT buffers belonging to one per-CPU context. Must not be called while State == PT_STATE_TRACING.
INT32 PtEngineStart (PT_PER_CPU *Cpu)
 Start tracing on the CURRENT CPU using the passed PT_PER_CPU. Programs all PT MSRs and sets TraceEn=1.
UINT64 PtEngineStop (PT_PER_CPU *Cpu, PT_OUTPUT_BUFFER *Out)
 Stop tracing on the CURRENT CPU. Reads final output position, copies trace data if requested, resets PT MSRs.
INT32 PtEnginePause (PT_PER_CPU *Cpu)
 Pause tracing on the CURRENT CPU. Preserves buffer state.
INT32 PtEngineResume (PT_PER_CPU *Cpu)
 Resume tracing on the CURRENT CPU after pause.
BOOLEAN PtEngineIsPtPmi ()
 Check whether the latest PMI was raised by Intel PT (IA32_PERF_GLOBAL_STATUS bit 55).
UINT64 PtEngineHandlePmi (PT_PER_CPU *Cpu, PT_OUTPUT_BUFFER *Out)
 Handle a ToPA PMI on the CURRENT CPU. Caller is responsible for having already disabled tracing (e.g. via VMCS clear of RTIT_CTL).
BOOLEAN PtCheck ()
 Check whether Intel PT is supported on the current CPU. Mirrors LbrCheck — must be called once before any Pt* operation.
BOOLEAN PtAllocateAllCpuBuffers ()
 Allocate ToPA / output / overflow buffers for every active CPU.
VOID PtFreeAllCpuBuffers ()
 Free ToPA / output / overflow buffers for every active CPU.
INT32 PtMmapAllCpuBuffersToUser (PT_USER_BUFFER_DESC *OutDescs, UINT32 MaxDescs, UINT32 *OutNumCpus)
 Map every per-CPU PT main output buffer and 4 KB overflow page into the current user process as a single virtually contiguous region per CPU, and fill OutDescs[i] with the base UserVa and the total Size (main + overflow) for that CPU.
VOID PtUnmapAllCpuBuffersFromUser ()
 Release every user mapping created by PtMmapAllCpuBuffersToUser. Called by PtFreeAllCpuBuffers (i.e. on PT disable / flush) so user VAs stop being usable before the backing memory is freed. Also used as a rollback path on partial mmap failure.
BOOLEAN PtStart ()
 Start PT tracing on the CURRENT CPU. Buffers must already be allocated by PtAllocateAllCpuBuffers (called at PASSIVE_LEVEL).
VOID PtStop ()
 Stop PT tracing on the CURRENT CPU. Trace data accumulated in the per-CPU output buffer is left in place; PtSize / PtDump can read it later.
VOID PtPause ()
 Pause PT tracing on the CURRENT CPU. Buffer state is preserved so a subsequent PtResume picks up where this left off.
VOID PtResume ()
 Resume PT tracing on the CURRENT CPU after a prior PtPause.
UINT64 PtSize ()
 Snapshot the current PT output position on the CURRENT CPU without disturbing tracing state. The returned value is the number of bytes of valid trace data sitting in this CPU's main + overflow buffer, i.e. the offset a decoder should stop at when reading from the user mapping.
VOID PtDump ()
 Print PT trace summary for the CURRENT CPU.
VOID PtFilter (const PT_FILTER_OPTIONS *FilterOptions)
 LBR-style filter wrapper: refresh tracing on the CURRENT CPU with a fresh PT_FILTER_OPTIONS.
VOID PtFlush ()
 Flush PT trace state on the CURRENT CPU — disables tracing and clears the bytes-captured counter so the next PtStart begins from a fresh baseline. Buffer freeing happens at PASSIVE_LEVEL via PtFreeAllCpuBuffers; this is safe to call from a DPC.

Detailed Description

Processor Trace (PT) tracing implementation for HyperTrace module.

Author
Masoud Rahimi Jafari (Masoo.nosp@m.drah.nosp@m.imy13.nosp@m.79@g.nosp@m.mail..nosp@m.com)

Programs Intel PT MSRs and manages per-CPU ToPA / output buffers. The engine half (PtEngine*) deals with a single PT_PER_CPU at a time and is OS-agnostic. The HyperDbg wrappers (PtCheck, PtStart, PtStop, PtPause, PtResume, PtSize, PtDump, PtFlush) operate on the global per-CPU state list (g_PtStateList) and mirror the LBR API surface.

Version
0.19
Date
2026-04-29

Function Documentation

◆ PtAllocateAllCpuBuffers()

BOOLEAN PtAllocateAllCpuBuffers ( )

Allocate ToPA / output / overflow buffers for every active CPU.

   Must be called at IRQL == PASSIVE_LEVEL (before broadcasting the
   per-core enable DPC), because MmAllocateContiguousMemorySpecifyCache
   is paged.

   Idempotent: cores that already have buffers (State != DISABLED)
   are skipped.
Returns
BOOLEAN TRUE if every core ended up with a usable buffer set.
1052{
1053 UINT32 ProcessorsCount;
1054 UINT32 i;
1055
1056 if (g_PtStateList == NULL)
1057 return FALSE;
1058
1059 ProcessorsCount = KeQueryActiveProcessorCount(0);
1060
1061 for (i = 0; i < ProcessorsCount; i++)
1062 {
1063 PT_PER_CPU * Cpu = &g_PtStateList[i];
1064 PT_TRACE_CONFIG Cfg = Cpu->Config;
1065
1066 if (Cpu->State != PT_STATE_DISABLED)
1067 continue;
1068
1069 if (Cfg.BufferSize == 0)
1071
1072 if (PtEngineAllocateBuffers(Cpu, &Cfg) != 0)
1073 {
1074 LogInfo("PT: buffer allocation failed on core %u\n", i);
1075 return FALSE;
1076 }
1077 }
1078
1079 return TRUE;
1080}
INT32 PtEngineAllocateBuffers(PT_PER_CPU *Cpu, const PT_TRACE_CONFIG *Config)
Allocate the ToPA table, output buffer, and overflow zone for one per-CPU PT context,...
Definition Pt.c:380
VOID PtEngineInitDefaultConfig(PT_TRACE_CONFIG *Config)
Initialize a PT_TRACE_CONFIG with sensible defaults. Trace user + kernel, branch + TSC packets,...
Definition Pt.c:342
#define TRUE
Definition BasicTypes.h:114
#define FALSE
Definition BasicTypes.h:113
unsigned int UINT32
Definition BasicTypes.h:54
struct _PT_TRACE_CONFIG PT_TRACE_CONFIG
Intel PT trace configuration — what the user specifies.
struct _PT_PER_CPU PT_PER_CPU
Per-CPU Intel PT state — one of these per logical processor.
@ PT_STATE_DISABLED
Definition PtDefinitions.h:218
#define LogInfo(format,...)
Define log variables.
Definition HyperDbgHyperLogIntrinsics.h:71
PT_PER_CPU * g_PtStateList
Dynamically allocated array of per-CPU Intel PT state. Sized to KeQueryActiveProcessorCount(0) at hyp...
Definition GlobalVariables.h:84
PT_TRACE_CONFIG Config
Definition PtDefinitions.h:317
PT_STATE State
Definition PtDefinitions.h:318
UINT64 BufferSize
Definition PtDefinitions.h:270

◆ PtCheck()

BOOLEAN PtCheck ( )

Check whether Intel PT is supported on the current CPU. Mirrors LbrCheck — must be called once before any Pt* operation.

If running under HyperDbg's hypervisor we also verify that the platform's IA32_VMX_MISC MSR advertises PT-in-VMX support, since without that bit set we may not be able to keep PT alive across VM transitions in a future VMCS-controlled implementation. This is a soft check — the current direct-MSR path still works because PT MSRs aren't trapped — but it surfaces the situation in the log.

Returns
BOOLEAN TRUE if Intel PT is available.
1013{
1014 PT_CAPABILITIES Caps = {0};
1015
1016 if (PtEngineQueryCapabilities(&Caps) != 0)
1017 return FALSE;
1018
1019 //
1020 // We require ToPA output for our buffer scheme
1021 //
1022 if (!Caps.TopaOutput)
1023 {
1024 LogInfo("PT: CPU does not support ToPA output; PT cannot be used here.\n");
1025 return FALSE;
1026 }
1027
1029 {
1030 LogInfo("PT: IA32_VMX_MISC[14] is clear — Intel PT in VMX is not "
1031 "advertised on this CPU. Direct MSR programming still works "
1032 "from VMX non-root because PT MSRs are not trapped.\n");
1033 }
1034
1035 return TRUE;
1036}
INT32 PtEngineQueryCapabilities(PT_CAPABILITIES *OutCaps)
Probe Intel PT capabilities via CPUID leaf 7 / leaf 0x14.
Definition Pt.c:266
struct _PT_CAPABILITIES PT_CAPABILITIES
Discovered Intel PT capabilities (populated from CPUID leaf 0x14).
BOOLEAN g_RunningOnHypervisorEnvironment
The flag indicating whether the initialization is being done for hypervisor environment or not.
Definition GlobalVariables.h:35
UINT32 TopaOutput
Definition PtDefinitions.h:199
UINT32 VmxSupport
Definition PtDefinitions.h:198

◆ PtDump()

VOID PtDump ( )

Print PT trace summary for the CURRENT CPU.

   PT packets are a compressed binary stream that requires a decoder
   (libipt or similar) to be human-readable, so this only emits the
   per-CPU statistics; bulk packet data is preserved in the output
   buffer for offline retrieval.
1362{
1363 UINT32 CurrentCore;
1364 PT_PER_CPU * Cpu;
1365
1366 if (g_PtStateList == NULL)
1367 return;
1368
1369 CurrentCore = KeGetCurrentProcessorNumberEx(NULL);
1370 Cpu = &g_PtStateList[CurrentCore];
1371
1372 Log("PT trace summary for core %u\n", CurrentCore);
1373 Log(" State : %d\n", Cpu->State);
1374 Log(" TotalBytesCaptured : 0x%llx\n", Cpu->TotalBytesCaptured);
1375 Log(" OutputBufferSize : 0x%llx\n", Cpu->Buffer.OutputSize);
1376 Log(" TopaPhysical : 0x%llx\n", Cpu->Buffer.TopaPhysical);
1377 Log(" OutputPhysical : 0x%llx\n", Cpu->Buffer.OutputPhysical);
1378 Log(" TraceUser/Kernel : %u / %u\n", Cpu->Config.TraceUser, Cpu->Config.TraceKernel);
1379 Log(" TargetCr3 : 0x%llx\n", Cpu->Config.TargetCr3);
1380}
#define Log(format,...)
Log without any prefix.
Definition HyperDbgHyperLogIntrinsics.h:129
UINT64 OutputPhysical
Definition PtDefinitions.h:299
UINT64 TopaPhysical
Definition PtDefinitions.h:293
UINT64 OutputSize
Definition PtDefinitions.h:300
UINT64 TotalBytesCaptured
Definition PtDefinitions.h:319
PT_BUFFER Buffer
Definition PtDefinitions.h:315
BOOLEAN TraceUser
Definition PtDefinitions.h:245
BOOLEAN TraceKernel
Definition PtDefinitions.h:246
UINT64 TargetCr3
Definition PtDefinitions.h:247

◆ PtEngineAllocateBuffers()

INT32 PtEngineAllocateBuffers ( PT_PER_CPU * Cpu,
const PT_TRACE_CONFIG * Config )

Allocate the ToPA table, output buffer, and overflow zone for one per-CPU PT context, then build the ToPA entries.

ToPA layout: [0] main output buffer (Config->BufferSize), INT=1 [1] overflow page (4 KB), INT=0 [2] END, wraps back to ToPA table (circular)

Returns
INT32 0 on success, -1 on allocation failure, -2 on bad config.
381{
382 INT32 SizeEncoding;
383 UINT64 BufSize;
384 PT_TOPA_ENTRY * Topa;
385
386 if (Cpu == NULL || Config == NULL)
387 return -2;
388
389 BufSize = Config->BufferSize;
390 if (BufSize == 0)
391 BufSize = PT_DEFAULT_BUFFER_SIZE;
392
393 //
394 // Validate buffer size is a valid ToPA encoding
395 //
396 SizeEncoding = PtEngineSizeToTopaEncoding(BufSize);
397 if (SizeEncoding < 0)
398 return -2;
399
400 //
401 // 1. ToPA table — one 4KB page, must be 4KB-aligned
402 //
403 Cpu->Buffer.TopaVa = (PT_TOPA_ENTRY *)PtAllocateContiguousAligned(PT_PAGE_SIZE, PT_PAGE_SIZE);
404 if (Cpu->Buffer.TopaVa == NULL)
405 return -1;
406 Cpu->Buffer.TopaPhysical = PtVaToPa(Cpu->Buffer.TopaVa);
407
408 //
409 // 2. Main output buffer — must be aligned to its own size
410 //
411 Cpu->Buffer.OutputVa = PtAllocateContiguousAligned(BufSize, BufSize);
412 if (Cpu->Buffer.OutputVa == NULL)
413 {
414 PtFreeContiguous(Cpu->Buffer.TopaVa);
415 Cpu->Buffer.TopaVa = NULL;
416 return -1;
417 }
418 Cpu->Buffer.OutputPhysical = PtVaToPa(Cpu->Buffer.OutputVa);
419 Cpu->Buffer.OutputSize = BufSize;
420
421 //
422 // 3. Overflow landing zone — 4KB
423 //
424 Cpu->Buffer.OverflowVa = PtAllocateContiguousAligned(PT_OVERFLOW_SIZE, PT_PAGE_SIZE);
425 if (Cpu->Buffer.OverflowVa == NULL)
426 {
427 PtFreeContiguous(Cpu->Buffer.OutputVa);
428 Cpu->Buffer.OutputVa = NULL;
429 PtFreeContiguous(Cpu->Buffer.TopaVa);
430 Cpu->Buffer.TopaVa = NULL;
431 return -1;
432 }
433 Cpu->Buffer.OverflowPhysical = PtVaToPa(Cpu->Buffer.OverflowVa);
434
435 //
436 // 4. Build the ToPA table — 3 entries
437 //
438 Topa = Cpu->Buffer.TopaVa;
439
440 //
441 // Entry 0: main data buffer, INT=1 (trigger PMI when full)
442 //
443 Topa[0].Value = 0;
444 Topa[0].BaseAddr = Cpu->Buffer.OutputPhysical >> 12;
445 Topa[0].Size = (UINT64)SizeEncoding;
446 Topa[0].Int = 1;
447 Topa[0].Stop = 0;
448
449 //
450 // Entry 1: overflow landing zone (4 KB), no interrupt
451 //
452 Topa[1].Value = 0;
453 Topa[1].BaseAddr = Cpu->Buffer.OverflowPhysical >> 12;
454 Topa[1].Size = PT_TOPA_SIZE_4K;
455 Topa[1].Int = 0;
456 Topa[1].Stop = 0;
457
458 //
459 // Entry 2: END — circular, points back to this ToPA table
460 //
461 Topa[2].Value = 0;
462 Topa[2].End = 1;
463 Topa[2].BaseAddr = Cpu->Buffer.TopaPhysical >> 12;
464
465 //
466 // Copy config and initialize state
467 //
468 Cpu->Config = *Config;
469 Cpu->Config.BufferSize = BufSize;
470 Cpu->SavedCtl.Value = 0;
471 Cpu->State = PT_STATE_READY;
472 Cpu->TotalBytesCaptured = 0;
473
474 return 0;
475}
INT32 PtEngineSizeToTopaEncoding(UINT64 SizeInBytes)
Convert a buffer size in bytes to the ToPA Size field encoding. Valid sizes are 4KB * 2^N for N = 0....
Definition Pt.c:232
signed int INT32
Definition BasicTypes.h:50
#define PT_TOPA_SIZE_4K
Definition PtDefinitions.h:65
union _PT_TOPA_ENTRY PT_TOPA_ENTRY
ToPA Table Entry.
#define PT_OVERFLOW_SIZE
Definition PtDefinitions.h:52
@ PT_STATE_READY
Definition PtDefinitions.h:219
#define PT_PAGE_SIZE
Definition PtDefinitions.h:50
#define PT_DEFAULT_BUFFER_SIZE
Definition PtDefinitions.h:51
NULL()
Definition test-case-generator.py:530
PT_TOPA_ENTRY * TopaVa
Definition PtDefinitions.h:292
PVOID OutputVa
Definition PtDefinitions.h:298
PVOID OverflowVa
Definition PtDefinitions.h:305
UINT64 OverflowPhysical
Definition PtDefinitions.h:306
PT_RTIT_CTL_REGISTER SavedCtl
Definition PtDefinitions.h:316
UINT64 Value
Definition PtDefinitions.h:120
UINT64 Stop
Definition PtDefinitions.h:173
UINT64 End
Definition PtDefinitions.h:169
UINT64 Size
Definition PtDefinitions.h:175
UINT64 BaseAddr
Definition PtDefinitions.h:177
UINT64 Value
Definition PtDefinitions.h:180
UINT64 Int
Definition PtDefinitions.h:171

◆ PtEngineFreeBuffers()

VOID PtEngineFreeBuffers ( PT_PER_CPU * Cpu)

Free all PT buffers belonging to one per-CPU context. Must not be called while State == PT_STATE_TRACING.

483{
484 if (Cpu == NULL)
485 return;
486
487 //
488 // Refuse to free while tracing — caller must stop first
489 //
490 if (Cpu->State == PT_STATE_TRACING)
491 return;
492
493 if (Cpu->Buffer.OverflowVa)
494 {
495 PtFreeContiguous(Cpu->Buffer.OverflowVa);
496 Cpu->Buffer.OverflowVa = NULL;
497 Cpu->Buffer.OverflowPhysical = 0;
498 }
499 if (Cpu->Buffer.OutputVa)
500 {
501 PtFreeContiguous(Cpu->Buffer.OutputVa);
502 Cpu->Buffer.OutputVa = NULL;
503 Cpu->Buffer.OutputPhysical = 0;
504 Cpu->Buffer.OutputSize = 0;
505 }
506 if (Cpu->Buffer.TopaVa)
507 {
508 PtFreeContiguous(Cpu->Buffer.TopaVa);
509 Cpu->Buffer.TopaVa = NULL;
510 Cpu->Buffer.TopaPhysical = 0;
511 }
512
514 Cpu->TotalBytesCaptured = 0;
515 Cpu->SavedCtl.Value = 0;
516}
@ PT_STATE_TRACING
Definition PtDefinitions.h:220

◆ PtEngineHandlePmi()

UINT64 PtEngineHandlePmi ( PT_PER_CPU * Cpu,
PT_OUTPUT_BUFFER * Out )

Handle a ToPA PMI on the CURRENT CPU. Caller is responsible for having already disabled tracing (e.g. via VMCS clear of RTIT_CTL).

Returns
UINT64 number of bytes captured in this PMI event.
943{
944 UINT64 BytesThisEvent;
945
946 if (Cpu == NULL)
947 return 0;
948
949 //
950 // 1. Read how many bytes were written and copy them out
951 //
952 BytesThisEvent = PtEngineReadBytesWritten(&Cpu->Buffer, Out);
953 Cpu->TotalBytesCaptured += BytesThisEvent;
954
955 //
956 // 2. Reset output position to start of buffer BEFORE acknowledging the
957 // PMI. PT keeps tracing during the handler (TraceEn stays 1) and the
958 // hardware is free to raise another ToPA PMI as soon as PendTopaPmi /
959 // PERF_GLOBAL_STATUS.TopaPmi are cleared. If MASK_PTRS still points
960 // deep into the overflow page (or past wrap) at that moment, a
961 // back-to-back PMI would observe stale state.
962 //
963 {
965 ResetMask.Value = 0;
966 ResetMask.LowerMask = 0x7F;
967 ResetMask.MaskOrTableOffset = 0;
968 ResetMask.OutputOffset = 0;
969 __writemsr(MSR_IA32_RTIT_OUTPUT_MASK_PTRS, ResetMask.Value);
970 }
971
972 //
973 // 3. Clear PT pending PMI bits in IA32_RTIT_STATUS
974 //
975 {
977 Status.Value = __readmsr(MSR_IA32_RTIT_STATUS);
978 Status.PendTopaPmi = 0;
979 Status.PendPsbPmi = 0;
980 Status.Error = 0;
981 Status.Stopped = 0;
982 __writemsr(MSR_IA32_RTIT_STATUS, Status.Value);
983 }
984
985 //
986 // 4. Acknowledge the global PMI bit
987 //
989
990 return BytesThisEvent;
991}
#define MSR_IA32_RTIT_OUTPUT_MASK_PTRS
Definition PtDefinitions.h:21
#define PERF_GLOBAL_STATUS_TOPA_PMI
Definition PtDefinitions.h:44
union _PT_OUTPUT_MASK_PTRS_REGISTER PT_OUTPUT_MASK_PTRS_REGISTER
IA32_RTIT_OUTPUT_MASK_PTRS — Output position tracker.
union _PT_RTIT_STATUS_REGISTER PT_RTIT_STATUS_REGISTER
IA32_RTIT_STATUS — PT status / error register.
#define MSR_IA32_PERF_GLOBAL_OVF_CTRL
Definition PtDefinitions.h:39
#define MSR_IA32_RTIT_STATUS
Definition PtDefinitions.h:23
UINT64 LowerMask
Definition PtDefinitions.h:154
UINT64 OutputOffset
Definition PtDefinitions.h:156
UINT64 Value
Definition PtDefinitions.h:158
UINT64 MaskOrTableOffset
Definition PtDefinitions.h:155
UINT64 Value
Definition PtDefinitions.h:143
UINT64 Error
Definition PtDefinitions.h:135
UINT64 PendTopaPmi
Definition PtDefinitions.h:137
UINT64 Stopped
Definition PtDefinitions.h:136
UINT64 PendPsbPmi
Definition PtDefinitions.h:138

◆ PtEngineInitDefaultConfig()

VOID PtEngineInitDefaultConfig ( PT_TRACE_CONFIG * Config)

Initialize a PT_TRACE_CONFIG with sensible defaults. Trace user + kernel, branch + TSC packets, 2 MB output buffer.

343{
344 UINT32 i;
345
346 Config->TraceUser = TRUE;
347 Config->TraceKernel = TRUE;
348 Config->TargetCr3 = 0;
349 Config->NumAddrRanges = 0;
350 Config->EnableBranch = TRUE;
351 Config->EnableTsc = FALSE;
352 Config->EnableMtc = FALSE;
353 Config->EnableCyc = FALSE;
354 Config->EnableRetCompression = FALSE;
355 Config->MtcFreq = 0;
356 Config->CycThresh = 0;
357 Config->PsbFreq = 0;
359
360 for (i = 0; i < PT_MAX_ADDR_RANGES; i++)
361 {
362 Config->AddrRanges[i].Start = 0;
363 Config->AddrRanges[i].End = 0;
364 Config->AddrRanges[i].IsStopRange = FALSE;
365 }
366}
#define PT_MAX_ADDR_RANGES
Definition PtDefinitions.h:53
UINT64 End
Definition PtDefinitions.h:232
UINT64 Start
Definition PtDefinitions.h:231
BOOLEAN IsStopRange
Definition PtDefinitions.h:233
BOOLEAN EnableCyc
Definition PtDefinitions.h:261
BOOLEAN EnableRetCompression
Definition PtDefinitions.h:262
PT_ADDR_RANGE AddrRanges[PT_MAX_ADDR_RANGES]
Definition PtDefinitions.h:253
UINT8 PsbFreq
Definition PtDefinitions.h:265
BOOLEAN EnableMtc
Definition PtDefinitions.h:260
UINT32 NumAddrRanges
Definition PtDefinitions.h:252
UINT8 CycThresh
Definition PtDefinitions.h:264
BOOLEAN EnableBranch
Definition PtDefinitions.h:258
BOOLEAN EnableTsc
Definition PtDefinitions.h:259
UINT8 MtcFreq
Definition PtDefinitions.h:263

◆ PtEngineIsPtPmi()

BOOLEAN PtEngineIsPtPmi ( )

Check whether the latest PMI was raised by Intel PT (IA32_PERF_GLOBAL_STATUS bit 55).

930{
931 UINT64 GlobalStatus = __readmsr(MSR_IA32_PERF_GLOBAL_STATUS);
932 return (GlobalStatus & PERF_GLOBAL_STATUS_TOPA_PMI) ? TRUE : FALSE;
933}
#define MSR_IA32_PERF_GLOBAL_STATUS
Definition PtDefinitions.h:38

◆ PtEnginePause()

INT32 PtEnginePause ( PT_PER_CPU * Cpu)

Pause tracing on the CURRENT CPU. Preserves buffer state.

891{
893
894 if (Cpu == NULL || Cpu->State != PT_STATE_TRACING)
895 return -1;
896
897 Ctl.Value = __readmsr(MSR_IA32_RTIT_CTL);
898 Ctl.TraceEn = 0;
899 __writemsr(MSR_IA32_RTIT_CTL, Ctl.Value);
900
901 Cpu->State = PT_STATE_PAUSED;
902 return 0;
903}
@ PT_STATE_PAUSED
Definition PtDefinitions.h:221
#define MSR_IA32_RTIT_CTL
Definition PtDefinitions.h:22
union _PT_RTIT_CTL_REGISTER PT_RTIT_CTL_REGISTER
IA32_RTIT_CTL — PT master control register.
UINT64 TraceEn
Definition PtDefinitions.h:94

◆ PtEngineQueryCapabilities()

INT32 PtEngineQueryCapabilities ( PT_CAPABILITIES * OutCaps)

Probe Intel PT capabilities via CPUID leaf 7 / leaf 0x14.

Parameters
OutCapsOptional. If non-NULL, populated with detailed capabilities.
Returns
INT32 0 on success (PT supported), -1 if PT is not supported.
267{
268 int Info[4] = {0};
269 int MaxSubleaf = 0;
270 UINT64 VmxMisc;
271
272 //
273 // Check PT support: CPUID.(EAX=07H,ECX=0):EBX[25]
274 //
275 __cpuidex(Info, 0x07, 0);
276 if ((Info[1] & (1 << 25)) == 0)
277 return -1;
278
279 if (OutCaps == NULL)
280 return 0;
281
282 RtlZeroMemory(OutCaps, sizeof(*OutCaps));
283
284 //
285 // Leaf 0x14, sub-leaf 0: capability flags
286 //
287 Info[0] = Info[1] = Info[2] = Info[3] = 0;
288 __cpuidex(Info, 0x14, 0);
289
290 MaxSubleaf = Info[0]; /* EAX: max valid sub-leaf */
291
292 //
293 // EBX capabilities
294 //
295 OutCaps->Cr3Filtering = (Info[1] >> 0) & 1;
296 OutCaps->PsbCycConfigurable = (Info[1] >> 1) & 1;
297 OutCaps->IpFiltering = (Info[1] >> 2) & 1;
298 OutCaps->MtcSupport = (Info[1] >> 3) & 1;
299 OutCaps->PtwriteSupport = (Info[1] >> 4) & 1;
300 OutCaps->PowerEventTrace = (Info[1] >> 5) & 1;
301
302 //
303 // ECX capabilities
304 //
305 OutCaps->TopaOutput = (Info[2] >> 0) & 1;
306 OutCaps->TopaMultiEntry = (Info[2] >> 1) & 1;
307 OutCaps->SingleRangeOutput = (Info[2] >> 2) & 1;
308 OutCaps->TransportOutput = (Info[2] >> 3) & 1;
309 OutCaps->IpPayloadsAreLip = (Info[2] >> 31) & 1;
310
311 //
312 // Sub-leaf 1: detailed numerics
313 //
314 if (MaxSubleaf >= 1)
315 {
316 Info[0] = Info[1] = Info[2] = Info[3] = 0;
317 __cpuidex(Info, 0x14, 1);
318
319 OutCaps->NumAddrRanges = (UINT32)(Info[0] & 0x7);
320 OutCaps->MtcPeriodBitmap = (UINT16)((Info[0] >> 16) & 0xFFFF);
321 OutCaps->CycThresholdBitmap = (UINT16)(Info[1] & 0xFFFF);
322 OutCaps->PsbFreqBitmap = (UINT16)((Info[1] >> 16) & 0xFFFF);
323 }
324
325 //
326 // VMX support: IA32_VMX_MISC[14] indicates Intel PT may be used in VMX
327 // operation. Reading IA32_VMX_MISC is unconditional on a VMX-capable
328 // CPU; if the CPU isn't VMX-capable the bit is meaningless and the
329 // value falls through as 0.
330 //
331 VmxMisc = __readmsr(0x00000485 /* IA32_VMX_MISC */);
332 OutCaps->VmxSupport = (VmxMisc >> 14) & 1;
333
334 return 0;
335}
unsigned short UINT16
Definition BasicTypes.h:53
UINT32 IpFiltering
Definition PtDefinitions.h:194
UINT32 TopaMultiEntry
Definition PtDefinitions.h:200
UINT32 Cr3Filtering
Definition PtDefinitions.h:192
UINT32 TransportOutput
Definition PtDefinitions.h:202
UINT32 IpPayloadsAreLip
Definition PtDefinitions.h:203
UINT32 NumAddrRanges
Definition PtDefinitions.h:206
UINT32 PtwriteSupport
Definition PtDefinitions.h:196
UINT16 PsbFreqBitmap
Definition PtDefinitions.h:209
UINT32 PsbCycConfigurable
Definition PtDefinitions.h:193
UINT16 MtcPeriodBitmap
Definition PtDefinitions.h:207
UINT32 SingleRangeOutput
Definition PtDefinitions.h:201
UINT16 CycThresholdBitmap
Definition PtDefinitions.h:208
UINT32 PowerEventTrace
Definition PtDefinitions.h:197
UINT32 MtcSupport
Definition PtDefinitions.h:195

◆ PtEngineResume()

INT32 PtEngineResume ( PT_PER_CPU * Cpu)

Resume tracing on the CURRENT CPU after pause.

910{
912
913 if (Cpu == NULL || Cpu->State != PT_STATE_PAUSED)
914 return -1;
915
916 Ctl = Cpu->SavedCtl;
917 Ctl.TraceEn = 1;
918 __writemsr(MSR_IA32_RTIT_CTL, Ctl.Value);
919
920 Cpu->State = PT_STATE_TRACING;
921 return 0;
922}

◆ PtEngineSizeToTopaEncoding()

INT32 PtEngineSizeToTopaEncoding ( UINT64 SizeInBytes)

Convert a buffer size in bytes to the ToPA Size field encoding. Valid sizes are 4KB * 2^N for N = 0..15.

Parameters
SizeInBytes
Returns
INT32 Encoding 0..15, or -1 if the size is not supported.
233{
234 UINT64 Pages;
235 INT32 N;
236
237 if (SizeInBytes < PT_PAGE_SIZE)
238 return -1;
239
240 Pages = SizeInBytes / PT_PAGE_SIZE;
241
242 //
243 // Must be an exact power-of-two number of pages
244 //
245 if ((Pages & (Pages - 1)) != 0)
246 return -1;
247
248 N = 0;
249 while ((Pages >> N) != 1)
250 N++;
251
252 if (N > 15)
253 return -1;
254
255 return N;
256}

◆ PtEngineStart()

INT32 PtEngineStart ( PT_PER_CPU * Cpu)

Start tracing on the CURRENT CPU using the passed PT_PER_CPU. Programs all PT MSRs and sets TraceEn=1.

Must be called from the target CPU (DPC or VMX root).

Returns
INT32 0 on success, -1 on error.
611{
613 PT_CAPABILITIES Caps;
614 UINT32 i;
615 BOOLEAN IsOnVmxRootMode = FALSE;
616
617 if (Cpu == NULL)
618 return -1;
619 if (Cpu->State != PT_STATE_READY && Cpu->State != PT_STATE_STOPPED)
620 return -1;
621 if (Cpu->Buffer.TopaVa == NULL || Cpu->Buffer.OutputVa == NULL)
622 return -1;
623
624 if (PtEngineQueryCapabilities(&Caps) != 0)
625 return -1;
626
627 //
628 // Detect VMX-root context if running under HyperDbg's hypervisor.
629 //
630 // Intel PT MSRs (IA32_RTIT_*) are NOT in the default MSR bitmap, so a
631 // direct WRMSR to them in either VMX root or VMX non-root passes
632 // through to hardware without trapping. This means the same MSR
633 // sequence below works correctly from a DPC running in the guest as
634 // well as from a VMX-root packet handler. We still read the mode for
635 // logging and for any future hypervisor-only code (e.g. setting the
636 // "Clear IA32_RTIT_CTL on VM-exit" VMCS control or saving guest-side
637 // RTIT state across VM transitions).
638 //
640 {
641 IsOnVmxRootMode = g_Callbacks.VmFuncVmxGetCurrentExecutionMode();
642 LogInfo("PT: PtEngineStart on core %u (vmx-root=%u)\n",
643 KeGetCurrentProcessorNumberEx(NULL),
644 (UINT32)IsOnVmxRootMode);
645 }
646
647 //
648 // Don't request more IP ranges or features than the CPU supports
649 //
650 if (Cpu->Config.NumAddrRanges > Caps.NumAddrRanges)
651 return -1;
652 if (Cpu->Config.NumAddrRanges > 0 && !Caps.IpFiltering)
653 return -1;
654 if (Cpu->Config.TargetCr3 != 0 && !Caps.Cr3Filtering)
655 return -1;
656
657 //
658 // Step 1: Ensure tracing is off and clear status
659 //
660 {
662 Cur.Value = __readmsr(MSR_IA32_RTIT_CTL);
663 if (Cur.TraceEn)
664 {
665 Cur.TraceEn = 0;
666 __writemsr(MSR_IA32_RTIT_CTL, Cur.Value);
667 }
668 }
669
670 __writemsr(MSR_IA32_RTIT_STATUS, 0);
671
672 //
673 // Step 2: Set output base and reset output position
674 //
676 {
678 InitMask.Value = 0;
679 InitMask.LowerMask = 0x7F;
680 InitMask.MaskOrTableOffset = 0;
681 InitMask.OutputOffset = 0;
682 __writemsr(MSR_IA32_RTIT_OUTPUT_MASK_PTRS, InitMask.Value);
683 }
684
685 //
686 // Step 3: Set CR3 filter
687 //
688 __writemsr(MSR_IA32_RTIT_CR3_MATCH, Cpu->Config.TargetCr3);
689
690 //
691 // Step 4: Set IP address ranges. IMPORTANT: only touch ADDRn MSRs the
692 // CPU actually supports; writing to non-existent ADDRn MSRs causes #GP.
693 //
694 for (i = 0; i < Caps.NumAddrRanges; i++)
695 {
696 UINT32 MsrA = MSR_IA32_RTIT_ADDR0_A + (i * 2);
697 UINT32 MsrB = MSR_IA32_RTIT_ADDR0_B + (i * 2);
698
699 if (i < Cpu->Config.NumAddrRanges)
700 {
701 __writemsr(MsrA, Cpu->Config.AddrRanges[i].Start);
702 __writemsr(MsrB, Cpu->Config.AddrRanges[i].End);
703 }
704 else
705 {
706 __writemsr(MsrA, 0);
707 __writemsr(MsrB, 0);
708 }
709 }
710
711 //
712 // Step 5: Build IA32_RTIT_CTL and enable
713 //
714 Ctl = PtEngineBuildCtlFromConfig(&Cpu->Config, &Caps);
715
716 //
717 // Save the CTL value for resume
718 //
719 Cpu->SavedCtl = Ctl;
720
721 //
722 // Enable tracing — MUST be the last MSR write
723 //
724 Ctl.TraceEn = 1;
725 Cpu->SavedCtl.TraceEn = 1;
726 __writemsr(MSR_IA32_RTIT_CTL, Ctl.Value);
727
728 //
729 // Step 6: Verify tracing started
730 //
731 {
733 Status.Value = __readmsr(MSR_IA32_RTIT_STATUS);
734 if (Status.Error)
735 {
736 Ctl.TraceEn = 0;
737 __writemsr(MSR_IA32_RTIT_CTL, Ctl.Value);
738 Cpu->State = PT_STATE_ERROR;
739 return -1;
740 }
741 }
742
743 Cpu->State = PT_STATE_TRACING;
744 return 0;
745}
UCHAR BOOLEAN
Definition BasicTypes.h:35
#define MSR_IA32_RTIT_ADDR0_A
Definition PtDefinitions.h:26
#define MSR_IA32_RTIT_CR3_MATCH
Definition PtDefinitions.h:24
@ PT_STATE_STOPPED
Definition PtDefinitions.h:222
@ PT_STATE_ERROR
Definition PtDefinitions.h:223
#define MSR_IA32_RTIT_ADDR0_B
Definition PtDefinitions.h:27
#define MSR_IA32_RTIT_OUTPUT_BASE
Definition PtDefinitions.h:20
HYPEREVADE_CALLBACKS g_Callbacks
List of callbacks.
Definition Transparency.h:23

◆ PtEngineStop()

UINT64 PtEngineStop ( PT_PER_CPU * Cpu,
PT_OUTPUT_BUFFER * Out )

Stop tracing on the CURRENT CPU. Reads final output position, copies trace data if requested, resets PT MSRs.

Returns
UINT64 Total bytes captured (across all PMIs + final), or 0 on error.
812{
813 UINT64 BytesThisRun;
814 UINT32 i;
815 BOOLEAN IsOnVmxRootMode = FALSE;
816
817 if (Cpu == NULL)
818 return 0;
819 if (Cpu->State != PT_STATE_TRACING && Cpu->State != PT_STATE_PAUSED)
820 return 0;
821
822 //
823 // Detect VMX-root context for symmetry with PtEngineStart. Direct WRMSR
824 // works in both modes for PT MSRs (not in MSR bitmap by default).
825 //
827 {
828 IsOnVmxRootMode = g_Callbacks.VmFuncVmxGetCurrentExecutionMode();
829 LogInfo("PT: PtEngineStop on core %u (vmx-root=%u)\n",
830 KeGetCurrentProcessorNumberEx(NULL),
831 (UINT32)IsOnVmxRootMode);
832 }
833
834 //
835 // Disable tracing
836 //
837 {
839 Ctl.Value = __readmsr(MSR_IA32_RTIT_CTL);
840 Ctl.TraceEn = 0;
841 __writemsr(MSR_IA32_RTIT_CTL, Ctl.Value);
842 }
843
844 //
845 // Read final output position and copy data if requested
846 //
847 BytesThisRun = PtEngineReadBytesWritten(&Cpu->Buffer, Out);
848 Cpu->TotalBytesCaptured += BytesThisRun;
849
850 //
851 // Check for hardware error
852 //
853 {
855 Status.Value = __readmsr(MSR_IA32_RTIT_STATUS);
856 if (Status.Error)
857 {
858 Cpu->State = PT_STATE_ERROR;
859 return 0;
860 }
861 }
862
863 //
864 // Reset all PT MSRs
865 //
866 __writemsr(MSR_IA32_RTIT_CTL, 0);
867 __writemsr(MSR_IA32_RTIT_STATUS, 0);
868 __writemsr(MSR_IA32_RTIT_OUTPUT_BASE, 0);
869 __writemsr(MSR_IA32_RTIT_OUTPUT_MASK_PTRS, 0);
870 __writemsr(MSR_IA32_RTIT_CR3_MATCH, 0);
871
872 //
873 // Clear all ADDRn MSRs that this CPU supports.
874 // NumAddrRanges was validated against caps in PtEngineStart.
875 //
876 for (i = 0; i < Cpu->Config.NumAddrRanges; i++)
877 {
878 __writemsr(MSR_IA32_RTIT_ADDR0_A + (i * 2), 0);
879 __writemsr(MSR_IA32_RTIT_ADDR0_B + (i * 2), 0);
880 }
881
882 Cpu->State = PT_STATE_STOPPED;
883 return Cpu->TotalBytesCaptured;
884}

◆ PtFilter()

VOID PtFilter ( const PT_FILTER_OPTIONS * FilterOptions)

LBR-style filter wrapper: refresh tracing on the CURRENT CPU with a fresh PT_FILTER_OPTIONS.

Mirrors LbrFilter — the caller hands in only the fields a user is allowed to drive (TraceUser, TraceKernel, TargetCr3, BufferSize, NumAddrRanges, AddrRanges) and PtFilter writes them into the per-CPU PT_TRACE_CONFIG one at a time. Engine-managed options (BranchEn, TscEn, MtcEn, CycEn, RetCompression, *Freq) are left alone, so a filter call can never accidentally turn off the packet types the engine relies on.

BufferSize == 0 keeps the per-CPU value already in place, so pure filter changes (privilege bits, CR3, IP ranges) skip any reallocation of the ToPA / output / overflow buffers and can run entirely from a DPC. Genuine buffer-size changes still need a PASSIVE_LEVEL caller to free + reallocate; HyperTracePtFilter handles that case before broadcasting.

1403{
1404 UINT32 CurrentCore;
1405 PT_PER_CPU * Cpu;
1406 UINT32 i;
1407 PT_CAPABILITIES Caps = {0};
1408
1409 if (g_PtStateList == NULL || FilterOptions == NULL)
1410 return;
1411
1412 if (PtEngineQueryCapabilities(&Caps) != 0)
1413 return;
1414
1415 CurrentCore = KeGetCurrentProcessorNumberEx(NULL);
1416 Cpu = &g_PtStateList[CurrentCore];
1417
1418 LogInfo("PT: applying filter on core %u\n", CurrentCore);
1419
1420 //
1421 // Stop tracing on this CPU first so we can safely mutate Cpu->Config
1422 // and reprogram the RTIT_CTL bits.
1423 //
1424 if (Cpu->State == PT_STATE_TRACING || Cpu->State == PT_STATE_PAUSED)
1425 {
1426 PtEngineStop(Cpu, NULL);
1427 }
1428
1429 //
1430 // Apply only the user-tunable fields to this CPU's per-CPU config.
1431 //
1432 Cpu->Config.TraceUser = FilterOptions->TraceUser;
1433 Cpu->Config.TraceKernel = FilterOptions->TraceKernel;
1434
1435 if (FilterOptions->TargetCr3 != 0 && !Caps.Cr3Filtering)
1436 {
1437 LogInfo("PT: CR3 filtering requested but not supported by CPU\n");
1438 Cpu->Config.TargetCr3 = 0;
1439 }
1440 else
1441 {
1442 Cpu->Config.TargetCr3 = FilterOptions->TargetCr3;
1443 }
1444
1445 if (FilterOptions->NumAddrRanges > Caps.NumAddrRanges)
1446 {
1447 LogInfo("PT: requested %u IP filter ranges, but CPU only supports %u\n", FilterOptions->NumAddrRanges, Caps.NumAddrRanges);
1449 }
1450 else if (FilterOptions->NumAddrRanges > 0 && !Caps.IpFiltering)
1451 {
1452 LogInfo("PT: IP filtering requested but not supported by CPU\n");
1453 Cpu->Config.NumAddrRanges = 0;
1454 }
1455 else
1456 {
1457 Cpu->Config.NumAddrRanges = FilterOptions->NumAddrRanges;
1458 }
1459
1460 if (FilterOptions->BufferSize != 0)
1461 {
1462 Cpu->Config.BufferSize = FilterOptions->BufferSize;
1463 }
1464
1465 for (i = 0; i < PT_MAX_ADDR_RANGES; i++)
1466 {
1467 Cpu->Config.AddrRanges[i] = FilterOptions->AddrRanges[i];
1468 }
1469
1470 //
1471 // If the per-CPU buffers haven't been allocated yet, leave the slot
1472 // configured so the next PtStart picks it up — skip starting here
1473 // because PtEngineStart needs ToPA / output / overflow already
1474 // allocated at PASSIVE_LEVEL.
1475 //
1476 if (Cpu->State == PT_STATE_DISABLED)
1477 return;
1478
1479 PtEngineStart(Cpu);
1480}
INT32 PtEngineStart(PT_PER_CPU *Cpu)
Start tracing on the CURRENT CPU using the passed PT_PER_CPU. Programs all PT MSRs and sets TraceEn=1...
Definition Pt.c:610
UINT64 PtEngineStop(PT_PER_CPU *Cpu, PT_OUTPUT_BUFFER *Out)
Stop tracing on the CURRENT CPU. Reads final output position, copies trace data if requested,...
Definition Pt.c:811
UINT64 BufferSize
Definition Pt.h:45
UINT32 NumAddrRanges
Definition Pt.h:46
UINT64 TargetCr3
Definition Pt.h:44
BOOLEAN TraceUser
Definition Pt.h:42
BOOLEAN TraceKernel
Definition Pt.h:43
PT_ADDR_RANGE AddrRanges[PT_MAX_ADDR_RANGES]
Definition Pt.h:47

◆ PtFlush()

VOID PtFlush ( )

Flush PT trace state on the CURRENT CPU — disables tracing and clears the bytes-captured counter so the next PtStart begins from a fresh baseline. Buffer freeing happens at PASSIVE_LEVEL via PtFreeAllCpuBuffers; this is safe to call from a DPC.

Mirrors LbrFlush.

1492{
1493 UINT32 CurrentCore;
1494 PT_PER_CPU * Cpu;
1495
1496 if (g_PtStateList == NULL)
1497 return;
1498
1499 CurrentCore = KeGetCurrentProcessorNumberEx(NULL);
1500 Cpu = &g_PtStateList[CurrentCore];
1501
1502 LogInfo("PT: flush on core %u\n", CurrentCore);
1503
1504 if (Cpu->State == PT_STATE_TRACING || Cpu->State == PT_STATE_PAUSED)
1505 {
1506 PtEngineStop(Cpu, NULL);
1507 }
1508
1509 Cpu->TotalBytesCaptured = 0;
1510}

◆ PtFreeAllCpuBuffers()

VOID PtFreeAllCpuBuffers ( )

Free ToPA / output / overflow buffers for every active CPU.

   Must be called at IRQL == PASSIVE_LEVEL (after broadcasting any
   per-core disable DPC), because MmFreeContiguousMemory is paged.
1090{
1091 UINT32 ProcessorsCount;
1092 UINT32 i;
1093
1094 if (g_PtStateList == NULL)
1095 return;
1096
1097 //
1098 // Drop any live user mappings before the underlying contiguous
1099 // memory goes away. Must run in the same process that called the
1100 // mmap IOCTL — see HYPERTRACE_PT_MMAP_PACKETS for the contract.
1101 //
1103
1104 ProcessorsCount = KeQueryActiveProcessorCount(0);
1105
1106 for (i = 0; i < ProcessorsCount; i++)
1107 {
1109 }
1110}
VOID PtUnmapAllCpuBuffersFromUser()
Release every user mapping created by PtMmapAllCpuBuffersToUser. Called by PtFreeAllCpuBuffers (i....
Definition Pt.c:1190
VOID PtEngineFreeBuffers(PT_PER_CPU *Cpu)
Free all PT buffers belonging to one per-CPU context. Must not be called while State == PT_STATE_TRAC...
Definition Pt.c:482

◆ PtMmapAllCpuBuffersToUser()

INT32 PtMmapAllCpuBuffersToUser ( PT_USER_BUFFER_DESC * OutDescs,
UINT32 MaxDescs,
UINT32 * OutNumCpus )

Map every per-CPU PT main output buffer and 4 KB overflow page into the current user process as a single virtually contiguous region per CPU, and fill OutDescs[i] with the base UserVa and the total Size (main + overflow) for that CPU.

PT buffers must already exist (PtAllocateAllCpuBuffers must have run, i.e. PT is enabled). Idempotent within an enable cycle: a second call returns the already-cached mappings. On any per-CPU failure the partial work is rolled back and the function returns -1.

Returns
INT32 0 on success, -1 on failure.
1128{
1129 UINT32 ProcessorsCount;
1130 UINT32 i;
1131
1132 if (OutDescs == NULL || OutNumCpus == NULL || g_PtStateList == NULL)
1133 return -1;
1134
1135 ProcessorsCount = KeQueryActiveProcessorCount(0);
1136 if (ProcessorsCount == 0 || ProcessorsCount > MaxDescs || ProcessorsCount > PT_MAX_CPUS_FOR_MMAP)
1137 return -1;
1138
1140 {
1141 for (i = 0; i < ProcessorsCount; i++)
1142 {
1143 PT_PER_CPU * Cpu = &g_PtStateList[i];
1144
1145 if (Cpu->Buffer.OutputVa == NULL || Cpu->Buffer.OverflowVa == NULL)
1146 {
1148 return -1;
1149 }
1150
1151 if (PtMmapCpuRegionToUser(Cpu->Buffer.OutputVa,
1153 (SIZE_T)Cpu->Buffer.OutputSize,
1155 (SIZE_T)PT_OVERFLOW_SIZE,
1156 &g_PtUserMappings[i].Mdl,
1157 &g_PtUserMappings[i].UserVa) != 0)
1158 {
1160 return -1;
1161 }
1162 }
1163
1165 }
1166
1167 for (i = 0; i < ProcessorsCount; i++)
1168 {
1169 OutDescs[i].CpuId = i;
1170 OutDescs[i].Reserved = 0;
1171 OutDescs[i].UserVa = (UINT64)(ULONG_PTR)g_PtUserMappings[i].UserVa;
1172 OutDescs[i].Size = g_PtStateList[i].Buffer.OutputSize + PT_OVERFLOW_SIZE;
1173 }
1174
1175 *OutNumCpus = ProcessorsCount;
1176 return 0;
1177}
#define PT_MAX_CPUS_FOR_MMAP
Definition PtDefinitions.h:60
PT_USER_MAPPING g_PtUserMappings[PT_MAX_CPUS_FOR_MMAP]
Per-CPU MDL + user-mode VA for the PT mmap surface (main output buffer concatenated with the 4 KB ove...
Definition GlobalVariables.h:95
BOOLEAN g_PtUserMappingsActive
Set while g_PtUserMappings holds live user mappings; cleared by PtUnmapAllCpuBuffersFromUser.
Definition GlobalVariables.h:101
UINT64 UserVa
Definition PtDefinitions.h:367
UINT64 Size
Definition PtDefinitions.h:368
UINT32 CpuId
Definition PtDefinitions.h:365
UINT32 Reserved
Definition PtDefinitions.h:366

◆ PtPause()

VOID PtPause ( )

Pause PT tracing on the CURRENT CPU. Buffer state is preserved so a subsequent PtResume picks up where this left off.

1277{
1278 UINT32 CurrentCore;
1279 PT_PER_CPU * Cpu;
1280
1281 if (g_PtStateList == NULL)
1282 return;
1283
1284 CurrentCore = KeGetCurrentProcessorNumberEx(NULL);
1285 Cpu = &g_PtStateList[CurrentCore];
1286
1287 LogInfo("PT: pausing trace on core %u\n", CurrentCore);
1288
1289 PtEnginePause(Cpu);
1290}
INT32 PtEnginePause(PT_PER_CPU *Cpu)
Pause tracing on the CURRENT CPU. Preserves buffer state.
Definition Pt.c:890

◆ PtResume()

VOID PtResume ( )

Resume PT tracing on the CURRENT CPU after a prior PtPause.

1297{
1298 UINT32 CurrentCore;
1299 PT_PER_CPU * Cpu;
1300
1301 if (g_PtStateList == NULL)
1302 return;
1303
1304 CurrentCore = KeGetCurrentProcessorNumberEx(NULL);
1305 Cpu = &g_PtStateList[CurrentCore];
1306
1307 LogInfo("PT: resuming trace on core %u\n", CurrentCore);
1308
1309 PtEngineResume(Cpu);
1310}
INT32 PtEngineResume(PT_PER_CPU *Cpu)
Resume tracing on the CURRENT CPU after pause.
Definition Pt.c:909

◆ PtSize()

UINT64 PtSize ( )

Snapshot the current PT output position on the CURRENT CPU without disturbing tracing state. The returned value is the number of bytes of valid trace data sitting in this CPU's main + overflow buffer, i.e. the offset a decoder should stop at when reading from the user mapping.

1321{
1322 UINT32 CurrentCore;
1323 PT_PER_CPU * Cpu;
1325 UINT32 TopaIndex;
1326 UINT32 BytesInEntry;
1327
1328 if (g_PtStateList == NULL)
1329 return 0;
1330
1331 CurrentCore = KeGetCurrentProcessorNumberEx(NULL);
1332 Cpu = &g_PtStateList[CurrentCore];
1333
1334 if (Cpu->State != PT_STATE_TRACING && Cpu->State != PT_STATE_PAUSED && Cpu->State != PT_STATE_STOPPED)
1335 return 0;
1336
1337 //
1338 // Read MASK_PTRS without touching MSRs that would disturb tracing.
1339 // For an active trace this gives the live byte count; for stopped
1340 // traces it returns the position at the moment tracing was disabled.
1341 //
1342 Mask.Value = __readmsr(MSR_IA32_RTIT_OUTPUT_MASK_PTRS);
1343 TopaIndex = (UINT32)Mask.MaskOrTableOffset;
1344 BytesInEntry = (UINT32)Mask.OutputOffset;
1345
1346 if (TopaIndex == 0)
1347 return BytesInEntry;
1348
1349 return Cpu->Buffer.OutputSize + BytesInEntry;
1350}

◆ PtStart()

BOOLEAN PtStart ( )

Start PT tracing on the CURRENT CPU. Buffers must already be allocated by PtAllocateAllCpuBuffers (called at PASSIVE_LEVEL).

Mirrors LbrStart but takes no parameters: per-CPU configuration is sourced from g_PtStateList[core].Config (defaulted at init time).

Returns
BOOLEAN TRUE on success.
1216{
1217 UINT32 CurrentCore;
1218 PT_PER_CPU * Cpu;
1219
1220 if (g_PtStateList == NULL)
1221 {
1222 LogInfo("PT: per-CPU state not initialized.\n");
1223 return FALSE;
1224 }
1225
1226 CurrentCore = KeGetCurrentProcessorNumberEx(NULL);
1227 Cpu = &g_PtStateList[CurrentCore];
1228
1229 if (Cpu->State == PT_STATE_DISABLED)
1230 {
1231 //
1232 // Buffers should have been allocated at PASSIVE_LEVEL beforehand.
1233 // Allocating from a DPC is unsafe (MmAllocateContiguousMemory* is
1234 // a paged routine) so just bail out.
1235 //
1236 LogInfo("PT: buffers not allocated for core %u\n", CurrentCore);
1237 return FALSE;
1238 }
1239
1240 if (PtEngineStart(Cpu) != 0)
1241 {
1242 LogInfo("PT: PtEngineStart failed on core %u (state=%d)\n", CurrentCore, Cpu->State);
1243 return FALSE;
1244 }
1245
1246 return TRUE;
1247}

◆ PtStop()

VOID PtStop ( )

Stop PT tracing on the CURRENT CPU. Trace data accumulated in the per-CPU output buffer is left in place; PtSize / PtDump can read it later.

1256{
1257 UINT32 CurrentCore;
1258 PT_PER_CPU * Cpu;
1259
1260 if (g_PtStateList == NULL)
1261 return;
1262
1263 CurrentCore = KeGetCurrentProcessorNumberEx(NULL);
1264 Cpu = &g_PtStateList[CurrentCore];
1265
1266 LogInfo("PT: stopping trace on core %d\n", CurrentCore);
1267
1268 PtEngineStop(Cpu, NULL);
1269}

◆ PtUnmapAllCpuBuffersFromUser()

VOID PtUnmapAllCpuBuffersFromUser ( )

Release every user mapping created by PtMmapAllCpuBuffersToUser. Called by PtFreeAllCpuBuffers (i.e. on PT disable / flush) so user VAs stop being usable before the backing memory is freed. Also used as a rollback path on partial mmap failure.

Always walks the full table (PtUnmapCpuRegionFromUser is NULL-safe) so rollback after a half-finished mapping still cleans up the CPUs that were mapped before the failure.

1191{
1192 UINT32 i;
1193
1194 for (i = 0; i < PT_MAX_CPUS_FOR_MMAP; i++)
1195 {
1196 PtUnmapCpuRegionFromUser(g_PtUserMappings[i].Mdl,
1197 g_PtUserMappings[i].UserVa);
1198 g_PtUserMappings[i].Mdl = NULL;
1199 g_PtUserMappings[i].UserVa = NULL;
1200 }
1201
1203}