[RFC v1 16/19] ptwrite uprobes / perf tools pt: Improve FUP error handling for ptwrite
From: Andi Kleen
Date: Mon Aug 31 2026 - 16:15:20 EST
The PT decoder can't see the patched code generated by ptwrite uprobes.
Without ptw_on_fup the ptwrite stubs are invisible to PT branch tracing
(other than the ptwrite packet itself) because they don't contain any
indirect or conditional branches, so there is no problem with the PT
decoder.
However when ptw_on_fup is enabled there is a FUP (Flow Update Packet)
reporting the IP of each ptwrite after the PTW packets. The decoder tries
to resolve this FUP packet to the code, but it errors out because it can't
see the uprobes generated code.
Normally this is not a problem because we just use 'q' mode which doesn't
walk instructions, but still reports on the ptwrites and their FUPs.
Also it's possible to disable fup_on_ptw, however that reduces the
tolerance to data loss in the uprobes ptwrite decoder.
When full instruction tracing is desired the PTW+FUP errors cause data
loss.
Special case this in the decoder instead. When the FUP is associated with a
ptwrite don't error out on missing instructions pages. Just report the
ptwrite with its IP and continue.
An alternative would be to define a metadata event for the JITed code and
let the decoder understand it. That may be desirable in the future so that
the PT users sees all the code executed. But for now this simple change is
good enough.
Assisted-by: omp:gpt-5.6-luna
Signed-off-by: Andi Kleen <ak@xxxxxxxxxx>
---
.../util/intel-pt-decoder/intel-pt-decoder.c | 23 ++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c
index e733f6b1f7ac..bd31d65dbe03 100644
--- a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c
+++ b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c
@@ -1440,8 +1440,29 @@ static int intel_pt_walk_fup(struct intel_pt_decoder *decoder)
return -EAGAIN;
}
decoder->set_fup_tx_flags = false;
- if (err)
+ if (err) {
+ /*
+ * A ptwrite's FUP can target an address whose
+ * instruction cannot be resolved (e.g. the
+ * [uprobes-ptwrite] stub is an anonymous special
+ * mapping invisible to the machine). The FUP is
+ * still the ptwrite's IP: report it rather than
+ * failing the whole walk.
+ */
+ if (decoder->set_fup_ptw) {
+ decoder->set_fup_ptw = false;
+ decoder->pkt_state = INTEL_PT_STATE_IN_SYNC;
+ decoder->state.type &= ~INTEL_PT_BRANCH;
+ decoder->state.type |= INTEL_PT_PTW;
+ decoder->state.flags |= INTEL_PT_FUP_IP;
+ decoder->state.from_ip = decoder->ip;
+ decoder->state.to_ip = 0;
+ decoder->state.ptw_payload =
+ decoder->fup_ptw_payload;
+ return 0;
+ }
return err;
+ }
if (intel_pt_insn.branch == INTEL_PT_BR_INDIRECT) {
intel_pt_log_at("ERROR: Unexpected indirect branch",
--
2.54.0