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 OS << pt_errstr(pt_errcode(m_libipt_error_code)); 43 if (m_address != LLDB_INVALID_ADDRESS && m_address > 0) 44 OS << formatv(": {0:x+16}", m_address); 45 } 46 47 int64_t DecodedThread::GetItemsCount() const { 48 return static_cast<int64_t>(m_item_kinds.size()); 49 } 50 51 lldb::addr_t DecodedThread::GetInstructionLoadAddress(size_t item_index) const { 52 return m_item_data[item_index].load_address; 53 } 54 55 ThreadSP DecodedThread::GetThread() { return m_thread_sp; } 56 57 DecodedThread::TraceItemStorage & 58 DecodedThread::CreateNewTraceItem(lldb::TraceItemKind kind) { 59 m_item_kinds.push_back(kind); 60 m_item_data.emplace_back(); 61 return m_item_data.back(); 62 } 63 64 void DecodedThread::NotifyTsc(uint64_t tsc) { 65 if (!m_last_tsc || *m_last_tsc != tsc) { 66 m_timestamps.emplace(m_item_kinds.size(), tsc); 67 m_last_tsc = tsc; 68 } 69 } 70 71 void DecodedThread::NotifyCPU(lldb::cpu_id_t cpu_id) { 72 if (!m_last_cpu || *m_last_cpu != cpu_id) { 73 m_cpus.emplace(m_item_kinds.size(), cpu_id); 74 m_last_cpu = cpu_id; 75 AppendEvent(lldb::eTraceEventCPUChanged); 76 } 77 } 78 79 Optional<lldb::cpu_id_t> 80 DecodedThread::GetCPUByIndex(uint64_t insn_index) const { 81 // Could possibly optimize the search 82 auto it = m_cpus.upper_bound(insn_index); 83 if (it == m_cpus.begin()) 84 return None; 85 return prev(it)->second; 86 } 87 88 void DecodedThread::AppendEvent(lldb::TraceEvent event) { 89 CreateNewTraceItem(lldb::eTraceItemKindEvent).event = event; 90 m_events_stats.RecordEvent(event); 91 } 92 93 void DecodedThread::AppendInstruction(const pt_insn &insn) { 94 CreateNewTraceItem(lldb::eTraceItemKindInstruction).load_address = insn.ip; 95 } 96 97 void DecodedThread::AppendError(const IntelPTError &error) { 98 // End of stream shouldn't be a public error 99 if (IsEndOfStream(error.GetLibiptErrorCode())) 100 return; 101 CreateNewTraceItem(lldb::eTraceItemKindError).error = 102 ConstString(error.message()).AsCString(); 103 } 104 105 void DecodedThread::AppendCustomError(StringRef err) { 106 CreateNewTraceItem(lldb::eTraceItemKindError).error = 107 ConstString(err).AsCString(); 108 } 109 110 lldb::TraceEvent DecodedThread::GetEventByIndex(int item_index) const { 111 return m_item_data[item_index].event; 112 } 113 114 void DecodedThread::LibiptErrorsStats::RecordError(int libipt_error_code) { 115 libipt_errors_counts[pt_errstr(pt_errcode(libipt_error_code))]++; 116 total_count++; 117 } 118 119 void DecodedThread::RecordTscError(int libipt_error_code) { 120 m_tsc_errors_stats.RecordError(libipt_error_code); 121 } 122 123 const DecodedThread::LibiptErrorsStats & 124 DecodedThread::GetTscErrorsStats() const { 125 return m_tsc_errors_stats; 126 } 127 128 const DecodedThread::EventsStats &DecodedThread::GetEventsStats() const { 129 return m_events_stats; 130 } 131 132 void DecodedThread::EventsStats::RecordEvent(lldb::TraceEvent event) { 133 events_counts[event]++; 134 total_count++; 135 } 136 137 Optional<DecodedThread::TscRange> DecodedThread::CalculateTscRange( 138 size_t insn_index, 139 const Optional<DecodedThread::TscRange> &hint_range) const { 140 // We first try to check the given hint range in case we are traversing the 141 // trace in short jumps. If that fails, then we do the more expensive 142 // arbitrary lookup. 143 if (hint_range) { 144 Optional<TscRange> candidate_range; 145 if (insn_index < hint_range->GetStartInstructionIndex()) 146 candidate_range = hint_range->Prev(); 147 else if (insn_index > hint_range->GetEndInstructionIndex()) 148 candidate_range = hint_range->Next(); 149 else 150 candidate_range = hint_range; 151 152 if (candidate_range && candidate_range->InRange(insn_index)) 153 return candidate_range; 154 } 155 // Now we do a more expensive lookup 156 auto it = m_timestamps.upper_bound(insn_index); 157 if (it == m_timestamps.begin()) 158 return None; 159 160 return TscRange(--it, *this); 161 } 162 163 lldb::TraceItemKind DecodedThread::GetItemKindByIndex(size_t item_index) const { 164 return static_cast<lldb::TraceItemKind>(m_item_kinds[item_index]); 165 } 166 167 const char *DecodedThread::GetErrorByIndex(size_t item_index) const { 168 return m_item_data[item_index].error; 169 } 170 171 DecodedThread::DecodedThread(ThreadSP thread_sp) : m_thread_sp(thread_sp) {} 172 173 lldb::TraceCursorUP DecodedThread::CreateNewCursor() { 174 return std::make_unique<TraceCursorIntelPT>(m_thread_sp, shared_from_this()); 175 } 176 177 size_t DecodedThread::CalculateApproximateMemoryUsage() const { 178 return sizeof(TraceItemStorage) * m_item_data.size() + 179 sizeof(uint8_t) * m_item_kinds.size() + 180 (sizeof(size_t) + sizeof(uint64_t)) * m_timestamps.size() + 181 (sizeof(size_t) + sizeof(lldb::cpu_id_t)) * m_cpus.size(); 182 } 183 184 DecodedThread::TscRange::TscRange(std::map<size_t, uint64_t>::const_iterator it, 185 const DecodedThread &decoded_thread) 186 : m_it(it), m_decoded_thread(&decoded_thread) { 187 auto next_it = m_it; 188 ++next_it; 189 m_end_index = (next_it == m_decoded_thread->m_timestamps.end()) 190 ? std::numeric_limits<uint64_t>::max() 191 : next_it->first - 1; 192 } 193 194 size_t DecodedThread::TscRange::GetTsc() const { return m_it->second; } 195 196 size_t DecodedThread::TscRange::GetStartInstructionIndex() const { 197 return m_it->first; 198 } 199 200 size_t DecodedThread::TscRange::GetEndInstructionIndex() const { 201 return m_end_index; 202 } 203 204 bool DecodedThread::TscRange::InRange(size_t insn_index) const { 205 return GetStartInstructionIndex() <= insn_index && 206 insn_index <= GetEndInstructionIndex(); 207 } 208 209 Optional<DecodedThread::TscRange> DecodedThread::TscRange::Next() const { 210 auto next_it = m_it; 211 ++next_it; 212 if (next_it == m_decoded_thread->m_timestamps.end()) 213 return None; 214 return TscRange(next_it, *m_decoded_thread); 215 } 216 217 Optional<DecodedThread::TscRange> DecodedThread::TscRange::Prev() const { 218 if (m_it == m_decoded_thread->m_timestamps.begin()) 219 return None; 220 auto prev_it = m_it; 221 --prev_it; 222 return TscRange(prev_it, *m_decoded_thread); 223 } 224