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 #include <memory>
13 
14 #include "TraceCursorIntelPT.h"
15 #include "lldb/Utility/StreamString.h"
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 IntelPTInstruction::IntelPTInstruction() {
39   m_pt_insn.ip = LLDB_INVALID_ADDRESS;
40   m_pt_insn.iclass = ptic_error;
41   m_is_error = true;
42 }
43 
44 bool IntelPTInstruction::IsError() const { return m_is_error; }
45 
46 lldb::addr_t IntelPTInstruction::GetLoadAddress() const { return m_pt_insn.ip; }
47 
48 size_t IntelPTInstruction::GetMemoryUsage() {
49   return sizeof(IntelPTInstruction);
50 }
51 
52 Optional<size_t> DecodedThread::GetRawTraceSize() const {
53   return m_raw_trace_size;
54 }
55 
56 TraceInstructionControlFlowType
57 IntelPTInstruction::GetControlFlowType(lldb::addr_t next_load_address) const {
58   if (IsError())
59     return (TraceInstructionControlFlowType)0;
60 
61   TraceInstructionControlFlowType mask =
62       eTraceInstructionControlFlowTypeInstruction;
63 
64   switch (m_pt_insn.iclass) {
65   case ptic_cond_jump:
66   case ptic_jump:
67   case ptic_far_jump:
68     mask |= eTraceInstructionControlFlowTypeBranch;
69     if (m_pt_insn.ip + m_pt_insn.size != next_load_address)
70       mask |= eTraceInstructionControlFlowTypeTakenBranch;
71     break;
72   case ptic_return:
73   case ptic_far_return:
74     mask |= eTraceInstructionControlFlowTypeReturn;
75     break;
76   case ptic_call:
77   case ptic_far_call:
78     mask |= eTraceInstructionControlFlowTypeCall;
79     break;
80   default:
81     break;
82   }
83 
84   return mask;
85 }
86 
87 ThreadSP DecodedThread::GetThread() { return m_thread_sp; }
88 
89 void DecodedThread::AppendInstruction(const pt_insn &insn) {
90   m_instructions.emplace_back(insn);
91 }
92 
93 void DecodedThread::AppendInstruction(const pt_insn &insn, uint64_t tsc) {
94   m_instructions.emplace_back(insn);
95   if (!m_last_tsc || *m_last_tsc != tsc) {
96     // In case the first instructions are errors or did not have a TSC, we'll
97     // get a first valid TSC not in position 0. We can safely force these error
98     // instructions to use the first valid TSC, so that all the trace has TSCs.
99     size_t start_index =
100         m_instruction_timestamps.empty() ? 0 : m_instructions.size() - 1;
101     m_instruction_timestamps.emplace(start_index, tsc);
102     m_last_tsc = tsc;
103   }
104 }
105 
106 void DecodedThread::AppendError(llvm::Error &&error) {
107   m_errors.try_emplace(m_instructions.size(), toString(std::move(error)));
108   m_instructions.emplace_back();
109 }
110 
111 ArrayRef<IntelPTInstruction> DecodedThread::GetInstructions() const {
112   return makeArrayRef(m_instructions);
113 }
114 
115 Optional<DecodedThread::TscRange>
116 DecodedThread::CalculateTscRange(size_t insn_index) const {
117   auto it = m_instruction_timestamps.upper_bound(insn_index);
118   if (it == m_instruction_timestamps.begin())
119     return None;
120 
121   return TscRange(--it, *this);
122 }
123 
124 bool DecodedThread::IsInstructionAnError(size_t insn_idx) const {
125   return m_instructions[insn_idx].IsError();
126 }
127 
128 const char *DecodedThread::GetErrorByInstructionIndex(size_t insn_idx) {
129   auto it = m_errors.find(insn_idx);
130   if (it == m_errors.end())
131     return nullptr;
132 
133   return it->second.c_str();
134 }
135 
136 DecodedThread::DecodedThread(ThreadSP thread_sp) : m_thread_sp(thread_sp) {}
137 
138 DecodedThread::DecodedThread(ThreadSP thread_sp, Error &&error)
139     : m_thread_sp(thread_sp) {
140   AppendError(std::move(error));
141 }
142 
143 void DecodedThread::SetRawTraceSize(size_t size) { m_raw_trace_size = size; }
144 
145 lldb::TraceCursorUP DecodedThread::GetCursor() {
146   // We insert a fake error signaling an empty trace if needed becasue the
147   // TraceCursor requires non-empty traces.
148   if (m_instructions.empty())
149     AppendError(createStringError(inconvertibleErrorCode(), "empty trace"));
150   return std::make_unique<TraceCursorIntelPT>(m_thread_sp, shared_from_this());
151 }
152 
153 size_t DecodedThread::CalculateApproximateMemoryUsage() const {
154   return IntelPTInstruction::GetMemoryUsage() * m_instructions.size() +
155          m_errors.getMemorySize();
156 }
157 
158 DecodedThread::TscRange::TscRange(std::map<size_t, uint64_t>::const_iterator it,
159                                   const DecodedThread &decoded_thread)
160     : m_it(it), m_decoded_thread(&decoded_thread) {
161   auto next_it = m_it;
162   ++next_it;
163   m_end_index = (next_it == m_decoded_thread->m_instruction_timestamps.end())
164                     ? m_decoded_thread->GetInstructions().size() - 1
165                     : next_it->first - 1;
166 }
167 
168 size_t DecodedThread::TscRange::GetTsc() const { return m_it->second; }
169 
170 size_t DecodedThread::TscRange::GetStartInstructionIndex() const {
171   return m_it->first;
172 }
173 
174 size_t DecodedThread::TscRange::GetEndInstructionIndex() const {
175   return m_end_index;
176 }
177 
178 bool DecodedThread::TscRange::InRange(size_t insn_index) {
179   return GetStartInstructionIndex() <= insn_index &&
180          insn_index <= GetEndInstructionIndex();
181 }
182 
183 Optional<DecodedThread::TscRange> DecodedThread::TscRange::Next() {
184   auto next_it = m_it;
185   ++next_it;
186   if (next_it == m_decoded_thread->m_instruction_timestamps.end())
187     return None;
188   return TscRange(next_it, *m_decoded_thread);
189 }
190 
191 Optional<DecodedThread::TscRange> DecodedThread::TscRange::Prev() {
192   if (m_it == m_decoded_thread->m_instruction_timestamps.begin())
193     return None;
194   auto prev_it = m_it;
195   --prev_it;
196   return TscRange(prev_it, *m_decoded_thread);
197 }