LLDB  mainline
IntelPTDecoder.cpp
Go to the documentation of this file.
1 //===-- IntelPTDecoder.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 "IntelPTDecoder.h"
9 
10 #include "llvm/Support/MemoryBuffer.h"
11 
12 #include "../common/ThreadPostMortemTrace.h"
13 #include "DecodedThread.h"
14 #include "TraceIntelPT.h"
15 #include "lldb/Core/Module.h"
16 #include "lldb/Core/Section.h"
17 #include "lldb/Target/Target.h"
19 
20 using namespace lldb;
21 using namespace lldb_private;
22 using namespace lldb_private::trace_intel_pt;
23 using namespace llvm;
24 
25 /// Move the decoder forward to the next synchronization point (i.e. next PSB
26 /// packet).
27 ///
28 /// Once the decoder is at that sync. point, it can start decoding instructions.
29 ///
30 /// \return
31 /// A negative number with the libipt error if we couldn't synchronize.
32 /// Otherwise, a positive number with the synchronization status will be
33 /// returned.
34 static int FindNextSynchronizationPoint(pt_insn_decoder &decoder) {
35  // Try to sync the decoder. If it fails, then get
36  // the decoder_offset and try to sync again from
37  // the next synchronization point. If the
38  // new_decoder_offset is same as decoder_offset
39  // then we can't move to the next synchronization
40  // point. Otherwise, keep resyncing until either
41  // end of trace stream (eos) is reached or
42  // pt_insn_sync_forward() passes.
43  int errcode = pt_insn_sync_forward(&decoder);
44 
45  if (errcode != -pte_eos && errcode < 0) {
46  uint64_t decoder_offset = 0;
47  int errcode_off = pt_insn_get_offset(&decoder, &decoder_offset);
48  if (errcode_off >= 0) { // we could get the offset
49  while (true) {
50  errcode = pt_insn_sync_forward(&decoder);
51  if (errcode >= 0 || errcode == -pte_eos)
52  break;
53 
54  uint64_t new_decoder_offset = 0;
55  errcode_off = pt_insn_get_offset(&decoder, &new_decoder_offset);
56  if (errcode_off < 0)
57  break; // We can't further synchronize.
58  else if (new_decoder_offset <= decoder_offset) {
59  // We tried resyncing the decoder and
60  // decoder didn't make any progress because
61  // the offset didn't change. We will not
62  // make any progress further. Hence,
63  // stopping in this situation.
64  break;
65  }
66  // We'll try again starting from a new offset.
67  decoder_offset = new_decoder_offset;
68  }
69  }
70  }
71 
72  return errcode;
73 }
74 
75 /// Before querying instructions, we need to query the events associated that
76 /// instruction e.g. timing events like ptev_tick, or paging events like
77 /// ptev_paging.
78 ///
79 /// \return
80 /// 0 if there were no errors processing the events, or a negative libipt
81 /// error code in case of errors.
82 static int ProcessPTEvents(pt_insn_decoder &decoder, int errcode) {
83  while (errcode & pts_event_pending) {
84  pt_event event;
85  errcode = pt_insn_event(&decoder, &event, sizeof(event));
86  if (errcode < 0)
87  return errcode;
88  }
89  return 0;
90 }
91 
92 /// Decode all the instructions from a configured decoder.
93 /// The decoding flow is based on
94 /// https://github.com/intel/libipt/blob/master/doc/howto_libipt.md#the-instruction-flow-decode-loop
95 /// but with some relaxation to allow for gaps in the trace.
96 ///
97 /// Error codes returned by libipt while decoding are:
98 /// - negative: actual errors
99 /// - positive or zero: not an error, but a list of bits signaling the status of
100 /// the decoder
101 ///
102 /// \param[in] decoder
103 /// A configured libipt \a pt_insn_decoder.
104 ///
105 /// \return
106 /// The decoded instructions.
107 static std::vector<IntelPTInstruction>
108 DecodeInstructions(pt_insn_decoder &decoder) {
109  std::vector<IntelPTInstruction> instructions;
110 
111  while (true) {
112  int errcode = FindNextSynchronizationPoint(decoder);
113  if (errcode == -pte_eos)
114  break;
115 
116  if (errcode < 0) {
117  instructions.emplace_back(make_error<IntelPTError>(errcode));
118  break;
119  }
120 
121  // We have synchronized, so we can start decoding
122  // instructions and events.
123  while (true) {
124  errcode = ProcessPTEvents(decoder, errcode);
125  if (errcode < 0) {
126  instructions.emplace_back(make_error<IntelPTError>(errcode));
127  break;
128  }
129  pt_insn insn;
130 
131  errcode = pt_insn_next(&decoder, &insn, sizeof(insn));
132  if (errcode == -pte_eos)
133  break;
134 
135  if (errcode < 0) {
136  instructions.emplace_back(make_error<IntelPTError>(errcode, insn.ip));
137  break;
138  }
139 
140  uint64_t time;
141  int time_error = pt_insn_time(&decoder, &time, nullptr, nullptr);
142  if (time_error == -pte_invalid) {
143  // This happens if we invoke the pt_insn_time method incorrectly,
144  // but the instruction is good though.
145  instructions.emplace_back(
146  make_error<IntelPTError>(time_error, insn.ip));
147  instructions.emplace_back(insn);
148  break;
149  }
150  if (time_error == -pte_no_time) {
151  // We simply don't have time information, i.e. None of TSC, MTC or CYC
152  // was enabled.
153  instructions.emplace_back(insn);
154  } else {
155  instructions.emplace_back(insn, time);
156  }
157  }
158  }
159 
160  return instructions;
161 }
162 
163 /// Callback used by libipt for reading the process memory.
164 ///
165 /// More information can be found in
166 /// https://github.com/intel/libipt/blob/master/doc/man/pt_image_set_callback.3.md
167 static int ReadProcessMemory(uint8_t *buffer, size_t size,
168  const pt_asid * /* unused */, uint64_t pc,
169  void *context) {
170  Process *process = static_cast<Process *>(context);
171 
172  Status error;
173  int bytes_read = process->ReadMemory(pc, buffer, size, error);
174  if (error.Fail())
175  return -pte_nomap;
176  return bytes_read;
177 }
178 
179 static Expected<std::vector<IntelPTInstruction>>
180 DecodeInMemoryTrace(Process &process, TraceIntelPT &trace_intel_pt,
181  MutableArrayRef<uint8_t> buffer) {
182  Expected<pt_cpu> cpu_info = trace_intel_pt.GetCPUInfo();
183  if (!cpu_info)
184  return cpu_info.takeError();
185 
186  pt_config config;
187  pt_config_init(&config);
188  config.cpu = *cpu_info;
189 
190  if (int errcode = pt_cpu_errata(&config.errata, &config.cpu))
191  return make_error<IntelPTError>(errcode);
192 
193  config.begin = buffer.data();
194  config.end = buffer.data() + buffer.size();
195 
196  pt_insn_decoder *decoder = pt_insn_alloc_decoder(&config);
197  if (!decoder)
198  return make_error<IntelPTError>(-pte_nomem);
199 
200  pt_image *image = pt_insn_get_image(decoder);
201 
202  int errcode = pt_image_set_callback(image, ReadProcessMemory, &process);
203  assert(errcode == 0);
204  (void)errcode;
205 
206  std::vector<IntelPTInstruction> instructions = DecodeInstructions(*decoder);
207 
208  pt_insn_free_decoder(decoder);
209  return instructions;
210 }
211 
212 static Expected<std::vector<IntelPTInstruction>>
213 DecodeTraceFile(Process &process, TraceIntelPT &trace_intel_pt,
214  const FileSpec &trace_file, size_t &raw_trace_size) {
215  ErrorOr<std::unique_ptr<MemoryBuffer>> trace_or_error =
216  MemoryBuffer::getFile(trace_file.GetPath());
217  if (std::error_code err = trace_or_error.getError())
218  return errorCodeToError(err);
219 
220  MemoryBuffer &trace = **trace_or_error;
221  MutableArrayRef<uint8_t> trace_data(
222  // The libipt library does not modify the trace buffer, hence the
223  // following cast is safe.
224  reinterpret_cast<uint8_t *>(const_cast<char *>(trace.getBufferStart())),
225  trace.getBufferSize());
226  raw_trace_size = trace_data.size();
227  return DecodeInMemoryTrace(process, trace_intel_pt, trace_data);
228 }
229 
230 static Expected<std::vector<IntelPTInstruction>>
231 DecodeLiveThread(Thread &thread, TraceIntelPT &trace, size_t &raw_trace_size) {
232  Expected<std::vector<uint8_t>> buffer =
233  trace.GetLiveThreadBuffer(thread.GetID());
234  if (!buffer)
235  return buffer.takeError();
236  raw_trace_size = buffer->size();
237  if (Expected<pt_cpu> cpu_info = trace.GetCPUInfo())
238  return DecodeInMemoryTrace(*thread.GetProcess(), trace,
239  MutableArrayRef<uint8_t>(*buffer));
240  else
241  return cpu_info.takeError();
242 }
243 
244 DecodedThreadSP ThreadDecoder::Decode() {
245  if (!m_decoded_thread.hasValue())
246  m_decoded_thread = DoDecode();
247  return *m_decoded_thread;
248 }
249 
250 PostMortemThreadDecoder::PostMortemThreadDecoder(
251  const lldb::ThreadPostMortemTraceSP &trace_thread, TraceIntelPT &trace)
252  : m_trace_thread(trace_thread), m_trace(trace) {}
253 
255  size_t raw_trace_size = 0;
256  if (Expected<std::vector<IntelPTInstruction>> instructions =
257  DecodeTraceFile(*m_trace_thread->GetProcess(), m_trace,
258  m_trace_thread->GetTraceFile(), raw_trace_size))
259  return std::make_shared<DecodedThread>(m_trace_thread->shared_from_this(),
260  std::move(*instructions),
261  raw_trace_size);
262  else
263  return std::make_shared<DecodedThread>(m_trace_thread->shared_from_this(),
264  instructions.takeError());
265 }
266 
268  : m_thread_sp(thread.shared_from_this()), m_trace(trace) {}
269 
271  size_t raw_trace_size = 0;
272  if (Expected<std::vector<IntelPTInstruction>> instructions =
273  DecodeLiveThread(*m_thread_sp, m_trace, raw_trace_size))
274  return std::make_shared<DecodedThread>(
275  m_thread_sp, std::move(*instructions), raw_trace_size);
276  else
277  return std::make_shared<DecodedThread>(m_thread_sp,
278  instructions.takeError());
279 }
lldb_private::trace_intel_pt::PostMortemThreadDecoder::m_trace_thread
lldb::ThreadPostMortemTraceSP m_trace_thread
Definition: IntelPTDecoder.h:64
llvm
Definition: Debugger.h:49
lldb_private::Process::ReadMemory
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
IntelPTDecoder.h
lldb_private::Process
Definition: Process.h:341
Module.h
lldb_private::trace_intel_pt::TraceIntelPT
Definition: TraceIntelPT.h:21
pc
@ pc
Definition: CompactUnwindInfo.cpp:1250
ProcessPTEvents
static int ProcessPTEvents(pt_insn_decoder &decoder, int errcode)
Before querying instructions, we need to query the events associated that instruction e....
Definition: IntelPTDecoder.cpp:82
Section.h
lldb_private::trace_intel_pt::TraceIntelPT::GetLiveThreadBuffer
llvm::Expected< std::vector< uint8_t > > GetLiveThreadBuffer(lldb::tid_t tid)
Get the thread buffer content for a live thread.
Definition: TraceIntelPT.cpp:343
lldb_private::trace_intel_pt::LiveThreadDecoder::LiveThreadDecoder
LiveThreadDecoder(Thread &thread, TraceIntelPT &trace)
Definition: IntelPTDecoder.cpp:267
DecodeTraceFile
static Expected< std::vector< IntelPTInstruction > > DecodeTraceFile(Process &process, TraceIntelPT &trace_intel_pt, const FileSpec &trace_file, size_t &raw_trace_size)
Definition: IntelPTDecoder.cpp:213
Target.h
lldb_private::Thread::GetProcess
lldb::ProcessSP GetProcess() const
Definition: Thread.h:152
lldb_private::FileSpec
Definition: FileSpec.h:56
ReadProcessMemory
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.
Definition: IntelPTDecoder.cpp:167
error
static llvm::raw_ostream & error(Stream &strm)
Definition: CommandReturnObject.cpp:17
lldb_private::trace_intel_pt::LiveThreadDecoder::m_thread_sp
lldb::ThreadSP m_thread_sp
Definition: IntelPTDecoder.h:80
DecodeInMemoryTrace
static Expected< std::vector< IntelPTInstruction > > DecodeInMemoryTrace(Process &process, TraceIntelPT &trace_intel_pt, MutableArrayRef< uint8_t > buffer)
Definition: IntelPTDecoder.cpp:180
lldb_private::Thread
Definition: Thread.h:60
DecodedThread.h
lldb_private::trace_intel_pt::PostMortemThreadDecoder::m_trace
TraceIntelPT & m_trace
Definition: IntelPTDecoder.h:65
DecodeLiveThread
static Expected< std::vector< IntelPTInstruction > > DecodeLiveThread(Thread &thread, TraceIntelPT &trace, size_t &raw_trace_size)
Definition: IntelPTDecoder.cpp:231
DecodeInstructions
static std::vector< IntelPTInstruction > DecodeInstructions(pt_insn_decoder &decoder)
Decode all the instructions from a configured decoder.
Definition: IntelPTDecoder.cpp:108
lldb_private::UserID::GetID
lldb::user_id_t GetID() const
Get accessor for the user ID.
Definition: UserID.h:47
FindNextSynchronizationPoint
static int FindNextSynchronizationPoint(pt_insn_decoder &decoder)
Move the decoder forward to the next synchronization point (i.e.
Definition: IntelPTDecoder.cpp:34
lldb_private::trace_intel_pt::PostMortemThreadDecoder::DoDecode
DecodedThreadSP DoDecode() override
Decode the thread.
Definition: IntelPTDecoder.cpp:254
lldb_private::Status
Definition: Status.h:44
StringExtractor.h
lldb_private::trace_intel_pt::DecodedThreadSP
std::shared_ptr< DecodedThread > DecodedThreadSP
Definition: DecodedThread.h:159
lldb_private
A class that represents a running process on the host machine.
Definition: SBCommandInterpreterRunOptions.h:16
lldb_private::trace_intel_pt::LiveThreadDecoder::m_trace
TraceIntelPT & m_trace
Definition: IntelPTDecoder.h:81
lldb_private::trace_intel_pt::TraceIntelPT::GetCPUInfo
llvm::Expected< pt_cpu > GetCPUInfo()
Definition: TraceIntelPT.cpp:181
lldb_private::trace_intel_pt
Definition: CommandObjectTraceStartIntelPT.h:18
lldb_private::FileSpec::GetPath
size_t GetPath(char *path, size_t max_path_length, bool denormalize=true) const
Extract the full path to the file.
Definition: FileSpec.cpp:348
lldb
Definition: SBAddress.h:15
lldb_private::trace_intel_pt::LiveThreadDecoder::DoDecode
DecodedThreadSP DoDecode() override
Decode the thread.
Definition: IntelPTDecoder.cpp:270
TraceIntelPT.h