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   return std::make_unique<TraceCursorIntelPT>(m_thread_sp, shared_from_this());
229 }
230 
231 size_t DecodedThread::CalculateApproximateMemoryUsage() const {
232   return sizeof(pt_insn::ip) * m_instruction_ips.size() +
233          sizeof(pt_insn::size) * m_instruction_sizes.size() +
234          sizeof(pt_insn::iclass) * m_instruction_classes.size() +
235          (sizeof(size_t) + sizeof(uint64_t)) * m_instruction_timestamps.size() +
236          m_errors.getMemorySize() + m_events.getMemorySize();
237 }
238 
239 DecodedThread::TscRange::TscRange(std::map<size_t, uint64_t>::const_iterator it,
240                                   const DecodedThread &decoded_thread)
241     : m_it(it), m_decoded_thread(&decoded_thread) {
242   auto next_it = m_it;
243   ++next_it;
244   m_end_index = (next_it == m_decoded_thread->m_instruction_timestamps.end())
245                     ? m_decoded_thread->GetInstructionsCount() - 1
246                     : next_it->first - 1;
247 }
248 
249 size_t DecodedThread::TscRange::GetTsc() const { return m_it->second; }
250 
251 size_t DecodedThread::TscRange::GetStartInstructionIndex() const {
252   return m_it->first;
253 }
254 
255 size_t DecodedThread::TscRange::GetEndInstructionIndex() const {
256   return m_end_index;
257 }
258 
259 bool DecodedThread::TscRange::InRange(size_t insn_index) const {
260   return GetStartInstructionIndex() <= insn_index &&
261          insn_index <= GetEndInstructionIndex();
262 }
263 
264 Optional<DecodedThread::TscRange> DecodedThread::TscRange::Next() const {
265   auto next_it = m_it;
266   ++next_it;
267   if (next_it == m_decoded_thread->m_instruction_timestamps.end())
268     return None;
269   return TscRange(next_it, *m_decoded_thread);
270 }
271 
272 Optional<DecodedThread::TscRange> DecodedThread::TscRange::Prev() const {
273   if (m_it == m_decoded_thread->m_instruction_timestamps.begin())
274     return None;
275   auto prev_it = m_it;
276   --prev_it;
277   return TscRange(prev_it, *m_decoded_thread);
278 }
279