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 bool DecodedThread::TSCRange::InRange(uint64_t item_index) const { 48 return item_index >= first_item_index && 49 item_index < first_item_index + items_count; 50 } 51 52 bool DecodedThread::NanosecondsRange::InRange(uint64_t item_index) const { 53 return item_index >= first_item_index && 54 item_index < first_item_index + items_count; 55 } 56 57 double DecodedThread::NanosecondsRange::GetInterpolatedTime( 58 uint64_t item_index, uint64_t begin_of_time_nanos, 59 const LinuxPerfZeroTscConversion &tsc_conversion) const { 60 uint64_t items_since_last_tsc = item_index - first_item_index; 61 62 auto interpolate = [&](uint64_t next_range_start_ns) { 63 if (next_range_start_ns == nanos) { 64 // If the resolution of the conversion formula is bad enough to consider 65 // these two timestamps as equal, then we just increase the next one by 1 66 // for correction 67 next_range_start_ns++; 68 } 69 long double item_duration = 70 static_cast<long double>(items_count) / (next_range_start_ns - nanos); 71 return (nanos - begin_of_time_nanos) + items_since_last_tsc * item_duration; 72 }; 73 74 if (!next_range) { 75 // If this is the last TSC range, so we have to extrapolate. In this case, 76 // we assume that each instruction took one TSC, which is what an 77 // instruction would take if no parallelism is achieved and the frequency 78 // multiplier is 1. 79 return interpolate(tsc_conversion.ToNanos(tsc + items_count)); 80 } 81 if (items_count < (next_range->tsc - tsc)) { 82 // If the numbers of items in this range is less than the total TSC duration 83 // of this range, i.e. each instruction taking longer than 1 TSC, then we 84 // can assume that something else happened between these TSCs (e.g. a 85 // context switch, change to kernel, decoding errors, etc). In this case, we 86 // also assume that each instruction took 1 TSC. A proper way to improve 87 // this would be to analize the next events in the trace looking for context 88 // switches or trace disablement events, but for now, as we only want an 89 // approximation, we keep it simple. We are also guaranteed that the time in 90 // nanos of the next range is different to the current one, just because of 91 // the definition of a NanosecondsRange. 92 return interpolate( 93 std::min(tsc_conversion.ToNanos(tsc + items_count), next_range->nanos)); 94 } 95 96 // In this case, each item took less than 1 TSC, so some parallelism was 97 // achieved, which is an indication that we didn't suffered of any kind of 98 // interruption. 99 return interpolate(next_range->nanos); 100 } 101 102 uint64_t DecodedThread::GetItemsCount() const { return m_item_kinds.size(); } 103 104 lldb::addr_t 105 DecodedThread::GetInstructionLoadAddress(uint64_t item_index) const { 106 return m_item_data[item_index].load_address; 107 } 108 109 ThreadSP DecodedThread::GetThread() { return m_thread_sp; } 110 111 DecodedThread::TraceItemStorage & 112 DecodedThread::CreateNewTraceItem(lldb::TraceItemKind kind) { 113 m_item_kinds.push_back(kind); 114 m_item_data.emplace_back(); 115 if (m_last_tsc) 116 (*m_last_tsc)->second.items_count++; 117 if (m_last_nanoseconds) 118 (*m_last_nanoseconds)->second.items_count++; 119 return m_item_data.back(); 120 } 121 122 void DecodedThread::NotifyTsc(TSC tsc) { 123 if (m_last_tsc && (*m_last_tsc)->second.tsc == tsc) 124 return; 125 126 m_last_tsc = 127 m_tscs.emplace(GetItemsCount(), TSCRange{tsc, 0, GetItemsCount()}).first; 128 129 if (m_tsc_conversion) { 130 uint64_t nanos = m_tsc_conversion->ToNanos(tsc); 131 if (!m_last_nanoseconds || (*m_last_nanoseconds)->second.nanos != nanos) { 132 m_last_nanoseconds = 133 m_nanoseconds 134 .emplace(GetItemsCount(), NanosecondsRange{nanos, tsc, nullptr, 0, 135 GetItemsCount()}) 136 .first; 137 if (*m_last_nanoseconds != m_nanoseconds.begin()) { 138 auto prev_range = prev(*m_last_nanoseconds); 139 prev_range->second.next_range = &(*m_last_nanoseconds)->second; 140 } 141 } 142 } 143 AppendEvent(lldb::eTraceEventHWClockTick); 144 } 145 146 void DecodedThread::NotifyCPU(lldb::cpu_id_t cpu_id) { 147 if (!m_last_cpu || *m_last_cpu != cpu_id) { 148 m_cpus.emplace(GetItemsCount(), cpu_id); 149 m_last_cpu = cpu_id; 150 AppendEvent(lldb::eTraceEventCPUChanged); 151 } 152 } 153 154 Optional<lldb::cpu_id_t> 155 DecodedThread::GetCPUByIndex(uint64_t item_index) const { 156 auto it = m_cpus.upper_bound(item_index); 157 if (it == m_cpus.begin()) 158 return None; 159 return prev(it)->second; 160 } 161 162 Optional<DecodedThread::TSCRange> 163 DecodedThread::GetTSCRangeByIndex(uint64_t item_index) const { 164 auto next_it = m_tscs.upper_bound(item_index); 165 if (next_it == m_tscs.begin()) 166 return None; 167 return prev(next_it)->second; 168 } 169 170 Optional<DecodedThread::NanosecondsRange> 171 DecodedThread::GetNanosecondsRangeByIndex(uint64_t item_index) { 172 auto next_it = m_nanoseconds.upper_bound(item_index); 173 if (next_it == m_nanoseconds.begin()) 174 return None; 175 return prev(next_it)->second; 176 } 177 178 void DecodedThread::AppendEvent(lldb::TraceEvent event) { 179 CreateNewTraceItem(lldb::eTraceItemKindEvent).event = event; 180 m_events_stats.RecordEvent(event); 181 } 182 183 void DecodedThread::AppendInstruction(const pt_insn &insn) { 184 CreateNewTraceItem(lldb::eTraceItemKindInstruction).load_address = insn.ip; 185 } 186 187 void DecodedThread::AppendError(const IntelPTError &error) { 188 // End of stream shouldn't be a public error 189 if (IsEndOfStream(error.GetLibiptErrorCode())) 190 return; 191 CreateNewTraceItem(lldb::eTraceItemKindError).error = 192 ConstString(error.message()).AsCString(); 193 } 194 195 void DecodedThread::AppendCustomError(StringRef err) { 196 CreateNewTraceItem(lldb::eTraceItemKindError).error = 197 ConstString(err).AsCString(); 198 } 199 200 lldb::TraceEvent DecodedThread::GetEventByIndex(int item_index) const { 201 return m_item_data[item_index].event; 202 } 203 204 void DecodedThread::LibiptErrorsStats::RecordError(int libipt_error_code) { 205 libipt_errors_counts[pt_errstr(pt_errcode(libipt_error_code))]++; 206 total_count++; 207 } 208 209 void DecodedThread::RecordTscError(int libipt_error_code) { 210 m_tsc_errors_stats.RecordError(libipt_error_code); 211 } 212 213 const DecodedThread::LibiptErrorsStats & 214 DecodedThread::GetTscErrorsStats() const { 215 return m_tsc_errors_stats; 216 } 217 218 const DecodedThread::EventsStats &DecodedThread::GetEventsStats() const { 219 return m_events_stats; 220 } 221 222 void DecodedThread::EventsStats::RecordEvent(lldb::TraceEvent event) { 223 events_counts[event]++; 224 total_count++; 225 } 226 227 lldb::TraceItemKind 228 DecodedThread::GetItemKindByIndex(uint64_t item_index) const { 229 return static_cast<lldb::TraceItemKind>(m_item_kinds[item_index]); 230 } 231 232 const char *DecodedThread::GetErrorByIndex(uint64_t item_index) const { 233 return m_item_data[item_index].error; 234 } 235 236 DecodedThread::DecodedThread( 237 ThreadSP thread_sp, 238 const llvm::Optional<LinuxPerfZeroTscConversion> &tsc_conversion) 239 : m_thread_sp(thread_sp), m_tsc_conversion(tsc_conversion) {} 240 241 size_t DecodedThread::CalculateApproximateMemoryUsage() const { 242 return sizeof(TraceItemStorage) * m_item_data.size() + 243 sizeof(uint8_t) * m_item_kinds.size() + 244 (sizeof(uint64_t) + sizeof(TSC)) * m_tscs.size() + 245 (sizeof(uint64_t) + sizeof(uint64_t)) * m_nanoseconds.size() + 246 (sizeof(uint64_t) + sizeof(lldb::cpu_id_t)) * m_cpus.size(); 247 } 248