aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp')
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp262
1 files changed, 262 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
new file mode 100644
index 000000000000..9c075398d547
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
@@ -0,0 +1,262 @@
+//===-- DecodedThread.cpp -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DecodedThread.h"
+#include "TraceCursorIntelPT.h"
+#include <intel-pt.h>
+#include <memory>
+#include <optional>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::trace_intel_pt;
+using namespace llvm;
+
+char IntelPTError::ID;
+
+IntelPTError::IntelPTError(int libipt_error_code, lldb::addr_t address)
+ : m_libipt_error_code(libipt_error_code), m_address(address) {
+ assert(libipt_error_code < 0);
+}
+
+void IntelPTError::log(llvm::raw_ostream &OS) const {
+ OS << pt_errstr(pt_errcode(m_libipt_error_code));
+ if (m_address != LLDB_INVALID_ADDRESS && m_address > 0)
+ OS << formatv(": {0:x+16}", m_address);
+}
+
+bool DecodedThread::TSCRange::InRange(uint64_t item_index) const {
+ return item_index >= first_item_index &&
+ item_index < first_item_index + items_count;
+}
+
+bool DecodedThread::NanosecondsRange::InRange(uint64_t item_index) const {
+ return item_index >= first_item_index &&
+ item_index < first_item_index + items_count;
+}
+
+double DecodedThread::NanosecondsRange::GetInterpolatedTime(
+ uint64_t item_index, uint64_t begin_of_time_nanos,
+ const LinuxPerfZeroTscConversion &tsc_conversion) const {
+ uint64_t items_since_last_tsc = item_index - first_item_index;
+
+ auto interpolate = [&](uint64_t next_range_start_ns) {
+ if (next_range_start_ns == nanos) {
+ // If the resolution of the conversion formula is bad enough to consider
+ // these two timestamps as equal, then we just increase the next one by 1
+ // for correction
+ next_range_start_ns++;
+ }
+ long double item_duration =
+ static_cast<long double>(items_count) / (next_range_start_ns - nanos);
+ return (nanos - begin_of_time_nanos) + items_since_last_tsc * item_duration;
+ };
+
+ if (!next_range) {
+ // If this is the last TSC range, so we have to extrapolate. In this case,
+ // we assume that each instruction took one TSC, which is what an
+ // instruction would take if no parallelism is achieved and the frequency
+ // multiplier is 1.
+ return interpolate(tsc_conversion.ToNanos(tsc + items_count));
+ }
+ if (items_count < (next_range->tsc - tsc)) {
+ // If the numbers of items in this range is less than the total TSC duration
+ // of this range, i.e. each instruction taking longer than 1 TSC, then we
+ // can assume that something else happened between these TSCs (e.g. a
+ // context switch, change to kernel, decoding errors, etc). In this case, we
+ // also assume that each instruction took 1 TSC. A proper way to improve
+ // this would be to analize the next events in the trace looking for context
+ // switches or trace disablement events, but for now, as we only want an
+ // approximation, we keep it simple. We are also guaranteed that the time in
+ // nanos of the next range is different to the current one, just because of
+ // the definition of a NanosecondsRange.
+ return interpolate(
+ std::min(tsc_conversion.ToNanos(tsc + items_count), next_range->nanos));
+ }
+
+ // In this case, each item took less than 1 TSC, so some parallelism was
+ // achieved, which is an indication that we didn't suffered of any kind of
+ // interruption.
+ return interpolate(next_range->nanos);
+}
+
+uint64_t DecodedThread::GetItemsCount() const { return m_item_data.size(); }
+
+lldb::addr_t
+DecodedThread::GetInstructionLoadAddress(uint64_t item_index) const {
+ return std::get<lldb::addr_t>(m_item_data[item_index]);
+}
+
+lldb::addr_t
+DecodedThread::GetSyncPointOffsetByIndex(uint64_t item_index) const {
+ return m_psb_offsets.find(item_index)->second;
+}
+
+ThreadSP DecodedThread::GetThread() { return m_thread_sp; }
+
+template <typename Data>
+DecodedThread::TraceItemStorage &
+DecodedThread::CreateNewTraceItem(lldb::TraceItemKind kind, Data &&data) {
+ m_item_data.emplace_back(data);
+
+ if (m_last_tsc)
+ (*m_last_tsc)->second.items_count++;
+ if (m_last_nanoseconds)
+ (*m_last_nanoseconds)->second.items_count++;
+
+ return m_item_data.back();
+}
+
+void DecodedThread::NotifySyncPoint(lldb::addr_t psb_offset) {
+ m_psb_offsets.try_emplace(GetItemsCount(), psb_offset);
+ AppendEvent(lldb::eTraceEventSyncPoint);
+}
+
+void DecodedThread::NotifyTsc(TSC tsc) {
+ if (m_last_tsc && (*m_last_tsc)->second.tsc == tsc)
+ return;
+ if (m_last_tsc)
+ assert(tsc >= (*m_last_tsc)->second.tsc &&
+ "We can't have decreasing times");
+
+ m_last_tsc =
+ m_tscs.emplace(GetItemsCount(), TSCRange{tsc, 0, GetItemsCount()}).first;
+
+ if (m_tsc_conversion) {
+ uint64_t nanos = m_tsc_conversion->ToNanos(tsc);
+ if (!m_last_nanoseconds || (*m_last_nanoseconds)->second.nanos != nanos) {
+ m_last_nanoseconds =
+ m_nanoseconds
+ .emplace(GetItemsCount(), NanosecondsRange{nanos, tsc, nullptr, 0,
+ GetItemsCount()})
+ .first;
+ if (*m_last_nanoseconds != m_nanoseconds.begin()) {
+ auto prev_range = prev(*m_last_nanoseconds);
+ prev_range->second.next_range = &(*m_last_nanoseconds)->second;
+ }
+ }
+ }
+ AppendEvent(lldb::eTraceEventHWClockTick);
+}
+
+void DecodedThread::NotifyCPU(lldb::cpu_id_t cpu_id) {
+ if (!m_last_cpu || *m_last_cpu != cpu_id) {
+ m_cpus.emplace(GetItemsCount(), cpu_id);
+ m_last_cpu = cpu_id;
+ AppendEvent(lldb::eTraceEventCPUChanged);
+ }
+}
+
+lldb::cpu_id_t DecodedThread::GetCPUByIndex(uint64_t item_index) const {
+ auto it = m_cpus.upper_bound(item_index);
+ return it == m_cpus.begin() ? LLDB_INVALID_CPU_ID : prev(it)->second;
+}
+
+std::optional<DecodedThread::TSCRange>
+DecodedThread::GetTSCRangeByIndex(uint64_t item_index) const {
+ auto next_it = m_tscs.upper_bound(item_index);
+ if (next_it == m_tscs.begin())
+ return std::nullopt;
+ return prev(next_it)->second;
+}
+
+std::optional<DecodedThread::NanosecondsRange>
+DecodedThread::GetNanosecondsRangeByIndex(uint64_t item_index) {
+ auto next_it = m_nanoseconds.upper_bound(item_index);
+ if (next_it == m_nanoseconds.begin())
+ return std::nullopt;
+ return prev(next_it)->second;
+}
+
+uint64_t DecodedThread::GetTotalInstructionCount() const {
+ return m_insn_count;
+}
+
+void DecodedThread::AppendEvent(lldb::TraceEvent event) {
+ CreateNewTraceItem(lldb::eTraceItemKindEvent, event);
+ m_events_stats.RecordEvent(event);
+}
+
+void DecodedThread::AppendInstruction(const pt_insn &insn) {
+ CreateNewTraceItem(lldb::eTraceItemKindInstruction, insn.ip);
+ m_insn_count++;
+}
+
+void DecodedThread::AppendError(const IntelPTError &error) {
+ CreateNewTraceItem(lldb::eTraceItemKindError, error.message());
+ m_error_stats.RecordError(/*fatal=*/false);
+}
+
+void DecodedThread::AppendCustomError(StringRef err, bool fatal) {
+ CreateNewTraceItem(lldb::eTraceItemKindError, err.str());
+ m_error_stats.RecordError(fatal);
+}
+
+lldb::TraceEvent DecodedThread::GetEventByIndex(int item_index) const {
+ return std::get<lldb::TraceEvent>(m_item_data[item_index]);
+}
+
+const DecodedThread::EventsStats &DecodedThread::GetEventsStats() const {
+ return m_events_stats;
+}
+
+void DecodedThread::EventsStats::RecordEvent(lldb::TraceEvent event) {
+ events_counts[event]++;
+ total_count++;
+}
+
+uint64_t DecodedThread::ErrorStats::GetTotalCount() const {
+ uint64_t total = 0;
+ for (const auto &[kind, count] : libipt_errors)
+ total += count;
+
+ return total + other_errors + fatal_errors;
+}
+
+void DecodedThread::ErrorStats::RecordError(bool fatal) {
+ if (fatal)
+ fatal_errors++;
+ else
+ other_errors++;
+}
+
+void DecodedThread::ErrorStats::RecordError(int libipt_error_code) {
+ libipt_errors[pt_errstr(pt_errcode(libipt_error_code))]++;
+}
+
+const DecodedThread::ErrorStats &DecodedThread::GetErrorStats() const {
+ return m_error_stats;
+}
+
+lldb::TraceItemKind
+DecodedThread::GetItemKindByIndex(uint64_t item_index) const {
+ return std::visit(
+ llvm::makeVisitor(
+ [](const std::string &) { return lldb::eTraceItemKindError; },
+ [](lldb::TraceEvent) { return lldb::eTraceItemKindEvent; },
+ [](lldb::addr_t) { return lldb::eTraceItemKindInstruction; }),
+ m_item_data[item_index]);
+}
+
+llvm::StringRef DecodedThread::GetErrorByIndex(uint64_t item_index) const {
+ if (item_index >= m_item_data.size())
+ return llvm::StringRef();
+ return std::get<std::string>(m_item_data[item_index]);
+}
+
+DecodedThread::DecodedThread(
+ ThreadSP thread_sp,
+ const std::optional<LinuxPerfZeroTscConversion> &tsc_conversion)
+ : m_thread_sp(thread_sp), m_tsc_conversion(tsc_conversion) {}
+
+size_t DecodedThread::CalculateApproximateMemoryUsage() const {
+ return sizeof(TraceItemStorage) * m_item_data.size() +
+ (sizeof(uint64_t) + sizeof(TSC)) * m_tscs.size() +
+ (sizeof(uint64_t) + sizeof(uint64_t)) * m_nanoseconds.size() +
+ (sizeof(uint64_t) + sizeof(lldb::cpu_id_t)) * m_cpus.size();
+}