LLDB mainline
LibiptDecoder.cpp
Go to the documentation of this file.
1//===-- LibiptDecoder.cpp --======-----------------------------------------===//
2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
3// See https://llvm.org/LICENSE.txt for license information.
4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5//
6//===----------------------------------------------------------------------===//
7
8#include "LibiptDecoder.h"
9#include "TraceIntelPT.h"
10#include "lldb/Target/Process.h"
11#include <optional>
12
13using namespace lldb;
14using namespace lldb_private;
15using namespace lldb_private::trace_intel_pt;
16using namespace llvm;
17
18bool IsLibiptError(int status) { return status < 0; }
19
20bool IsEndOfStream(int status) {
21 assert(status >= 0 && "We can't check if we reached the end of the stream if "
22 "we got a failed status");
23 return status & pts_eos;
24}
25
26bool HasEvents(int status) {
27 assert(status >= 0 && "We can't check for events if we got a failed status");
28 return status & pts_event_pending;
29}
30
31// RAII deleter for libipt's decoders
32auto InsnDecoderDeleter = [](pt_insn_decoder *decoder) {
33 pt_insn_free_decoder(decoder);
34};
35
36auto QueryDecoderDeleter = [](pt_query_decoder *decoder) {
37 pt_qry_free_decoder(decoder);
38};
39
41 std::unique_ptr<pt_insn_decoder, decltype(InsnDecoderDeleter)>;
42
44 std::unique_ptr<pt_query_decoder, decltype(QueryDecoderDeleter)>;
45
46/// Create a basic configuration object limited to a given buffer that can be
47/// used for many different decoders.
48static Expected<pt_config> CreateBasicLibiptConfig(TraceIntelPT &trace_intel_pt,
49 ArrayRef<uint8_t> buffer) {
50 Expected<pt_cpu> cpu_info = trace_intel_pt.GetCPUInfo();
51 if (!cpu_info)
52 return cpu_info.takeError();
53
54 pt_config config;
55 pt_config_init(&config);
56 config.cpu = *cpu_info;
57
58 int status = pt_cpu_errata(&config.errata, &config.cpu);
59 if (IsLibiptError(status))
60 return make_error<IntelPTError>(status);
61
62 // The libipt library does not modify the trace buffer, hence the
63 // following casts are safe.
64 config.begin = const_cast<uint8_t *>(buffer.data());
65 config.end = const_cast<uint8_t *>(buffer.data() + buffer.size());
66 return config;
67}
68
69/// Callback used by libipt for reading the process memory.
70///
71/// More information can be found in
72/// https://github.com/intel/libipt/blob/master/doc/man/pt_image_set_callback.3.md
73static int ReadProcessMemory(uint8_t *buffer, size_t size,
74 const pt_asid * /* unused */, uint64_t pc,
75 void *context) {
76 Process *process = static_cast<Process *>(context);
77
79 int bytes_read = process->ReadMemory(pc, buffer, size, error);
80 if (error.Fail())
81 return -pte_nomap;
82 return bytes_read;
83}
84
85/// Set up the memory image callback for the given decoder.
86static Error SetupMemoryImage(pt_insn_decoder *decoder, Process &process) {
87 pt_image *image = pt_insn_get_image(decoder);
88
89 int status = pt_image_set_callback(image, ReadProcessMemory, &process);
90 if (IsLibiptError(status))
91 return make_error<IntelPTError>(status);
92 return Error::success();
93}
94
95/// Create an instruction decoder for the given buffer and the given process.
96static Expected<PtInsnDecoderUP>
97CreateInstructionDecoder(TraceIntelPT &trace_intel_pt, ArrayRef<uint8_t> buffer,
98 Process &process) {
99 Expected<pt_config> config = CreateBasicLibiptConfig(trace_intel_pt, buffer);
100 if (!config)
101 return config.takeError();
102
103 pt_insn_decoder *decoder_ptr = pt_insn_alloc_decoder(&*config);
104 if (!decoder_ptr)
105 return make_error<IntelPTError>(-pte_nomem);
106
107 PtInsnDecoderUP decoder_up(decoder_ptr, InsnDecoderDeleter);
108
109 if (Error err = SetupMemoryImage(decoder_ptr, process))
110 return std::move(err);
111
112 return decoder_up;
113}
114
115/// Create a query decoder for the given buffer. The query decoder is the
116/// highest level decoder that operates directly on packets and doesn't perform
117/// actual instruction decoding. That's why it can be useful for inspecting a
118/// raw trace without pinning it to a particular process.
119static Expected<PtQueryDecoderUP>
120CreateQueryDecoder(TraceIntelPT &trace_intel_pt, ArrayRef<uint8_t> buffer) {
121 Expected<pt_config> config = CreateBasicLibiptConfig(trace_intel_pt, buffer);
122 if (!config)
123 return config.takeError();
124
125 pt_query_decoder *decoder_ptr = pt_qry_alloc_decoder(&*config);
126 if (!decoder_ptr)
127 return make_error<IntelPTError>(-pte_nomem);
128
129 return PtQueryDecoderUP(decoder_ptr, QueryDecoderDeleter);
130}
131
132/// Class used to identify anomalies in traces, which should often indicate a
133/// fatal error in the trace.
135public:
136 PSBBlockAnomalyDetector(pt_insn_decoder &decoder,
137 TraceIntelPT &trace_intel_pt,
138 DecodedThread &decoded_thread)
139 : m_decoder(decoder), m_decoded_thread(decoded_thread) {
141 trace_intel_pt.GetGlobalProperties()
144 trace_intel_pt.GetGlobalProperties()
148 }
149
150 /// \return
151 /// An \a llvm::Error if an anomaly that includes the last instruction item
152 /// in the trace, or \a llvm::Error::success otherwise.
155 uint64_t insn_added_since_last_packet_offset =
158
159 // We want to check if we might have fallen in an infinite loop. As this
160 // check is not a no-op, we want to do it when we have a strong suggestion
161 // that things went wrong. First, we check how many instructions we have
162 // decoded since we processed an Intel PT packet for the last time. This
163 // number should be low, because at some point we should see branches, jumps
164 // or interrupts that require a new packet to be processed. Once we reach
165 // certain threshold we start analyzing the trace.
166 //
167 // We use the number of decoded instructions since the last Intel PT packet
168 // as a proxy because, in fact, we don't expect a single packet to give,
169 // say, 100k instructions. That would mean that there are 100k sequential
170 // instructions without any single branch, which is highly unlikely, or that
171 // we found an infinite loop using direct jumps, e.g.
172 //
173 // 0x0A: nop or pause
174 // 0x0C: jump to 0x0A
175 //
176 // which is indeed code that is found in the kernel. I presume we reach
177 // this kind of code in the decoder because we don't handle self-modified
178 // code in post-mortem kernel traces.
179 //
180 // We are right now only signaling the anomaly as a trace error, but it
181 // would be more conservative to also discard all the trace items found in
182 // this PSB. I prefer not to do that for the time being to give more
183 // exposure to this kind of anomalies and help debugging. Discarding the
184 // trace items would just make investigation harded.
185 //
186 // Finally, if the user wants to see if a specific thread has an anomaly,
187 // it's enough to run the `thread trace dump info` command and look for the
188 // count of this kind of errors.
189
190 if (insn_added_since_last_packet_offset >=
192 // In this case, we have decoded a massive amount of sequential
193 // instructions that don't loop. Honestly I wonder if this will ever
194 // happen, but better safe than sorry.
195 return createStringError(
196 inconvertibleErrorCode(),
197 "anomalous trace: possible infinite trace detected");
198 }
199 if (insn_added_since_last_packet_offset ==
201 if (std::optional<uint64_t> loop_size = TryIdentifyInfiniteLoop()) {
202 return createStringError(
203 inconvertibleErrorCode(),
204 "anomalous trace: possible infinite loop detected of size %" PRIu64,
205 *loop_size);
206 }
208 }
209 return Error::success();
210 }
211
212private:
213 std::optional<uint64_t> TryIdentifyInfiniteLoop() {
214 // The infinite decoding loops we'll encounter are due to sequential
215 // instructions that repeat themselves due to direct jumps, therefore in a
216 // cycle each individual address will only appear once. We use this
217 // information to detect cycles by finding the last 2 ocurrences of the last
218 // instruction added to the trace. Then we traverse the trace making sure
219 // that these two instructions where the ends of a repeating loop.
220
221 // This is a utility that returns the most recent instruction index given a
222 // position in the trace. If the given position is an instruction, that
223 // position is returned. It skips non-instruction items.
224 auto most_recent_insn_index =
225 [&](uint64_t item_index) -> std::optional<uint64_t> {
226 while (true) {
227 if (m_decoded_thread.GetItemKindByIndex(item_index) ==
229 return item_index;
230 }
231 if (item_index == 0)
232 return std::nullopt;
233 item_index--;
234 }
235 return std::nullopt;
236 };
237 // Similar to most_recent_insn_index but skips the starting position.
238 auto prev_insn_index = [&](uint64_t item_index) -> std::optional<uint64_t> {
239 if (item_index == 0)
240 return std::nullopt;
241 return most_recent_insn_index(item_index - 1);
242 };
243
244 // We first find the most recent instruction.
245 std::optional<uint64_t> last_insn_index_opt =
246 *prev_insn_index(m_decoded_thread.GetItemsCount());
247 if (!last_insn_index_opt)
248 return std::nullopt;
249 uint64_t last_insn_index = *last_insn_index_opt;
250
251 // We then find the most recent previous occurrence of that last
252 // instruction.
253 std::optional<uint64_t> last_insn_copy_index =
254 prev_insn_index(last_insn_index);
255 uint64_t loop_size = 1;
256 while (last_insn_copy_index &&
257 m_decoded_thread.GetInstructionLoadAddress(*last_insn_copy_index) !=
259 last_insn_copy_index = prev_insn_index(*last_insn_copy_index);
260 loop_size++;
261 }
262 if (!last_insn_copy_index)
263 return std::nullopt;
264
265 // Now we check if the segment between these last positions of the last
266 // instruction address is in fact a repeating loop.
267 uint64_t loop_elements_visited = 1;
268 uint64_t insn_index_a = last_insn_index,
269 insn_index_b = *last_insn_copy_index;
270 while (loop_elements_visited < loop_size) {
271 if (std::optional<uint64_t> prev = prev_insn_index(insn_index_a))
272 insn_index_a = *prev;
273 else
274 return std::nullopt;
275 if (std::optional<uint64_t> prev = prev_insn_index(insn_index_b))
276 insn_index_b = *prev;
277 else
278 return std::nullopt;
281 return std::nullopt;
282 loop_elements_visited++;
283 }
284 return loop_size;
285 }
286
287 // Refresh the internal counters if a new packet offset has been visited
289 lldb::addr_t new_packet_offset;
290 if (!IsLibiptError(pt_insn_get_offset(&m_decoder, &new_packet_offset)) &&
291 new_packet_offset != m_last_packet_offset) {
292 m_last_packet_offset = new_packet_offset;
297 }
298 }
299
300 pt_insn_decoder &m_decoder;
307};
308
309/// Class that decodes a raw buffer for a single PSB block using the low level
310/// libipt library. It assumes that kernel and user mode instructions are not
311/// mixed in the same PSB block.
312///
313/// Throughout this code, the status of the decoder will be used to identify
314/// events needed to be processed or errors in the decoder. The values can be
315/// - negative: actual errors
316/// - positive or zero: not an error, but a list of bits signaling the status
317/// of the decoder, e.g. whether there are events that need to be decoded or
318/// not.
320public:
321 /// \param[in] decoder
322 /// A decoder configured to start and end within the boundaries of the
323 /// given \p psb_block.
324 ///
325 /// \param[in] psb_block
326 /// The PSB block to decode.
327 ///
328 /// \param[in] next_block_ip
329 /// The starting ip at the next PSB block of the same thread if available.
330 ///
331 /// \param[in] decoded_thread
332 /// A \a DecodedThread object where the decoded instructions will be
333 /// appended to. It might have already some instructions.
334 ///
335 /// \param[in] tsc_upper_bound
336 /// Maximum allowed value of TSCs decoded from this PSB block.
337 /// Any of this PSB's data occurring after this TSC will be excluded.
338 PSBBlockDecoder(PtInsnDecoderUP &&decoder_up, const PSBBlock &psb_block,
339 std::optional<lldb::addr_t> next_block_ip,
340 DecodedThread &decoded_thread, TraceIntelPT &trace_intel_pt,
341 std::optional<DecodedThread::TSC> tsc_upper_bound)
342 : m_decoder_up(std::move(decoder_up)), m_psb_block(psb_block),
343 m_next_block_ip(next_block_ip), m_decoded_thread(decoded_thread),
344 m_anomaly_detector(*m_decoder_up, trace_intel_pt, decoded_thread),
345 m_tsc_upper_bound(tsc_upper_bound) {}
346
347 /// \param[in] trace_intel_pt
348 /// The main Trace object that own the PSB block.
349 ///
350 /// \param[in] decoder
351 /// A decoder configured to start and end within the boundaries of the
352 /// given \p psb_block.
353 ///
354 /// \param[in] psb_block
355 /// The PSB block to decode.
356 ///
357 /// \param[in] buffer
358 /// The raw intel pt trace for this block.
359 ///
360 /// \param[in] process
361 /// The process to decode. It provides the memory image to use for
362 /// decoding.
363 ///
364 /// \param[in] next_block_ip
365 /// The starting ip at the next PSB block of the same thread if available.
366 ///
367 /// \param[in] decoded_thread
368 /// A \a DecodedThread object where the decoded instructions will be
369 /// appended to. It might have already some instructions.
370 static Expected<PSBBlockDecoder>
371 Create(TraceIntelPT &trace_intel_pt, const PSBBlock &psb_block,
372 ArrayRef<uint8_t> buffer, Process &process,
373 std::optional<lldb::addr_t> next_block_ip,
374 DecodedThread &decoded_thread,
375 std::optional<DecodedThread::TSC> tsc_upper_bound) {
376 Expected<PtInsnDecoderUP> decoder_up =
377 CreateInstructionDecoder(trace_intel_pt, buffer, process);
378 if (!decoder_up)
379 return decoder_up.takeError();
380
381 return PSBBlockDecoder(std::move(*decoder_up), psb_block, next_block_ip,
382 decoded_thread, trace_intel_pt, tsc_upper_bound);
383 }
384
386 int status = pt_insn_sync_forward(m_decoder_up.get());
387 assert(status >= 0 &&
388 "Synchronization shouldn't fail because this PSB was previously "
389 "decoded correctly.");
390
391 // We emit a TSC before a sync event to more easily associate a timestamp to
392 // the sync event. If present, the current block's TSC would be the first
393 // TSC we'll see when processing events.
394 if (m_psb_block.tsc)
396
398
400 }
401
402private:
403 /// Append an instruction and return \b false if and only if a serious anomaly
404 /// has been detected.
405 bool AppendInstructionAndDetectAnomalies(const pt_insn &insn) {
407
410 /*fatal=*/true);
411 return false;
412 }
413 return true;
414 }
415 /// Decode all the instructions and events of the given PSB block. The
416 /// decoding loop might stop abruptly if an infinite decoding loop is
417 /// detected.
419 pt_insn insn;
420
421 while (true) {
422 status = ProcessPTEvents(status);
423
424 if (IsLibiptError(status))
425 return;
426 else if (IsEndOfStream(status))
427 break;
428
429 // The status returned by pt_insn_next will need to be processed
430 // by ProcessPTEvents in the next loop if it is not an error.
431 std::memset(&insn, 0, sizeof insn);
432 status = pt_insn_next(m_decoder_up.get(), &insn, sizeof(insn));
433
434 if (IsLibiptError(status)) {
435 m_decoded_thread.AppendError(IntelPTError(status, insn.ip));
436 return;
437 } else if (IsEndOfStream(status)) {
438 break;
439 }
440
442 return;
443 }
444
445 // We need to keep querying non-branching instructions until we hit the
446 // starting point of the next PSB. We won't see events at this point. This
447 // is based on
448 // https://github.com/intel/libipt/blob/master/doc/howto_libipt.md#parallel-decode
449 if (m_next_block_ip && insn.ip != 0) {
450 while (insn.ip != *m_next_block_ip) {
452 return;
453
454 status = pt_insn_next(m_decoder_up.get(), &insn, sizeof(insn));
455
456 if (IsLibiptError(status)) {
457 m_decoded_thread.AppendError(IntelPTError(status, insn.ip));
458 return;
459 }
460 }
461 }
462 }
463
464 /// Process the TSC of a decoded PT event. Specifically, check if this TSC
465 /// is below the TSC upper bound for this PSB. If the TSC exceeds the upper
466 /// bound, return an error to abort decoding. Otherwise add the it to the
467 /// underlying DecodedThread and decoding should continue as expected.
468 ///
469 /// \param[in] tsc
470 /// The TSC of the a decoded event.
472 if (m_tsc_upper_bound && tsc >= *m_tsc_upper_bound) {
473 // This event and all the remaining events of this PSB have a TSC
474 // outside the range of the "owning" ThreadContinuousExecution. For
475 // now we drop all of these events/instructions, future work can
476 // improve upon this by determining the "owning"
477 // ThreadContinuousExecution of the remaining PSB data.
478 std::string err_msg = formatv("decoding truncated: TSC {0} exceeds "
479 "maximum TSC value {1}, will skip decoding"
480 " the remaining data of the PSB",
481 tsc, *m_tsc_upper_bound)
482 .str();
483
484 uint64_t offset;
485 int status = pt_insn_get_offset(m_decoder_up.get(), &offset);
486 if (!IsLibiptError(status)) {
487 err_msg = formatv("{2} (skipping {0} of {1} bytes)", offset,
488 m_psb_block.size, err_msg)
489 .str();
490 }
492 return createStringError(inconvertibleErrorCode(), err_msg);
493 } else {
495 return Error::success();
496 }
497 }
498
499 /// Before querying instructions, we need to query the events associated with
500 /// that instruction, e.g. timing and trace disablement events.
501 ///
502 /// \param[in] status
503 /// The status gotten from the previous instruction decoding or PSB
504 /// synchronization.
505 ///
506 /// \return
507 /// The pte_status after decoding events.
508 int ProcessPTEvents(int status) {
509 while (HasEvents(status)) {
510 pt_event event;
511 std::memset(&event, 0, sizeof event);
512 status = pt_insn_event(m_decoder_up.get(), &event, sizeof(event));
513
514 if (IsLibiptError(status)) {
516 return status;
517 }
518
519 if (event.has_tsc) {
520 if (Error err = ProcessPTEventTSC(event.tsc)) {
521 consumeError(std::move(err));
522 return -pte_internal;
523 }
524 }
525
526 switch (event.type) {
527 case ptev_disabled:
528 // The CPU paused tracing the program, e.g. due to ip filtering.
530 break;
531 case ptev_async_disabled:
532 // The kernel or user code paused tracing the program, e.g.
533 // a breakpoint or a ioctl invocation pausing the trace, or a
534 // context switch happened.
536 break;
537 case ptev_overflow:
538 // The CPU internal buffer had an overflow error and some instructions
539 // were lost. A OVF packet comes with an FUP packet (harcoded address)
540 // according to the documentation, so we'll continue seeing instructions
541 // after this event.
543 break;
544 default:
545 break;
546 }
547 }
548
549 return status;
550 }
551
552private:
555 std::optional<lldb::addr_t> m_next_block_ip;
558 std::optional<DecodedThread::TSC> m_tsc_upper_bound;
559};
560
562 DecodedThread &decoded_thread, TraceIntelPT &trace_intel_pt,
563 ArrayRef<uint8_t> buffer) {
564 Expected<std::vector<PSBBlock>> blocks =
565 SplitTraceIntoPSBBlock(trace_intel_pt, buffer, /*expect_tscs=*/false);
566 if (!blocks)
567 return blocks.takeError();
568
569 for (size_t i = 0; i < blocks->size(); i++) {
570 PSBBlock &block = blocks->at(i);
571
572 Expected<PSBBlockDecoder> decoder = PSBBlockDecoder::Create(
573 trace_intel_pt, block, buffer.slice(block.psb_offset, block.size),
574 *decoded_thread.GetThread()->GetProcess(),
575 i + 1 < blocks->size() ? blocks->at(i + 1).starting_ip : None,
576 decoded_thread, std::nullopt);
577 if (!decoder)
578 return decoder.takeError();
579
580 decoder->DecodePSBBlock();
581 }
582
583 return Error::success();
584}
585
587 DecodedThread &decoded_thread, TraceIntelPT &trace_intel_pt,
588 const DenseMap<lldb::cpu_id_t, llvm::ArrayRef<uint8_t>> &buffers,
589 const std::vector<IntelPTThreadContinousExecution> &executions) {
590 bool has_seen_psbs = false;
591 for (size_t i = 0; i < executions.size(); i++) {
592 const IntelPTThreadContinousExecution &execution = executions[i];
593
594 auto variant = execution.thread_execution.variant;
595
596 // We emit the first valid tsc
597 if (execution.psb_blocks.empty()) {
598 decoded_thread.NotifyTsc(execution.thread_execution.GetLowestKnownTSC());
599 } else {
600 assert(execution.psb_blocks.front().tsc &&
601 "per cpu decoding expects TSCs");
602 decoded_thread.NotifyTsc(
603 std::min(execution.thread_execution.GetLowestKnownTSC(),
604 *execution.psb_blocks.front().tsc));
605 }
606
607 // We then emit the CPU, which will be correctly associated with a tsc.
608 decoded_thread.NotifyCPU(execution.thread_execution.cpu_id);
609
610 // If we haven't seen a PSB yet, then it's fine not to show errors
611 if (has_seen_psbs) {
612 if (execution.psb_blocks.empty()) {
613 decoded_thread.AppendCustomError(
614 formatv("Unable to find intel pt data a thread "
615 "execution on cpu id = {0}",
616 execution.thread_execution.cpu_id)
617 .str());
618 }
619
620 // A hinted start is a non-initial execution that doesn't have a switch
621 // in. An only end is an initial execution that doesn't have a switch in.
622 // Any of those cases represent a gap because we have seen a PSB before.
623 if (variant == ThreadContinuousExecution::Variant::HintedStart ||
624 variant == ThreadContinuousExecution::Variant::OnlyEnd) {
625 decoded_thread.AppendCustomError(
626 formatv("Unable to find the context switch in for a thread "
627 "execution on cpu id = {0}",
628 execution.thread_execution.cpu_id)
629 .str());
630 }
631 }
632
633 for (size_t j = 0; j < execution.psb_blocks.size(); j++) {
634 const PSBBlock &psb_block = execution.psb_blocks[j];
635
636 Expected<PSBBlockDecoder> decoder = PSBBlockDecoder::Create(
637 trace_intel_pt, psb_block,
638 buffers.lookup(execution.thread_execution.cpu_id)
639 .slice(psb_block.psb_offset, psb_block.size),
640 *decoded_thread.GetThread()->GetProcess(),
641 j + 1 < execution.psb_blocks.size()
642 ? execution.psb_blocks[j + 1].starting_ip
643 : None,
644 decoded_thread, execution.thread_execution.GetEndTSC());
645 if (!decoder)
646 return decoder.takeError();
647
648 has_seen_psbs = true;
649 decoder->DecodePSBBlock();
650 }
651
652 // If we haven't seen a PSB yet, then it's fine not to show errors
653 if (has_seen_psbs) {
654 // A hinted end is a non-ending execution that doesn't have a switch out.
655 // An only start is an ending execution that doesn't have a switch out.
656 // Any of those cases represent a gap if we still have executions to
657 // process and we have seen a PSB before.
658 if (i + 1 != executions.size() &&
659 (variant == ThreadContinuousExecution::Variant::OnlyStart ||
660 variant == ThreadContinuousExecution::Variant::HintedEnd)) {
661 decoded_thread.AppendCustomError(
662 formatv("Unable to find the context switch out for a thread "
663 "execution on cpu id = {0}",
664 execution.thread_execution.cpu_id)
665 .str());
666 }
667 }
668 }
669 return Error::success();
670}
671
673 const IntelPTThreadContinousExecution &o) const {
674 // As the context switch might be incomplete, we look first for the first real
675 // PSB packet, which is a valid TSC. Otherwise, We query the thread execution
676 // itself for some tsc.
677 auto get_tsc = [](const IntelPTThreadContinousExecution &exec) {
678 return exec.psb_blocks.empty() ? exec.thread_execution.GetLowestKnownTSC()
679 : exec.psb_blocks.front().tsc;
680 };
681
682 return get_tsc(*this) < get_tsc(o);
683}
684
685Expected<std::vector<PSBBlock>>
687 TraceIntelPT &trace_intel_pt, llvm::ArrayRef<uint8_t> buffer,
688 bool expect_tscs) {
689 // This follows
690 // https://github.com/intel/libipt/blob/master/doc/howto_libipt.md#parallel-decode
691
692 Expected<PtQueryDecoderUP> decoder_up =
693 CreateQueryDecoder(trace_intel_pt, buffer);
694 if (!decoder_up)
695 return decoder_up.takeError();
696
697 pt_query_decoder *decoder = decoder_up.get().get();
698
699 std::vector<PSBBlock> executions;
700
701 while (true) {
702 uint64_t maybe_ip = LLDB_INVALID_ADDRESS;
703 int decoding_status = pt_qry_sync_forward(decoder, &maybe_ip);
704 if (IsLibiptError(decoding_status))
705 break;
706
707 uint64_t psb_offset;
708 int offset_status = pt_qry_get_sync_offset(decoder, &psb_offset);
709 assert(offset_status >= 0 &&
710 "This can't fail because we were able to synchronize");
711
712 std::optional<uint64_t> ip;
713 if (!(pts_ip_suppressed & decoding_status))
714 ip = maybe_ip;
715
716 std::optional<uint64_t> tsc;
717 // Now we fetch the first TSC that comes after the PSB.
718 while (HasEvents(decoding_status)) {
719 pt_event event;
720 decoding_status = pt_qry_event(decoder, &event, sizeof(event));
721 if (IsLibiptError(decoding_status))
722 break;
723 if (event.has_tsc) {
724 tsc = event.tsc;
725 break;
726 }
727 }
728 if (IsLibiptError(decoding_status)) {
729 // We continue to the next PSB. This effectively merges this PSB with the
730 // previous one, and that should be fine because this PSB might be the
731 // direct continuation of the previous thread and it's better to show an
732 // error in the decoded thread than to hide it. If this is the first PSB,
733 // we are okay losing it. Besides that, an error at processing events
734 // means that we wouldn't be able to get any instruction out of it.
735 continue;
736 }
737
738 if (expect_tscs && !tsc)
739 return createStringError(inconvertibleErrorCode(),
740 "Found a PSB without TSC.");
741
742 executions.push_back({
743 psb_offset,
744 tsc,
745 0,
746 ip,
747 });
748 }
749 if (!executions.empty()) {
750 // We now adjust the sizes of each block
751 executions.back().size = buffer.size() - executions.back().psb_offset;
752 for (int i = (int)executions.size() - 2; i >= 0; i--) {
753 executions[i].size =
754 executions[i + 1].psb_offset - executions[i].psb_offset;
755 }
756 }
757 return executions;
758}
759
760Expected<std::optional<uint64_t>>
762 ArrayRef<uint8_t> buffer) {
763 Expected<PtQueryDecoderUP> decoder_up =
764 CreateQueryDecoder(trace_intel_pt, buffer);
765 if (!decoder_up)
766 return decoder_up.takeError();
767
768 pt_query_decoder *decoder = decoder_up.get().get();
769 uint64_t ip = LLDB_INVALID_ADDRESS;
770 int status = pt_qry_sync_forward(decoder, &ip);
771 if (IsLibiptError(status))
772 return std::nullopt;
773
774 while (HasEvents(status)) {
775 pt_event event;
776 status = pt_qry_event(decoder, &event, sizeof(event));
777 if (IsLibiptError(status))
778 return std::nullopt;
779 if (event.has_tsc)
780 return event.tsc;
781 }
782 return std::nullopt;
783}
static llvm::raw_ostream & error(Stream &strm)
static Expected< pt_config > CreateBasicLibiptConfig(TraceIntelPT &trace_intel_pt, ArrayRef< uint8_t > buffer)
Create a basic configuration object limited to a given buffer that can be used for many different dec...
static Expected< PtQueryDecoderUP > CreateQueryDecoder(TraceIntelPT &trace_intel_pt, ArrayRef< uint8_t > buffer)
Create a query decoder for the given buffer.
bool IsEndOfStream(int status)
auto InsnDecoderDeleter
auto QueryDecoderDeleter
std::unique_ptr< pt_query_decoder, decltype(QueryDecoderDeleter)> PtQueryDecoderUP
static Expected< PtInsnDecoderUP > CreateInstructionDecoder(TraceIntelPT &trace_intel_pt, ArrayRef< uint8_t > buffer, Process &process)
Create an instruction decoder for the given buffer and the given process.
static Error SetupMemoryImage(pt_insn_decoder *decoder, Process &process)
Set up the memory image callback for the given decoder.
bool HasEvents(int status)
std::unique_ptr< pt_insn_decoder, decltype(InsnDecoderDeleter)> PtInsnDecoderUP
bool IsLibiptError(int status)
static int ReadProcessMemory(uint8_t *buffer, size_t size, const pt_asid *, uint64_t pc, void *context)
Callback used by libipt for reading the process memory.
llvm::Error Error
Class used to identify anomalies in traces, which should often indicate a fatal error in the trace.
DecodedThread & m_decoded_thread
lldb::addr_t m_last_packet_offset
uint64_t m_extremely_large_decoding_threshold
uint64_t m_infinite_decoding_loop_threshold
uint64_t m_next_infinite_decoding_loop_threshold
PSBBlockAnomalyDetector(pt_insn_decoder &decoder, TraceIntelPT &trace_intel_pt, DecodedThread &decoded_thread)
pt_insn_decoder & m_decoder
uint64_t m_insn_count_at_last_packet_offset
std::optional< uint64_t > TryIdentifyInfiniteLoop()
Class that decodes a raw buffer for a single PSB block using the low level libipt library.
PtInsnDecoderUP m_decoder_up
std::optional< DecodedThread::TSC > m_tsc_upper_bound
void DecodeInstructionsAndEvents(int status)
Decode all the instructions and events of the given PSB block.
bool AppendInstructionAndDetectAnomalies(const pt_insn &insn)
Append an instruction and return false if and only if a serious anomaly has been detected.
Error ProcessPTEventTSC(DecodedThread::TSC tsc)
Process the TSC of a decoded PT event.
DecodedThread & m_decoded_thread
PSBBlockAnomalyDetector m_anomaly_detector
PSBBlockDecoder(PtInsnDecoderUP &&decoder_up, const PSBBlock &psb_block, std::optional< lldb::addr_t > next_block_ip, DecodedThread &decoded_thread, TraceIntelPT &trace_intel_pt, std::optional< DecodedThread::TSC > tsc_upper_bound)
static Expected< PSBBlockDecoder > Create(TraceIntelPT &trace_intel_pt, const PSBBlock &psb_block, ArrayRef< uint8_t > buffer, Process &process, std::optional< lldb::addr_t > next_block_ip, DecodedThread &decoded_thread, std::optional< DecodedThread::TSC > tsc_upper_bound)
int ProcessPTEvents(int status)
Before querying instructions, we need to query the events associated with that instruction,...
std::optional< lldb::addr_t > m_next_block_ip
A plug-in interface definition class for debugging a process.
Definition: Process.h:342
virtual size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, Status &error)
Read of memory from a process.
Definition: Process.cpp:1926
An error handling class.
Definition: Status.h:44
Class holding the instructions and function call hierarchy obtained from decoding a trace,...
Definition: DecodedThread.h:61
lldb::TraceItemKind GetItemKindByIndex(uint64_t item_index) const
void AppendEvent(lldb::TraceEvent)
Append an event.
lldb::addr_t GetInstructionLoadAddress(uint64_t item_index) const
void AppendError(const IntelPTError &error)
Append a decoding error.
void NotifyCPU(lldb::cpu_id_t cpu_id)
Notify this object that a CPU has been seen.
void NotifySyncPoint(lldb::addr_t psb_offset)
Notify this object that a new PSB has been seen.
void AppendCustomError(llvm::StringRef error, bool fatal=false)
Append a custom decoding.
void NotifyTsc(TSC tsc)
Notify this object that a new tsc has been seen.
uint64_t GetItemsCount() const
Get the total number of instruction, errors and events from the decoded trace.
void AppendInstruction(const pt_insn &insn)
Append an instruction.
Class for representing a libipt decoding error.
Definition: DecodedThread.h:25
static PluginProperties & GetGlobalProperties()
Return the global properties for this trace plug-in.
llvm::Expected< pt_cpu > GetCPUInfo()
Get or fetch the cpu information from, for example, /proc/cpuinfo.
#define LLDB_INVALID_ADDRESS
Definition: lldb-defines.h:74
llvm::Expected< std::vector< PSBBlock > > SplitTraceIntoPSBBlock(TraceIntelPT &trace_intel_pt, llvm::ArrayRef< uint8_t > buffer, bool expect_tscs)
Given an intel pt trace, split it in chunks delimited by PSB packets.
llvm::Error DecodeSingleTraceForThread(DecodedThread &decoded_thread, TraceIntelPT &trace_intel_pt, llvm::ArrayRef< uint8_t > buffer)
Decode a raw Intel PT trace for a single thread given in buffer and append the decoded instructions a...
llvm::Error DecodeSystemWideTraceForThread(DecodedThread &decoded_thread, TraceIntelPT &trace_intel_pt, const llvm::DenseMap< lldb::cpu_id_t, llvm::ArrayRef< uint8_t > > &buffers, const std::vector< IntelPTThreadContinousExecution > &executions)
Decode a raw Intel PT trace for a single thread that was collected in a per cpu core basis.
llvm::Expected< std::optional< uint64_t > > FindLowestTSCInTrace(TraceIntelPT &trace_intel_pt, llvm::ArrayRef< uint8_t > buffer)
Find the lowest TSC in the given trace.
A class that represents a running process on the host machine.
const char * toString(AppleArm64ExceptionClass EC)
Definition: SBAddress.h:15
@ eTraceEventDisabledHW
Tracing was disable for some time due to a hardware trigger.
@ eTraceEventDisabledSW
Tracing was disabled for some time due to a software trigger.
@ eTraceItemKindInstruction
uint64_t addr_t
Definition: lldb-types.h:83
Definition: Debugger.h:51
This struct represents a continuous execution of a thread in a cpu, delimited by a context switch in ...
Definition: LibiptDecoder.h:42
bool operator<(const IntelPTThreadContinousExecution &o) const
Comparator by time.
This struct represents a contiguous section of a trace that starts at a PSB and ends right before the...
Definition: LibiptDecoder.h:23
std::optional< uint64_t > tsc
The timestamp associated with the PSB packet above.
Definition: LibiptDecoder.h:29
uint64_t psb_offset
The memory offset of a PSB packet that is a synchronization point for the decoder.
Definition: LibiptDecoder.h:27
uint64_t size
Size in bytes of this block.
Definition: LibiptDecoder.h:31
enum lldb_private::trace_intel_pt::ThreadContinuousExecution::Variant variant