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