• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===-- IntelPTDecoder.cpp --------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "IntelPTDecoder.h"
10 
11 #include "llvm/Support/MemoryBuffer.h"
12 
13 #include "lldb/Core/Module.h"
14 #include "lldb/Core/Section.h"
15 #include "lldb/Target/Target.h"
16 #include "lldb/Target/ThreadTrace.h"
17 
18 using namespace lldb;
19 using namespace lldb_private;
20 using namespace lldb_private::trace_intel_pt;
21 using namespace llvm;
22 
23 /// Move the decoder forward to the next synchronization point (i.e. next PSB
24 /// packet).
25 ///
26 /// Once the decoder is at that sync. point, it can start decoding instructions.
27 ///
28 /// \return
29 ///   A negative number with the libipt error if we couldn't synchronize.
30 ///   Otherwise, a positive number with the synchronization status will be
31 ///   returned.
FindNextSynchronizationPoint(pt_insn_decoder & decoder)32 static int FindNextSynchronizationPoint(pt_insn_decoder &decoder) {
33   // Try to sync the decoder. If it fails, then get
34   // the decoder_offset and try to sync again from
35   // the next synchronization point. If the
36   // new_decoder_offset is same as decoder_offset
37   // then we can't move to the next synchronization
38   // point. Otherwise, keep resyncing until either
39   // end of trace stream (eos) is reached or
40   // pt_insn_sync_forward() passes.
41   int errcode = pt_insn_sync_forward(&decoder);
42 
43   if (errcode != -pte_eos && errcode < 0) {
44     uint64_t decoder_offset = 0;
45     int errcode_off = pt_insn_get_offset(&decoder, &decoder_offset);
46     if (errcode_off >= 0) { // we could get the offset
47       while (true) {
48         errcode = pt_insn_sync_forward(&decoder);
49         if (errcode >= 0 || errcode == -pte_eos)
50           break;
51 
52         uint64_t new_decoder_offset = 0;
53         errcode_off = pt_insn_get_offset(&decoder, &new_decoder_offset);
54         if (errcode_off < 0)
55           break; // We can't further synchronize.
56         else if (new_decoder_offset <= decoder_offset) {
57           // We tried resyncing the decoder and
58           // decoder didn't make any progress because
59           // the offset didn't change. We will not
60           // make any progress further. Hence,
61           // stopping in this situation.
62           break;
63         }
64         // We'll try again starting from a new offset.
65         decoder_offset = new_decoder_offset;
66       }
67     }
68   }
69 
70   return errcode;
71 }
72 
73 /// Before querying instructions, we need to query the events associated that
74 /// instruction e.g. timing events like ptev_tick, or paging events like
75 /// ptev_paging.
76 ///
77 /// \return
78 ///   0 if there were no errors processing the events, or a negative libipt
79 ///   error code in case of errors.
ProcessPTEvents(pt_insn_decoder & decoder,int errcode)80 static int ProcessPTEvents(pt_insn_decoder &decoder, int errcode) {
81   while (errcode & pts_event_pending) {
82     pt_event event;
83     errcode = pt_insn_event(&decoder, &event, sizeof(event));
84     if (errcode < 0)
85       return errcode;
86   }
87   return 0;
88 };
89 
90 /// Decode all the instructions from a configured decoder.
91 /// The decoding flow is based on
92 /// https://github.com/intel/libipt/blob/master/doc/howto_libipt.md#the-instruction-flow-decode-loop
93 /// but with some relaxation to allow for gaps in the trace.
94 ///
95 /// Error codes returned by libipt while decoding are:
96 /// - negative: actual errors
97 /// - positive or zero: not an error, but a list of bits signaling the status of
98 /// the decoder
99 ///
100 /// \param[in] decoder
101 ///   A configured libipt \a pt_insn_decoder.
102 ///
103 /// \return
104 ///   The decoded instructions.
105 static std::vector<IntelPTInstruction>
DecodeInstructions(pt_insn_decoder & decoder)106 DecodeInstructions(pt_insn_decoder &decoder) {
107   std::vector<IntelPTInstruction> instructions;
108 
109   while (true) {
110     int errcode = FindNextSynchronizationPoint(decoder);
111     if (errcode == -pte_eos)
112       break;
113 
114     if (errcode < 0) {
115       instructions.emplace_back(make_error<IntelPTError>(errcode));
116       break;
117     }
118 
119     // We have synchronized, so we can start decoding
120     // instructions and events.
121     while (true) {
122       errcode = ProcessPTEvents(decoder, errcode);
123       if (errcode < 0) {
124         instructions.emplace_back(make_error<IntelPTError>(errcode));
125         break;
126       }
127       pt_insn insn;
128 
129       errcode = pt_insn_next(&decoder, &insn, sizeof(insn));
130       if (errcode == -pte_eos)
131         break;
132 
133       if (errcode < 0) {
134         instructions.emplace_back(make_error<IntelPTError>(errcode, insn.ip));
135         break;
136       }
137 
138       instructions.emplace_back(insn);
139     }
140   }
141 
142   return instructions;
143 }
144 
145 /// Callback used by libipt for reading the process memory.
146 ///
147 /// More information can be found in
148 /// https://github.com/intel/libipt/blob/master/doc/man/pt_image_set_callback.3.md
ReadProcessMemory(uint8_t * buffer,size_t size,const pt_asid *,uint64_t pc,void * context)149 static int ReadProcessMemory(uint8_t *buffer, size_t size,
150                              const pt_asid * /* unused */, uint64_t pc,
151                              void *context) {
152   Process *process = static_cast<Process *>(context);
153 
154   Status error;
155   int bytes_read = process->ReadMemory(pc, buffer, size, error);
156   if (error.Fail())
157     return -pte_nomap;
158   return bytes_read;
159 }
160 
makeInstructionListFromError(Error err)161 static std::vector<IntelPTInstruction> makeInstructionListFromError(Error err) {
162   std::vector<IntelPTInstruction> instructions;
163   instructions.emplace_back(std::move(err));
164   return instructions;
165 }
166 
167 static std::vector<IntelPTInstruction>
CreateDecoderAndDecode(Process & process,const pt_cpu & pt_cpu,const FileSpec & trace_file)168 CreateDecoderAndDecode(Process &process, const pt_cpu &pt_cpu,
169                        const FileSpec &trace_file) {
170   ErrorOr<std::unique_ptr<MemoryBuffer>> trace_or_error =
171       MemoryBuffer::getFile(trace_file.GetPath());
172   if (std::error_code err = trace_or_error.getError())
173     return makeInstructionListFromError(errorCodeToError(err));
174 
175   MemoryBuffer &trace = **trace_or_error;
176 
177   pt_config config;
178   pt_config_init(&config);
179   config.cpu = pt_cpu;
180 
181   if (int errcode = pt_cpu_errata(&config.errata, &config.cpu))
182     return makeInstructionListFromError(make_error<IntelPTError>(errcode));
183 
184   // The libipt library does not modify the trace buffer, hence the following
185   // cast is safe.
186   config.begin =
187       reinterpret_cast<uint8_t *>(const_cast<char *>(trace.getBufferStart()));
188   config.end =
189       reinterpret_cast<uint8_t *>(const_cast<char *>(trace.getBufferEnd()));
190 
191   pt_insn_decoder *decoder = pt_insn_alloc_decoder(&config);
192   if (!decoder)
193     return makeInstructionListFromError(make_error<IntelPTError>(-pte_nomem));
194 
195   pt_image *image = pt_insn_get_image(decoder);
196 
197   int errcode = pt_image_set_callback(image, ReadProcessMemory, &process);
198   assert(errcode == 0);
199   (void)errcode;
200 
201   std::vector<IntelPTInstruction> instructions = DecodeInstructions(*decoder);
202 
203   pt_insn_free_decoder(decoder);
204   return instructions;
205 }
206 
Decode()207 const DecodedThread &ThreadTraceDecoder::Decode() {
208   if (!m_decoded_thread.hasValue()) {
209     m_decoded_thread = DecodedThread(
210         CreateDecoderAndDecode(*m_trace_thread->GetProcess(), m_pt_cpu,
211                                m_trace_thread->GetTraceFile()));
212   }
213 
214   return *m_decoded_thread;
215 }
216