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 char IntelPTError::ID;
23 
24 IntelPTError::IntelPTError(int libipt_error_code, lldb::addr_t address)
25     : m_libipt_error_code(libipt_error_code), m_address(address) {
26   assert(libipt_error_code < 0);
27 }
28 
29 void IntelPTError::log(llvm::raw_ostream &OS) const {
30   const char *libipt_error_message = pt_errstr(pt_errcode(m_libipt_error_code));
31   if (m_address != LLDB_INVALID_ADDRESS && m_address > 0) {
32     write_hex(OS, m_address, HexPrintStyle::PrefixLower, 18);
33     OS << "    ";
34   }
35   OS << "error: " << libipt_error_message;
36 }
37 
38 Optional<size_t> DecodedThread::GetRawTraceSize() const {
39   return m_raw_trace_size;
40 }
41 
42 size_t DecodedThread::GetInstructionsCount() const {
43   return m_instruction_ips.size();
44 }
45 
46 lldb::addr_t DecodedThread::GetInstructionLoadAddress(size_t insn_index) const {
47   return m_instruction_ips[insn_index];
48 }
49 
50 TraceInstructionControlFlowType
51 DecodedThread::GetInstructionControlFlowType(size_t insn_index) const {
52   if (IsInstructionAnError(insn_index))
53     return (TraceInstructionControlFlowType)0;
54 
55   TraceInstructionControlFlowType mask =
56       eTraceInstructionControlFlowTypeInstruction;
57 
58   lldb::addr_t load_address = m_instruction_ips[insn_index];
59   uint8_t insn_byte_size = m_instruction_sizes[insn_index];
60   pt_insn_class iclass = m_instruction_classes[insn_index];
61 
62   switch (iclass) {
63   case ptic_cond_jump:
64   case ptic_jump:
65   case ptic_far_jump:
66     mask |= eTraceInstructionControlFlowTypeBranch;
67     if (insn_index + 1 < m_instruction_ips.size() &&
68         load_address + insn_byte_size != m_instruction_ips[insn_index + 1])
69       mask |= eTraceInstructionControlFlowTypeTakenBranch;
70     break;
71   case ptic_return:
72   case ptic_far_return:
73     mask |= eTraceInstructionControlFlowTypeReturn;
74     break;
75   case ptic_call:
76   case ptic_far_call:
77     mask |= eTraceInstructionControlFlowTypeCall;
78     break;
79   default:
80     break;
81   }
82 
83   return mask;
84 }
85 
86 ThreadSP DecodedThread::GetThread() { return m_thread_sp; }
87 
88 void DecodedThread::RecordTscForLastInstruction(uint64_t tsc) {
89   if (!m_last_tsc || *m_last_tsc != tsc) {
90     // In case the first instructions are errors or did not have a TSC, we'll
91     // get a first valid TSC not in position 0. We can safely force these error
92     // instructions to use the first valid TSC, so that all the trace has TSCs.
93     size_t start_index =
94         m_instruction_timestamps.empty() ? 0 : m_instruction_ips.size() - 1;
95     m_instruction_timestamps.emplace(start_index, tsc);
96     m_last_tsc = tsc;
97   }
98 }
99 
100 void DecodedThread::AppendInstruction(const pt_insn &insn) {
101   m_instruction_ips.emplace_back(insn.ip);
102   m_instruction_sizes.emplace_back(insn.size);
103   m_instruction_classes.emplace_back(insn.iclass);
104 }
105 
106 void DecodedThread::AppendInstruction(const pt_insn &insn, uint64_t tsc) {
107   AppendInstruction(insn);
108   RecordTscForLastInstruction(tsc);
109 }
110 
111 void DecodedThread::AppendError(llvm::Error &&error) {
112   m_errors.try_emplace(m_instruction_ips.size(), toString(std::move(error)));
113   m_instruction_ips.emplace_back(LLDB_INVALID_ADDRESS);
114   m_instruction_sizes.emplace_back(0);
115   m_instruction_classes.emplace_back(pt_insn_class::ptic_error);
116 }
117 
118 void DecodedThread::AppendError(llvm::Error &&error, uint64_t tsc) {
119   AppendError(std::move(error));
120   RecordTscForLastInstruction(tsc);
121 }
122 
123 void DecodedThread::LibiptErrors::RecordError(int libipt_error_code) {
124   libipt_errors[pt_errstr(pt_errcode(libipt_error_code))]++;
125   total_count++;
126 }
127 
128 void DecodedThread::RecordTscError(int libipt_error_code) {
129   m_tsc_errors.RecordError(libipt_error_code);
130 }
131 
132 const DecodedThread::LibiptErrors &DecodedThread::GetTscErrors() const {
133   return m_tsc_errors;
134 }
135 
136 Optional<DecodedThread::TscRange> DecodedThread::CalculateTscRange(
137     size_t insn_index,
138     const Optional<DecodedThread::TscRange> &hint_range) const {
139   // We first try to check the given hint range in case we are traversing the
140   // trace in short jumps. If that fails, then we do the more expensive
141   // arbitrary lookup.
142   if (hint_range) {
143     Optional<TscRange> candidate_range;
144     if (insn_index < hint_range->GetStartInstructionIndex())
145       candidate_range = hint_range->Prev();
146     else if (insn_index > hint_range->GetEndInstructionIndex())
147       candidate_range = hint_range->Next();
148     else
149       candidate_range = hint_range;
150 
151     if (candidate_range && candidate_range->InRange(insn_index))
152       return candidate_range;
153   }
154   // Now we do a more expensive lookup
155   auto it = m_instruction_timestamps.upper_bound(insn_index);
156   if (it == m_instruction_timestamps.begin())
157     return None;
158 
159   return TscRange(--it, *this);
160 }
161 
162 bool DecodedThread::IsInstructionAnError(size_t insn_idx) const {
163   return m_instruction_ips[insn_idx] == LLDB_INVALID_ADDRESS;
164 }
165 
166 const char *DecodedThread::GetErrorByInstructionIndex(size_t insn_idx) {
167   auto it = m_errors.find(insn_idx);
168   if (it == m_errors.end())
169     return nullptr;
170 
171   return it->second.c_str();
172 }
173 
174 DecodedThread::DecodedThread(ThreadSP thread_sp) : m_thread_sp(thread_sp) {}
175 
176 DecodedThread::DecodedThread(ThreadSP thread_sp, Error &&error)
177     : m_thread_sp(thread_sp) {
178   AppendError(std::move(error));
179 }
180 
181 void DecodedThread::SetRawTraceSize(size_t size) { m_raw_trace_size = size; }
182 
183 lldb::TraceCursorUP DecodedThread::GetCursor() {
184   // We insert a fake error signaling an empty trace if needed becasue the
185   // TraceCursor requires non-empty traces.
186   if (m_instruction_ips.empty())
187     AppendError(createStringError(inconvertibleErrorCode(), "empty trace"));
188   return std::make_unique<TraceCursorIntelPT>(m_thread_sp, shared_from_this());
189 }
190 
191 size_t DecodedThread::CalculateApproximateMemoryUsage() const {
192   return sizeof(pt_insn::ip) * m_instruction_ips.size() +
193          sizeof(pt_insn::size) * m_instruction_sizes.size() +
194          sizeof(pt_insn::iclass) * m_instruction_classes.size() +
195          (sizeof(size_t) + sizeof(uint64_t)) * m_instruction_timestamps.size() +
196          m_errors.getMemorySize();
197 }
198 
199 DecodedThread::TscRange::TscRange(std::map<size_t, uint64_t>::const_iterator it,
200                                   const DecodedThread &decoded_thread)
201     : m_it(it), m_decoded_thread(&decoded_thread) {
202   auto next_it = m_it;
203   ++next_it;
204   m_end_index = (next_it == m_decoded_thread->m_instruction_timestamps.end())
205                     ? m_decoded_thread->GetInstructionsCount() - 1
206                     : next_it->first - 1;
207 }
208 
209 size_t DecodedThread::TscRange::GetTsc() const { return m_it->second; }
210 
211 size_t DecodedThread::TscRange::GetStartInstructionIndex() const {
212   return m_it->first;
213 }
214 
215 size_t DecodedThread::TscRange::GetEndInstructionIndex() const {
216   return m_end_index;
217 }
218 
219 bool DecodedThread::TscRange::InRange(size_t insn_index) const {
220   return GetStartInstructionIndex() <= insn_index &&
221          insn_index <= GetEndInstructionIndex();
222 }
223 
224 Optional<DecodedThread::TscRange> DecodedThread::TscRange::Next() const {
225   auto next_it = m_it;
226   ++next_it;
227   if (next_it == m_decoded_thread->m_instruction_timestamps.end())
228     return None;
229   return TscRange(next_it, *m_decoded_thread);
230 }
231 
232 Optional<DecodedThread::TscRange> DecodedThread::TscRange::Prev() const {
233   if (m_it == m_decoded_thread->m_instruction_timestamps.begin())
234     return None;
235   auto prev_it = m_it;
236   --prev_it;
237   return TscRange(prev_it, *m_decoded_thread);
238 }
239