brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.9 KiB · 9c07539 Raw
263 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9#include "DecodedThread.h"10#include "TraceCursorIntelPT.h"11#include <intel-pt.h>12#include <memory>13#include <optional>14 15using namespace lldb;16using namespace lldb_private;17using namespace lldb_private::trace_intel_pt;18using namespace llvm;19 20char IntelPTError::ID;21 22IntelPTError::IntelPTError(int libipt_error_code, lldb::addr_t address)23    : m_libipt_error_code(libipt_error_code), m_address(address) {24  assert(libipt_error_code < 0);25}26 27void IntelPTError::log(llvm::raw_ostream &OS) const {28  OS << pt_errstr(pt_errcode(m_libipt_error_code));29  if (m_address != LLDB_INVALID_ADDRESS && m_address > 0)30    OS << formatv(": {0:x+16}", m_address);31}32 33bool DecodedThread::TSCRange::InRange(uint64_t item_index) const {34  return item_index >= first_item_index &&35         item_index < first_item_index + items_count;36}37 38bool DecodedThread::NanosecondsRange::InRange(uint64_t item_index) const {39  return item_index >= first_item_index &&40         item_index < first_item_index + items_count;41}42 43double DecodedThread::NanosecondsRange::GetInterpolatedTime(44    uint64_t item_index, uint64_t begin_of_time_nanos,45    const LinuxPerfZeroTscConversion &tsc_conversion) const {46  uint64_t items_since_last_tsc = item_index - first_item_index;47 48  auto interpolate = [&](uint64_t next_range_start_ns) {49    if (next_range_start_ns == nanos) {50      // If the resolution of the conversion formula is bad enough to consider51      // these two timestamps as equal, then we just increase the next one by 152      // for correction53      next_range_start_ns++;54    }55    long double item_duration =56        static_cast<long double>(items_count) / (next_range_start_ns - nanos);57    return (nanos - begin_of_time_nanos) + items_since_last_tsc * item_duration;58  };59 60  if (!next_range) {61    // If this is the last TSC range, so we have to extrapolate. In this case,62    // we assume that each instruction took one TSC, which is what an63    // instruction would take if no parallelism is achieved and the frequency64    // multiplier is 1.65    return interpolate(tsc_conversion.ToNanos(tsc + items_count));66  }67  if (items_count < (next_range->tsc - tsc)) {68    // If the numbers of items in this range is less than the total TSC duration69    // of this range, i.e. each instruction taking longer than 1 TSC, then we70    // can assume that something else happened between these TSCs (e.g. a71    // context switch, change to kernel, decoding errors, etc). In this case, we72    // also assume that each instruction took 1 TSC. A proper way to improve73    // this would be to analize the next events in the trace looking for context74    // switches or trace disablement events, but for now, as we only want an75    // approximation, we keep it simple. We are also guaranteed that the time in76    // nanos of the next range is different to the current one, just because of77    // the definition of a NanosecondsRange.78    return interpolate(79        std::min(tsc_conversion.ToNanos(tsc + items_count), next_range->nanos));80  }81 82  // In this case, each item took less than 1 TSC, so some parallelism was83  // achieved, which is an indication that we didn't suffered of any kind of84  // interruption.85  return interpolate(next_range->nanos);86}87 88uint64_t DecodedThread::GetItemsCount() const { return m_item_data.size(); }89 90lldb::addr_t91DecodedThread::GetInstructionLoadAddress(uint64_t item_index) const {92  return std::get<lldb::addr_t>(m_item_data[item_index]);93}94 95lldb::addr_t96DecodedThread::GetSyncPointOffsetByIndex(uint64_t item_index) const {97  return m_psb_offsets.find(item_index)->second;98}99 100ThreadSP DecodedThread::GetThread() { return m_thread_sp; }101 102template <typename Data>103DecodedThread::TraceItemStorage &104DecodedThread::CreateNewTraceItem(lldb::TraceItemKind kind, Data &&data) {105  m_item_data.emplace_back(data);106 107  if (m_last_tsc)108    (*m_last_tsc)->second.items_count++;109  if (m_last_nanoseconds)110    (*m_last_nanoseconds)->second.items_count++;111 112  return m_item_data.back();113}114 115void DecodedThread::NotifySyncPoint(lldb::addr_t psb_offset) {116  m_psb_offsets.try_emplace(GetItemsCount(), psb_offset);117  AppendEvent(lldb::eTraceEventSyncPoint);118}119 120void DecodedThread::NotifyTsc(TSC tsc) {121  if (m_last_tsc && (*m_last_tsc)->second.tsc == tsc)122    return;123  if (m_last_tsc)124    assert(tsc >= (*m_last_tsc)->second.tsc &&125           "We can't have decreasing times");126 127  m_last_tsc =128      m_tscs.emplace(GetItemsCount(), TSCRange{tsc, 0, GetItemsCount()}).first;129 130  if (m_tsc_conversion) {131    uint64_t nanos = m_tsc_conversion->ToNanos(tsc);132    if (!m_last_nanoseconds || (*m_last_nanoseconds)->second.nanos != nanos) {133      m_last_nanoseconds =134          m_nanoseconds135              .emplace(GetItemsCount(), NanosecondsRange{nanos, tsc, nullptr, 0,136                                                         GetItemsCount()})137              .first;138      if (*m_last_nanoseconds != m_nanoseconds.begin()) {139        auto prev_range = prev(*m_last_nanoseconds);140        prev_range->second.next_range = &(*m_last_nanoseconds)->second;141      }142    }143  }144  AppendEvent(lldb::eTraceEventHWClockTick);145}146 147void DecodedThread::NotifyCPU(lldb::cpu_id_t cpu_id) {148  if (!m_last_cpu || *m_last_cpu != cpu_id) {149    m_cpus.emplace(GetItemsCount(), cpu_id);150    m_last_cpu = cpu_id;151    AppendEvent(lldb::eTraceEventCPUChanged);152  }153}154 155lldb::cpu_id_t DecodedThread::GetCPUByIndex(uint64_t item_index) const {156  auto it = m_cpus.upper_bound(item_index);157  return it == m_cpus.begin() ? LLDB_INVALID_CPU_ID : prev(it)->second;158}159 160std::optional<DecodedThread::TSCRange>161DecodedThread::GetTSCRangeByIndex(uint64_t item_index) const {162  auto next_it = m_tscs.upper_bound(item_index);163  if (next_it == m_tscs.begin())164    return std::nullopt;165  return prev(next_it)->second;166}167 168std::optional<DecodedThread::NanosecondsRange>169DecodedThread::GetNanosecondsRangeByIndex(uint64_t item_index) {170  auto next_it = m_nanoseconds.upper_bound(item_index);171  if (next_it == m_nanoseconds.begin())172    return std::nullopt;173  return prev(next_it)->second;174}175 176uint64_t DecodedThread::GetTotalInstructionCount() const {177  return m_insn_count;178}179 180void DecodedThread::AppendEvent(lldb::TraceEvent event) {181  CreateNewTraceItem(lldb::eTraceItemKindEvent, event);182  m_events_stats.RecordEvent(event);183}184 185void DecodedThread::AppendInstruction(const pt_insn &insn) {186  CreateNewTraceItem(lldb::eTraceItemKindInstruction, insn.ip);187  m_insn_count++;188}189 190void DecodedThread::AppendError(const IntelPTError &error) {191  CreateNewTraceItem(lldb::eTraceItemKindError, error.message());192  m_error_stats.RecordError(/*fatal=*/false);193}194 195void DecodedThread::AppendCustomError(StringRef err, bool fatal) {196  CreateNewTraceItem(lldb::eTraceItemKindError, err.str());197  m_error_stats.RecordError(fatal);198}199 200lldb::TraceEvent DecodedThread::GetEventByIndex(int item_index) const {201  return std::get<lldb::TraceEvent>(m_item_data[item_index]);202}203 204const DecodedThread::EventsStats &DecodedThread::GetEventsStats() const {205  return m_events_stats;206}207 208void DecodedThread::EventsStats::RecordEvent(lldb::TraceEvent event) {209  events_counts[event]++;210  total_count++;211}212 213uint64_t DecodedThread::ErrorStats::GetTotalCount() const {214  uint64_t total = 0;215  for (const auto &[kind, count] : libipt_errors)216    total += count;217 218  return total + other_errors + fatal_errors;219}220 221void DecodedThread::ErrorStats::RecordError(bool fatal) {222  if (fatal)223    fatal_errors++;224  else225    other_errors++;226}227 228void DecodedThread::ErrorStats::RecordError(int libipt_error_code) {229  libipt_errors[pt_errstr(pt_errcode(libipt_error_code))]++;230}231 232const DecodedThread::ErrorStats &DecodedThread::GetErrorStats() const {233  return m_error_stats;234}235 236lldb::TraceItemKind237DecodedThread::GetItemKindByIndex(uint64_t item_index) const {238  return std::visit(239      llvm::makeVisitor(240          [](const std::string &) { return lldb::eTraceItemKindError; },241          [](lldb::TraceEvent) { return lldb::eTraceItemKindEvent; },242          [](lldb::addr_t) { return lldb::eTraceItemKindInstruction; }),243      m_item_data[item_index]);244}245 246llvm::StringRef DecodedThread::GetErrorByIndex(uint64_t item_index) const {247  if (item_index >= m_item_data.size())248    return llvm::StringRef();249  return std::get<std::string>(m_item_data[item_index]);250}251 252DecodedThread::DecodedThread(253    ThreadSP thread_sp,254    const std::optional<LinuxPerfZeroTscConversion> &tsc_conversion)255    : m_thread_sp(thread_sp), m_tsc_conversion(tsc_conversion) {}256 257size_t DecodedThread::CalculateApproximateMemoryUsage() const {258  return sizeof(TraceItemStorage) * m_item_data.size() +259         (sizeof(uint64_t) + sizeof(TSC)) * m_tscs.size() +260         (sizeof(uint64_t) + sizeof(uint64_t)) * m_nanoseconds.size() +261         (sizeof(uint64_t) + sizeof(lldb::cpu_id_t)) * m_cpus.size();262}263