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_instruction_timestamps.emplace(m_item_kinds.size(), tsc); 67 m_last_tsc = tsc; 68 } 69 } 70 71 void DecodedThread::AppendEvent(lldb::TraceEvent event) { 72 CreateNewTraceItem(lldb::eTraceItemKindEvent).event = event; 73 m_events_stats.RecordEvent(event); 74 } 75 76 void DecodedThread::AppendInstruction(const pt_insn &insn) { 77 CreateNewTraceItem(lldb::eTraceItemKindInstruction).load_address = insn.ip; 78 } 79 80 void DecodedThread::AppendError(const IntelPTError &error) { 81 // End of stream shouldn't be a public error 82 if (IsEndOfStream(error.GetLibiptErrorCode())) 83 return; 84 CreateNewTraceItem(lldb::eTraceItemKindError).error = 85 ConstString(error.message()).AsCString(); 86 } 87 88 void DecodedThread::AppendCustomError(StringRef err) { 89 CreateNewTraceItem(lldb::eTraceItemKindError).error = 90 ConstString(err).AsCString(); 91 } 92 93 lldb::TraceEvent DecodedThread::GetEventByIndex(int item_index) const { 94 return m_item_data[item_index].event; 95 } 96 97 void DecodedThread::LibiptErrorsStats::RecordError(int libipt_error_code) { 98 libipt_errors_counts[pt_errstr(pt_errcode(libipt_error_code))]++; 99 total_count++; 100 } 101 102 void DecodedThread::RecordTscError(int libipt_error_code) { 103 m_tsc_errors_stats.RecordError(libipt_error_code); 104 } 105 106 const DecodedThread::LibiptErrorsStats & 107 DecodedThread::GetTscErrorsStats() const { 108 return m_tsc_errors_stats; 109 } 110 111 const DecodedThread::EventsStats &DecodedThread::GetEventsStats() const { 112 return m_events_stats; 113 } 114 115 void DecodedThread::EventsStats::RecordEvent(lldb::TraceEvent event) { 116 events_counts[event]++; 117 total_count++; 118 } 119 120 Optional<DecodedThread::TscRange> DecodedThread::CalculateTscRange( 121 size_t insn_index, 122 const Optional<DecodedThread::TscRange> &hint_range) const { 123 // We first try to check the given hint range in case we are traversing the 124 // trace in short jumps. If that fails, then we do the more expensive 125 // arbitrary lookup. 126 if (hint_range) { 127 Optional<TscRange> candidate_range; 128 if (insn_index < hint_range->GetStartInstructionIndex()) 129 candidate_range = hint_range->Prev(); 130 else if (insn_index > hint_range->GetEndInstructionIndex()) 131 candidate_range = hint_range->Next(); 132 else 133 candidate_range = hint_range; 134 135 if (candidate_range && candidate_range->InRange(insn_index)) 136 return candidate_range; 137 } 138 // Now we do a more expensive lookup 139 auto it = m_instruction_timestamps.upper_bound(insn_index); 140 if (it == m_instruction_timestamps.begin()) 141 return None; 142 143 return TscRange(--it, *this); 144 } 145 146 lldb::TraceItemKind DecodedThread::GetItemKindByIndex(size_t item_index) const { 147 return static_cast<lldb::TraceItemKind>(m_item_kinds[item_index]); 148 } 149 150 const char *DecodedThread::GetErrorByIndex(size_t item_index) const { 151 return m_item_data[item_index].error; 152 } 153 154 DecodedThread::DecodedThread(ThreadSP thread_sp) : m_thread_sp(thread_sp) {} 155 156 lldb::TraceCursorUP DecodedThread::CreateNewCursor() { 157 return std::make_unique<TraceCursorIntelPT>(m_thread_sp, shared_from_this()); 158 } 159 160 size_t DecodedThread::CalculateApproximateMemoryUsage() const { 161 return sizeof(TraceItemStorage) * m_item_data.size() + 162 sizeof(uint8_t) * m_item_kinds.size() + 163 (sizeof(size_t) + sizeof(uint64_t)) * m_instruction_timestamps.size(); 164 } 165 166 DecodedThread::TscRange::TscRange(std::map<size_t, uint64_t>::const_iterator it, 167 const DecodedThread &decoded_thread) 168 : m_it(it), m_decoded_thread(&decoded_thread) { 169 auto next_it = m_it; 170 ++next_it; 171 m_end_index = (next_it == m_decoded_thread->m_instruction_timestamps.end()) 172 ? std::numeric_limits<uint64_t>::max() 173 : next_it->first - 1; 174 } 175 176 size_t DecodedThread::TscRange::GetTsc() const { return m_it->second; } 177 178 size_t DecodedThread::TscRange::GetStartInstructionIndex() const { 179 return m_it->first; 180 } 181 182 size_t DecodedThread::TscRange::GetEndInstructionIndex() const { 183 return m_end_index; 184 } 185 186 bool DecodedThread::TscRange::InRange(size_t insn_index) const { 187 return GetStartInstructionIndex() <= insn_index && 188 insn_index <= GetEndInstructionIndex(); 189 } 190 191 Optional<DecodedThread::TscRange> DecodedThread::TscRange::Next() const { 192 auto next_it = m_it; 193 ++next_it; 194 if (next_it == m_decoded_thread->m_instruction_timestamps.end()) 195 return None; 196 return TscRange(next_it, *m_decoded_thread); 197 } 198 199 Optional<DecodedThread::TscRange> DecodedThread::TscRange::Prev() const { 200 if (m_it == m_decoded_thread->m_instruction_timestamps.begin()) 201 return None; 202 auto prev_it = m_it; 203 --prev_it; 204 return TscRange(prev_it, *m_decoded_thread); 205 } 206