xref: /llvm-project/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp (revision 059f39d2f44503862cb9c752c28a3a77275b0e51)
1 //===-- DecodedThread.cpp -------------------------------------------------===//
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 "DecodedThread.h"
10 
11 #include <intel-pt.h>
12 
13 #include "TraceCursorIntelPT.h"
14 
15 #include <memory>
16 
17 using namespace lldb;
18 using namespace lldb_private;
19 using namespace lldb_private::trace_intel_pt;
20 using namespace llvm;
21 
22 bool lldb_private::trace_intel_pt::IsLibiptError(int libipt_status) {
23   return libipt_status < 0;
24 }
25 
26 bool lldb_private::trace_intel_pt::IsEndOfStream(int libipt_status) {
27   return libipt_status == -pte_eos;
28 }
29 
30 bool lldb_private::trace_intel_pt::IsTscUnavailable(int libipt_status) {
31   return libipt_status == -pte_no_time;
32 }
33 
34 char IntelPTError::ID;
35 
36 IntelPTError::IntelPTError(int libipt_error_code, lldb::addr_t address)
37     : m_libipt_error_code(libipt_error_code), m_address(address) {
38   assert(libipt_error_code < 0);
39 }
40 
41 void IntelPTError::log(llvm::raw_ostream &OS) const {
42   const char *libipt_error_message = pt_errstr(pt_errcode(m_libipt_error_code));
43   if (m_address != LLDB_INVALID_ADDRESS && m_address > 0) {
44     write_hex(OS, m_address, HexPrintStyle::PrefixLower, 18);
45     OS << "    ";
46   }
47   OS << "error: " << libipt_error_message;
48 }
49 
50 DecodedInstruction::operator bool() const {
51   return !IsLibiptError(libipt_error);
52 }
53 
54 size_t DecodedThread::GetInstructionsCount() const {
55   return m_instruction_ips.size();
56 }
57 
58 lldb::addr_t DecodedThread::GetInstructionLoadAddress(size_t insn_index) const {
59   return m_instruction_ips[insn_index];
60 }
61 
62 TraceInstructionControlFlowType
63 DecodedThread::GetInstructionControlFlowType(size_t insn_index) const {
64   if (IsInstructionAnError(insn_index))
65     return (TraceInstructionControlFlowType)0;
66 
67   TraceInstructionControlFlowType mask =
68       eTraceInstructionControlFlowTypeInstruction;
69 
70   lldb::addr_t load_address = m_instruction_ips[insn_index];
71   uint8_t insn_byte_size = m_instruction_sizes[insn_index];
72   pt_insn_class iclass = m_instruction_classes[insn_index];
73 
74   switch (iclass) {
75   case ptic_cond_jump:
76   case ptic_jump:
77   case ptic_far_jump:
78     mask |= eTraceInstructionControlFlowTypeBranch;
79     if (insn_index + 1 < m_instruction_ips.size() &&
80         load_address + insn_byte_size != m_instruction_ips[insn_index + 1])
81       mask |= eTraceInstructionControlFlowTypeTakenBranch;
82     break;
83   case ptic_return:
84   case ptic_far_return:
85     mask |= eTraceInstructionControlFlowTypeReturn;
86     break;
87   case ptic_call:
88   case ptic_far_call:
89     mask |= eTraceInstructionControlFlowTypeCall;
90     break;
91   default:
92     break;
93   }
94 
95   return mask;
96 }
97 
98 ThreadSP DecodedThread::GetThread() { return m_thread_sp; }
99 
100 void DecodedThread::RecordTscForLastInstruction(uint64_t tsc) {
101   if (!m_last_tsc || *m_last_tsc != tsc) {
102     // In case the first instructions are errors or did not have a TSC, we'll
103     // get a first valid TSC not in position 0. We can safely force these error
104     // instructions to use the first valid TSC, so that all the trace has TSCs.
105     size_t start_index =
106         m_instruction_timestamps.empty() ? 0 : m_instruction_ips.size() - 1;
107     m_instruction_timestamps.emplace(start_index, tsc);
108     m_last_tsc = tsc;
109   }
110 }
111 
112 void DecodedThread::Append(const DecodedInstruction &insn) {
113   if (!insn) {
114     // End of stream shouldn't be a public error
115     if (IsEndOfStream(insn.libipt_error))
116       return;
117 
118     AppendError(make_error<IntelPTError>(insn.libipt_error, insn.pt_insn.ip));
119   } else {
120     m_instruction_ips.emplace_back(insn.pt_insn.ip);
121     m_instruction_sizes.emplace_back(insn.pt_insn.size);
122     m_instruction_classes.emplace_back(insn.pt_insn.iclass);
123   }
124 
125   if (insn.tsc)
126     RecordTscForLastInstruction(*insn.tsc);
127 
128   if (insn.events) {
129     m_events.try_emplace(m_instruction_ips.size() - 1, insn.events);
130     m_events_stats.RecordEventsForInstruction(insn.events);
131   }
132 }
133 
134 void DecodedThread::AppendError(llvm::Error &&error) {
135   m_errors.try_emplace(m_instruction_ips.size(), toString(std::move(error)));
136   m_instruction_ips.emplace_back(LLDB_INVALID_ADDRESS);
137   m_instruction_sizes.emplace_back(0);
138   m_instruction_classes.emplace_back(pt_insn_class::ptic_error);
139 }
140 
141 void DecodedThread::SetAsFailed(llvm::Error &&error) {
142   AppendError(std::move(error));
143 }
144 
145 lldb::TraceEvents DecodedThread::GetEvents(int insn_index) {
146   auto it = m_events.find(insn_index);
147   if (it != m_events.end())
148     return it->second;
149   return (TraceEvents)0;
150 }
151 
152 void DecodedThread::LibiptErrorsStats::RecordError(int libipt_error_code) {
153   libipt_errors_counts[pt_errstr(pt_errcode(libipt_error_code))]++;
154   total_count++;
155 }
156 
157 void DecodedThread::RecordTscError(int libipt_error_code) {
158   m_tsc_errors_stats.RecordError(libipt_error_code);
159 }
160 
161 const DecodedThread::LibiptErrorsStats &
162 DecodedThread::GetTscErrorsStats() const {
163   return m_tsc_errors_stats;
164 }
165 
166 const DecodedThread::EventsStats &DecodedThread::GetEventsStats() const {
167   return m_events_stats;
168 }
169 
170 void DecodedThread::EventsStats::RecordEventsForInstruction(
171     lldb::TraceEvents events) {
172   if (!events)
173     return;
174 
175   total_instructions_with_events++;
176   trace_event_utils::ForEachEvent(events, [&](TraceEvents event) {
177     events_counts[event]++;
178     total_count++;
179   });
180 }
181 
182 Optional<DecodedThread::TscRange> DecodedThread::CalculateTscRange(
183     size_t insn_index,
184     const Optional<DecodedThread::TscRange> &hint_range) const {
185   // We first try to check the given hint range in case we are traversing the
186   // trace in short jumps. If that fails, then we do the more expensive
187   // arbitrary lookup.
188   if (hint_range) {
189     Optional<TscRange> candidate_range;
190     if (insn_index < hint_range->GetStartInstructionIndex())
191       candidate_range = hint_range->Prev();
192     else if (insn_index > hint_range->GetEndInstructionIndex())
193       candidate_range = hint_range->Next();
194     else
195       candidate_range = hint_range;
196 
197     if (candidate_range && candidate_range->InRange(insn_index))
198       return candidate_range;
199   }
200   // Now we do a more expensive lookup
201   auto it = m_instruction_timestamps.upper_bound(insn_index);
202   if (it == m_instruction_timestamps.begin())
203     return None;
204 
205   return TscRange(--it, *this);
206 }
207 
208 bool DecodedThread::IsInstructionAnError(size_t insn_idx) const {
209   return m_instruction_ips[insn_idx] == LLDB_INVALID_ADDRESS;
210 }
211 
212 const char *DecodedThread::GetErrorByInstructionIndex(size_t insn_idx) {
213   auto it = m_errors.find(insn_idx);
214   if (it == m_errors.end())
215     return nullptr;
216 
217   return it->second.c_str();
218 }
219 
220 DecodedThread::DecodedThread(ThreadSP thread_sp) : m_thread_sp(thread_sp) {}
221 
222 DecodedThread::DecodedThread(ThreadSP thread_sp, Error &&error)
223     : m_thread_sp(thread_sp) {
224   AppendError(std::move(error));
225 }
226 
227 lldb::TraceCursorUP DecodedThread::GetCursor() {
228   // We insert a fake error signaling an empty trace if needed becasue the
229   // TraceCursor requires non-empty traces.
230   if (m_instruction_ips.empty())
231     AppendError(createStringError(inconvertibleErrorCode(), "empty trace"));
232   return std::make_unique<TraceCursorIntelPT>(m_thread_sp, shared_from_this());
233 }
234 
235 size_t DecodedThread::CalculateApproximateMemoryUsage() const {
236   return sizeof(pt_insn::ip) * m_instruction_ips.size() +
237          sizeof(pt_insn::size) * m_instruction_sizes.size() +
238          sizeof(pt_insn::iclass) * m_instruction_classes.size() +
239          (sizeof(size_t) + sizeof(uint64_t)) * m_instruction_timestamps.size() +
240          m_errors.getMemorySize() + m_events.getMemorySize();
241 }
242 
243 DecodedThread::TscRange::TscRange(std::map<size_t, uint64_t>::const_iterator it,
244                                   const DecodedThread &decoded_thread)
245     : m_it(it), m_decoded_thread(&decoded_thread) {
246   auto next_it = m_it;
247   ++next_it;
248   m_end_index = (next_it == m_decoded_thread->m_instruction_timestamps.end())
249                     ? m_decoded_thread->GetInstructionsCount() - 1
250                     : next_it->first - 1;
251 }
252 
253 size_t DecodedThread::TscRange::GetTsc() const { return m_it->second; }
254 
255 size_t DecodedThread::TscRange::GetStartInstructionIndex() const {
256   return m_it->first;
257 }
258 
259 size_t DecodedThread::TscRange::GetEndInstructionIndex() const {
260   return m_end_index;
261 }
262 
263 bool DecodedThread::TscRange::InRange(size_t insn_index) const {
264   return GetStartInstructionIndex() <= insn_index &&
265          insn_index <= GetEndInstructionIndex();
266 }
267 
268 Optional<DecodedThread::TscRange> DecodedThread::TscRange::Next() const {
269   auto next_it = m_it;
270   ++next_it;
271   if (next_it == m_decoded_thread->m_instruction_timestamps.end())
272     return None;
273   return TscRange(next_it, *m_decoded_thread);
274 }
275 
276 Optional<DecodedThread::TscRange> DecodedThread::TscRange::Prev() const {
277   if (m_it == m_decoded_thread->m_instruction_timestamps.begin())
278     return None;
279   auto prev_it = m_it;
280   --prev_it;
281   return TscRange(prev_it, *m_decoded_thread);
282 }
283