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