1e8d8bef9SDimitry Andric //===-- DecodedThread.cpp -------------------------------------------------===// 2e8d8bef9SDimitry Andric // 3e8d8bef9SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4e8d8bef9SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5e8d8bef9SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6e8d8bef9SDimitry Andric // 7e8d8bef9SDimitry Andric //===----------------------------------------------------------------------===// 8e8d8bef9SDimitry Andric 9e8d8bef9SDimitry Andric #include "DecodedThread.h" 10fe6060f1SDimitry Andric #include "TraceCursorIntelPT.h" 11bdd1243dSDimitry Andric #include <intel-pt.h> 1281ad6265SDimitry Andric #include <memory> 13bdd1243dSDimitry Andric #include <optional> 14e8d8bef9SDimitry Andric 15fe6060f1SDimitry Andric using namespace lldb; 16e8d8bef9SDimitry Andric using namespace lldb_private; 17e8d8bef9SDimitry Andric using namespace lldb_private::trace_intel_pt; 18e8d8bef9SDimitry Andric using namespace llvm; 19e8d8bef9SDimitry Andric 20e8d8bef9SDimitry Andric char IntelPTError::ID; 21e8d8bef9SDimitry Andric 22e8d8bef9SDimitry Andric IntelPTError::IntelPTError(int libipt_error_code, lldb::addr_t address) 23e8d8bef9SDimitry Andric : m_libipt_error_code(libipt_error_code), m_address(address) { 24e8d8bef9SDimitry Andric assert(libipt_error_code < 0); 25e8d8bef9SDimitry Andric } 26e8d8bef9SDimitry Andric 27e8d8bef9SDimitry Andric void IntelPTError::log(llvm::raw_ostream &OS) const { 2881ad6265SDimitry Andric OS << pt_errstr(pt_errcode(m_libipt_error_code)); 2981ad6265SDimitry Andric if (m_address != LLDB_INVALID_ADDRESS && m_address > 0) 3081ad6265SDimitry Andric OS << formatv(": {0:x+16}", m_address); 31e8d8bef9SDimitry Andric } 32e8d8bef9SDimitry Andric 33972a253aSDimitry Andric bool DecodedThread::TSCRange::InRange(uint64_t item_index) const { 34972a253aSDimitry Andric return item_index >= first_item_index && 35972a253aSDimitry Andric item_index < first_item_index + items_count; 36fe6060f1SDimitry Andric } 37fe6060f1SDimitry Andric 38972a253aSDimitry Andric bool DecodedThread::NanosecondsRange::InRange(uint64_t item_index) const { 39972a253aSDimitry Andric return item_index >= first_item_index && 40972a253aSDimitry Andric item_index < first_item_index + items_count; 41972a253aSDimitry Andric } 42972a253aSDimitry Andric 43972a253aSDimitry Andric double DecodedThread::NanosecondsRange::GetInterpolatedTime( 44972a253aSDimitry Andric uint64_t item_index, uint64_t begin_of_time_nanos, 45972a253aSDimitry Andric const LinuxPerfZeroTscConversion &tsc_conversion) const { 46972a253aSDimitry Andric uint64_t items_since_last_tsc = item_index - first_item_index; 47972a253aSDimitry Andric 48972a253aSDimitry Andric auto interpolate = [&](uint64_t next_range_start_ns) { 49972a253aSDimitry Andric if (next_range_start_ns == nanos) { 50972a253aSDimitry Andric // If the resolution of the conversion formula is bad enough to consider 51972a253aSDimitry Andric // these two timestamps as equal, then we just increase the next one by 1 52972a253aSDimitry Andric // for correction 53972a253aSDimitry Andric next_range_start_ns++; 54972a253aSDimitry Andric } 55972a253aSDimitry Andric long double item_duration = 56972a253aSDimitry Andric static_cast<long double>(items_count) / (next_range_start_ns - nanos); 57972a253aSDimitry Andric return (nanos - begin_of_time_nanos) + items_since_last_tsc * item_duration; 58972a253aSDimitry Andric }; 59972a253aSDimitry Andric 60972a253aSDimitry Andric if (!next_range) { 61972a253aSDimitry Andric // If this is the last TSC range, so we have to extrapolate. In this case, 62972a253aSDimitry Andric // we assume that each instruction took one TSC, which is what an 63972a253aSDimitry Andric // instruction would take if no parallelism is achieved and the frequency 64972a253aSDimitry Andric // multiplier is 1. 65972a253aSDimitry Andric return interpolate(tsc_conversion.ToNanos(tsc + items_count)); 66972a253aSDimitry Andric } 67972a253aSDimitry Andric if (items_count < (next_range->tsc - tsc)) { 68972a253aSDimitry Andric // If the numbers of items in this range is less than the total TSC duration 69972a253aSDimitry Andric // of this range, i.e. each instruction taking longer than 1 TSC, then we 70972a253aSDimitry Andric // can assume that something else happened between these TSCs (e.g. a 71972a253aSDimitry Andric // context switch, change to kernel, decoding errors, etc). In this case, we 72972a253aSDimitry Andric // also assume that each instruction took 1 TSC. A proper way to improve 73972a253aSDimitry Andric // this would be to analize the next events in the trace looking for context 74972a253aSDimitry Andric // switches or trace disablement events, but for now, as we only want an 75972a253aSDimitry Andric // approximation, we keep it simple. We are also guaranteed that the time in 76972a253aSDimitry Andric // nanos of the next range is different to the current one, just because of 77972a253aSDimitry Andric // the definition of a NanosecondsRange. 78972a253aSDimitry Andric return interpolate( 79972a253aSDimitry Andric std::min(tsc_conversion.ToNanos(tsc + items_count), next_range->nanos)); 80972a253aSDimitry Andric } 81972a253aSDimitry Andric 82972a253aSDimitry Andric // In this case, each item took less than 1 TSC, so some parallelism was 83972a253aSDimitry Andric // achieved, which is an indication that we didn't suffered of any kind of 84972a253aSDimitry Andric // interruption. 85972a253aSDimitry Andric return interpolate(next_range->nanos); 86972a253aSDimitry Andric } 87972a253aSDimitry Andric 88*297eecfbSDimitry Andric uint64_t DecodedThread::GetItemsCount() const { return m_item_data.size(); } 89972a253aSDimitry Andric 90972a253aSDimitry Andric lldb::addr_t 91972a253aSDimitry Andric DecodedThread::GetInstructionLoadAddress(uint64_t item_index) const { 92*297eecfbSDimitry Andric return std::get<lldb::addr_t>(m_item_data[item_index]); 93e8d8bef9SDimitry Andric } 94e8d8bef9SDimitry Andric 95bdd1243dSDimitry Andric lldb::addr_t 96bdd1243dSDimitry Andric DecodedThread::GetSyncPointOffsetByIndex(uint64_t item_index) const { 97bdd1243dSDimitry Andric return m_psb_offsets.find(item_index)->second; 98bdd1243dSDimitry Andric } 99bdd1243dSDimitry Andric 10081ad6265SDimitry Andric ThreadSP DecodedThread::GetThread() { return m_thread_sp; } 101e8d8bef9SDimitry Andric 102*297eecfbSDimitry Andric template <typename Data> 10381ad6265SDimitry Andric DecodedThread::TraceItemStorage & 104*297eecfbSDimitry Andric DecodedThread::CreateNewTraceItem(lldb::TraceItemKind kind, Data &&data) { 105*297eecfbSDimitry Andric m_item_data.emplace_back(data); 106*297eecfbSDimitry Andric 107972a253aSDimitry Andric if (m_last_tsc) 108972a253aSDimitry Andric (*m_last_tsc)->second.items_count++; 109972a253aSDimitry Andric if (m_last_nanoseconds) 110972a253aSDimitry Andric (*m_last_nanoseconds)->second.items_count++; 111*297eecfbSDimitry Andric 11281ad6265SDimitry Andric return m_item_data.back(); 113fe6060f1SDimitry Andric } 114fe6060f1SDimitry Andric 115bdd1243dSDimitry Andric void DecodedThread::NotifySyncPoint(lldb::addr_t psb_offset) { 116bdd1243dSDimitry Andric m_psb_offsets.try_emplace(GetItemsCount(), psb_offset); 117bdd1243dSDimitry Andric AppendEvent(lldb::eTraceEventSyncPoint); 118bdd1243dSDimitry Andric } 119bdd1243dSDimitry Andric 120972a253aSDimitry Andric void DecodedThread::NotifyTsc(TSC tsc) { 121972a253aSDimitry Andric if (m_last_tsc && (*m_last_tsc)->second.tsc == tsc) 122972a253aSDimitry Andric return; 123bdd1243dSDimitry Andric if (m_last_tsc) 124bdd1243dSDimitry Andric assert(tsc >= (*m_last_tsc)->second.tsc && 125bdd1243dSDimitry Andric "We can't have decreasing times"); 126972a253aSDimitry Andric 127972a253aSDimitry Andric m_last_tsc = 128972a253aSDimitry Andric m_tscs.emplace(GetItemsCount(), TSCRange{tsc, 0, GetItemsCount()}).first; 129972a253aSDimitry Andric 130972a253aSDimitry Andric if (m_tsc_conversion) { 131972a253aSDimitry Andric uint64_t nanos = m_tsc_conversion->ToNanos(tsc); 132972a253aSDimitry Andric if (!m_last_nanoseconds || (*m_last_nanoseconds)->second.nanos != nanos) { 133972a253aSDimitry Andric m_last_nanoseconds = 134972a253aSDimitry Andric m_nanoseconds 135972a253aSDimitry Andric .emplace(GetItemsCount(), NanosecondsRange{nanos, tsc, nullptr, 0, 136972a253aSDimitry Andric GetItemsCount()}) 137972a253aSDimitry Andric .first; 138972a253aSDimitry Andric if (*m_last_nanoseconds != m_nanoseconds.begin()) { 139972a253aSDimitry Andric auto prev_range = prev(*m_last_nanoseconds); 140972a253aSDimitry Andric prev_range->second.next_range = &(*m_last_nanoseconds)->second; 14181ad6265SDimitry Andric } 142e8d8bef9SDimitry Andric } 143972a253aSDimitry Andric } 144972a253aSDimitry Andric AppendEvent(lldb::eTraceEventHWClockTick); 145972a253aSDimitry Andric } 146e8d8bef9SDimitry Andric 147753f127fSDimitry Andric void DecodedThread::NotifyCPU(lldb::cpu_id_t cpu_id) { 148753f127fSDimitry Andric if (!m_last_cpu || *m_last_cpu != cpu_id) { 149972a253aSDimitry Andric m_cpus.emplace(GetItemsCount(), cpu_id); 150753f127fSDimitry Andric m_last_cpu = cpu_id; 151753f127fSDimitry Andric AppendEvent(lldb::eTraceEventCPUChanged); 152753f127fSDimitry Andric } 153753f127fSDimitry Andric } 154753f127fSDimitry Andric 155bdd1243dSDimitry Andric lldb::cpu_id_t DecodedThread::GetCPUByIndex(uint64_t item_index) const { 156972a253aSDimitry Andric auto it = m_cpus.upper_bound(item_index); 157bdd1243dSDimitry Andric return it == m_cpus.begin() ? LLDB_INVALID_CPU_ID : prev(it)->second; 158753f127fSDimitry Andric } 159753f127fSDimitry Andric 160bdd1243dSDimitry Andric std::optional<DecodedThread::TSCRange> 161972a253aSDimitry Andric DecodedThread::GetTSCRangeByIndex(uint64_t item_index) const { 162972a253aSDimitry Andric auto next_it = m_tscs.upper_bound(item_index); 163972a253aSDimitry Andric if (next_it == m_tscs.begin()) 164bdd1243dSDimitry Andric return std::nullopt; 165972a253aSDimitry Andric return prev(next_it)->second; 166972a253aSDimitry Andric } 167972a253aSDimitry Andric 168bdd1243dSDimitry Andric std::optional<DecodedThread::NanosecondsRange> 169972a253aSDimitry Andric DecodedThread::GetNanosecondsRangeByIndex(uint64_t item_index) { 170972a253aSDimitry Andric auto next_it = m_nanoseconds.upper_bound(item_index); 171972a253aSDimitry Andric if (next_it == m_nanoseconds.begin()) 172bdd1243dSDimitry Andric return std::nullopt; 173972a253aSDimitry Andric return prev(next_it)->second; 174972a253aSDimitry Andric } 175972a253aSDimitry Andric 176bdd1243dSDimitry Andric uint64_t DecodedThread::GetTotalInstructionCount() const { 177bdd1243dSDimitry Andric return m_insn_count; 178bdd1243dSDimitry Andric } 179bdd1243dSDimitry Andric 18081ad6265SDimitry Andric void DecodedThread::AppendEvent(lldb::TraceEvent event) { 181*297eecfbSDimitry Andric CreateNewTraceItem(lldb::eTraceItemKindEvent, event); 18281ad6265SDimitry Andric m_events_stats.RecordEvent(event); 183e8d8bef9SDimitry Andric } 184e8d8bef9SDimitry Andric 18581ad6265SDimitry Andric void DecodedThread::AppendInstruction(const pt_insn &insn) { 186*297eecfbSDimitry Andric CreateNewTraceItem(lldb::eTraceItemKindInstruction, insn.ip); 187bdd1243dSDimitry Andric m_insn_count++; 188fe6060f1SDimitry Andric } 189e8d8bef9SDimitry Andric 19081ad6265SDimitry Andric void DecodedThread::AppendError(const IntelPTError &error) { 191*297eecfbSDimitry Andric CreateNewTraceItem(lldb::eTraceItemKindError, error.message()); 192bdd1243dSDimitry Andric m_error_stats.RecordError(/*fatal=*/false); 193fe6060f1SDimitry Andric } 194fe6060f1SDimitry Andric 195bdd1243dSDimitry Andric void DecodedThread::AppendCustomError(StringRef err, bool fatal) { 196*297eecfbSDimitry Andric CreateNewTraceItem(lldb::eTraceItemKindError, err.str()); 197bdd1243dSDimitry Andric m_error_stats.RecordError(fatal); 19881ad6265SDimitry Andric } 19981ad6265SDimitry Andric 20081ad6265SDimitry Andric lldb::TraceEvent DecodedThread::GetEventByIndex(int item_index) const { 201*297eecfbSDimitry Andric return std::get<lldb::TraceEvent>(m_item_data[item_index]); 20281ad6265SDimitry Andric } 20381ad6265SDimitry Andric 20481ad6265SDimitry Andric const DecodedThread::EventsStats &DecodedThread::GetEventsStats() const { 20581ad6265SDimitry Andric return m_events_stats; 20681ad6265SDimitry Andric } 20781ad6265SDimitry Andric 20881ad6265SDimitry Andric void DecodedThread::EventsStats::RecordEvent(lldb::TraceEvent event) { 20981ad6265SDimitry Andric events_counts[event]++; 21081ad6265SDimitry Andric total_count++; 21181ad6265SDimitry Andric } 21281ad6265SDimitry Andric 213bdd1243dSDimitry Andric uint64_t DecodedThread::ErrorStats::GetTotalCount() const { 214bdd1243dSDimitry Andric uint64_t total = 0; 215bdd1243dSDimitry Andric for (const auto &[kind, count] : libipt_errors) 216bdd1243dSDimitry Andric total += count; 217bdd1243dSDimitry Andric 218bdd1243dSDimitry Andric return total + other_errors + fatal_errors; 219bdd1243dSDimitry Andric } 220bdd1243dSDimitry Andric 221bdd1243dSDimitry Andric void DecodedThread::ErrorStats::RecordError(bool fatal) { 222bdd1243dSDimitry Andric if (fatal) 223bdd1243dSDimitry Andric fatal_errors++; 224bdd1243dSDimitry Andric else 225bdd1243dSDimitry Andric other_errors++; 226bdd1243dSDimitry Andric } 227bdd1243dSDimitry Andric 228bdd1243dSDimitry Andric void DecodedThread::ErrorStats::RecordError(int libipt_error_code) { 229bdd1243dSDimitry Andric libipt_errors[pt_errstr(pt_errcode(libipt_error_code))]++; 230bdd1243dSDimitry Andric } 231bdd1243dSDimitry Andric 232bdd1243dSDimitry Andric const DecodedThread::ErrorStats &DecodedThread::GetErrorStats() const { 233bdd1243dSDimitry Andric return m_error_stats; 234bdd1243dSDimitry Andric } 235bdd1243dSDimitry Andric 236972a253aSDimitry Andric lldb::TraceItemKind 237972a253aSDimitry Andric DecodedThread::GetItemKindByIndex(uint64_t item_index) const { 238*297eecfbSDimitry Andric return std::visit( 239*297eecfbSDimitry Andric llvm::makeVisitor( 240*297eecfbSDimitry Andric [](const std::string &) { return lldb::eTraceItemKindError; }, 241*297eecfbSDimitry Andric [](lldb::TraceEvent) { return lldb::eTraceItemKindEvent; }, 242*297eecfbSDimitry Andric [](lldb::addr_t) { return lldb::eTraceItemKindInstruction; }), 243*297eecfbSDimitry Andric m_item_data[item_index]); 24481ad6265SDimitry Andric } 24581ad6265SDimitry Andric 24606c3fb27SDimitry Andric llvm::StringRef DecodedThread::GetErrorByIndex(uint64_t item_index) const { 24706c3fb27SDimitry Andric if (item_index >= m_item_data.size()) 24806c3fb27SDimitry Andric return llvm::StringRef(); 249*297eecfbSDimitry Andric return std::get<std::string>(m_item_data[item_index]); 25081ad6265SDimitry Andric } 25181ad6265SDimitry Andric 252972a253aSDimitry Andric DecodedThread::DecodedThread( 253972a253aSDimitry Andric ThreadSP thread_sp, 254bdd1243dSDimitry Andric const std::optional<LinuxPerfZeroTscConversion> &tsc_conversion) 255972a253aSDimitry Andric : m_thread_sp(thread_sp), m_tsc_conversion(tsc_conversion) {} 25681ad6265SDimitry Andric 25781ad6265SDimitry Andric size_t DecodedThread::CalculateApproximateMemoryUsage() const { 25881ad6265SDimitry Andric return sizeof(TraceItemStorage) * m_item_data.size() + 259972a253aSDimitry Andric (sizeof(uint64_t) + sizeof(TSC)) * m_tscs.size() + 260972a253aSDimitry Andric (sizeof(uint64_t) + sizeof(uint64_t)) * m_nanoseconds.size() + 261972a253aSDimitry Andric (sizeof(uint64_t) + sizeof(lldb::cpu_id_t)) * m_cpus.size(); 26281ad6265SDimitry Andric } 263