331 lines · c
1//===-- DecodedThread.h -----------------------------------------*- C++ -*-===//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#ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H10#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H11 12#include "intel-pt.h"13#include "lldb/Target/Trace.h"14#include "lldb/Utility/TraceIntelPTGDBRemotePackets.h"15#include "llvm/Support/Errc.h"16#include "llvm/Support/Error.h"17#include <deque>18#include <optional>19#include <utility>20#include <variant>21 22namespace lldb_private {23namespace trace_intel_pt {24 25/// Class for representing a libipt decoding error.26class IntelPTError : public llvm::ErrorInfo<IntelPTError> {27public:28 static char ID;29 30 /// \param[in] libipt_error_code31 /// Negative number returned by libipt when decoding the trace and32 /// signaling errors.33 ///34 /// \param[in] address35 /// Optional instruction address. When decoding an individual instruction,36 /// its address might be available in the \a pt_insn object, and should be37 /// passed to this constructor. Other errors don't have an associated38 /// address.39 IntelPTError(int libipt_error_code,40 lldb::addr_t address = LLDB_INVALID_ADDRESS);41 42 std::error_code convertToErrorCode() const override {43 return llvm::errc::not_supported;44 }45 46 int GetLibiptErrorCode() const { return m_libipt_error_code; }47 48 void log(llvm::raw_ostream &OS) const override;49 50private:51 int m_libipt_error_code;52 lldb::addr_t m_address;53};54 55/// \class DecodedThread56/// Class holding the instructions and function call hierarchy obtained from57/// decoding a trace, as well as a position cursor used when reverse debugging58/// the trace.59///60/// Each decoded thread contains a cursor to the current position the user is61/// stopped at. See \a Trace::GetCursorPosition for more information.62class DecodedThread : public std::enable_shared_from_this<DecodedThread> {63public:64 using TSC = uint64_t;65 66 /// A structure that represents a maximal range of trace items associated to67 /// the same TSC value.68 struct TSCRange {69 TSC tsc;70 /// Number of trace items in this range.71 uint64_t items_count;72 /// Index of the first trace item in this range.73 uint64_t first_item_index;74 75 /// \return76 /// \b true if and only if the given \p item_index is covered by this77 /// range.78 bool InRange(uint64_t item_index) const;79 };80 81 /// A structure that represents a maximal range of trace items associated to82 /// the same non-interpolated timestamps in nanoseconds.83 struct NanosecondsRange {84 /// The nanoseconds value for this range.85 uint64_t nanos;86 /// The corresponding TSC value for this range.87 TSC tsc;88 /// A nullable pointer to the next range.89 NanosecondsRange *next_range;90 /// Number of trace items in this range.91 uint64_t items_count;92 /// Index of the first trace item in this range.93 uint64_t first_item_index;94 95 /// Calculate an interpolated timestamp in nanoseconds for the given item96 /// index. It's guaranteed that two different item indices will produce97 /// different interpolated values.98 ///99 /// \param[in] item_index100 /// The index of the item whose timestamp will be estimated. It has to be101 /// part of this range.102 ///103 /// \param[in] beginning_of_time_nanos104 /// The timestamp at which tracing started.105 ///106 /// \param[in] tsc_conversion107 /// The tsc -> nanos conversion utility108 ///109 /// \return110 /// An interpolated timestamp value for the given trace item.111 double112 GetInterpolatedTime(uint64_t item_index, uint64_t beginning_of_time_nanos,113 const LinuxPerfZeroTscConversion &tsc_conversion) const;114 115 /// \return116 /// \b true if and only if the given \p item_index is covered by this117 /// range.118 bool InRange(uint64_t item_index) const;119 };120 121 // Struct holding counts for events122 struct EventsStats {123 /// A count for each individual event kind. We use an unordered map instead124 /// of a DenseMap because DenseMap can't understand enums.125 ///126 /// Note: We can't use DenseMap because lldb::TraceEvent is not127 /// automatically handled correctly by DenseMap. We'd need to implement a128 /// custom DenseMapInfo struct for TraceEvent and that's a bit too much for129 /// such a simple structure.130 std::unordered_map<lldb::TraceEvent, uint64_t> events_counts;131 uint64_t total_count = 0;132 133 void RecordEvent(lldb::TraceEvent event);134 };135 136 // Struct holding counts for errors137 struct ErrorStats {138 /// The following counters are mutually exclusive139 /// \{140 uint64_t other_errors = 0;141 uint64_t fatal_errors = 0;142 // libipt error -> count143 llvm::DenseMap<const char *, uint64_t> libipt_errors;144 /// \}145 146 uint64_t GetTotalCount() const;147 148 void RecordError(int libipt_error_code);149 150 void RecordError(bool fatal);151 };152 153 DecodedThread(154 lldb::ThreadSP thread_sp,155 const std::optional<LinuxPerfZeroTscConversion> &tsc_conversion);156 157 /// Get the total number of instruction, errors and events from the decoded158 /// trace.159 uint64_t GetItemsCount() const;160 161 /// \return162 /// The error associated with a given trace item.163 llvm::StringRef GetErrorByIndex(uint64_t item_index) const;164 165 /// \return166 /// The trace item kind given an item index.167 lldb::TraceItemKind GetItemKindByIndex(uint64_t item_index) const;168 169 /// \return170 /// The underlying event type for the given trace item index.171 lldb::TraceEvent GetEventByIndex(int item_index) const;172 173 /// Get the most recent CPU id before or at the given trace item index.174 ///175 /// \param[in] item_index176 /// The trace item index to compare with.177 ///178 /// \return179 /// The requested cpu id, or \a LLDB_INVALID_CPU_ID if not available.180 lldb::cpu_id_t GetCPUByIndex(uint64_t item_index) const;181 182 /// \return183 /// The PSB offset associated with the given item index.184 lldb::addr_t GetSyncPointOffsetByIndex(uint64_t item_index) const;185 186 /// Get a maximal range of trace items that include the given \p item_index187 /// that have the same TSC value.188 ///189 /// \param[in] item_index190 /// The trace item index to compare with.191 ///192 /// \return193 /// The requested TSC range, or \a std::nullopt if not available.194 std::optional<DecodedThread::TSCRange>195 GetTSCRangeByIndex(uint64_t item_index) const;196 197 /// Get a maximal range of trace items that include the given \p item_index198 /// that have the same nanoseconds timestamp without interpolation.199 ///200 /// \param[in] item_index201 /// The trace item index to compare with.202 ///203 /// \return204 /// The requested nanoseconds range, or \a std::nullopt if not available.205 std::optional<DecodedThread::NanosecondsRange>206 GetNanosecondsRangeByIndex(uint64_t item_index);207 208 /// \return209 /// The load address of the instruction at the given index.210 lldb::addr_t GetInstructionLoadAddress(uint64_t item_index) const;211 212 /// \return213 /// The number of instructions in this trace (not trace items).214 uint64_t GetTotalInstructionCount() const;215 216 /// Return an object with statistics of the trace events that happened.217 ///218 /// \return219 /// The stats object of all the events.220 const EventsStats &GetEventsStats() const;221 222 /// Return an object with statistics of the trace errors that happened.223 ///224 /// \return225 /// The stats object of all the events.226 const ErrorStats &GetErrorStats() const;227 228 /// The approximate size in bytes used by this instance,229 /// including all the already decoded instructions.230 size_t CalculateApproximateMemoryUsage() const;231 232 lldb::ThreadSP GetThread();233 234 /// Notify this object that a new tsc has been seen.235 /// If this a new TSC, an event will be created.236 void NotifyTsc(TSC tsc);237 238 /// Notify this object that a CPU has been seen.239 /// If this a new CPU, an event will be created.240 void NotifyCPU(lldb::cpu_id_t cpu_id);241 242 /// Notify this object that a new PSB has been seen.243 void NotifySyncPoint(lldb::addr_t psb_offset);244 245 /// Append a decoding error.246 void AppendError(const IntelPTError &error);247 248 /// Append a custom decoding.249 ///250 /// \param[in] error251 /// The error message.252 ///253 /// \param[in] fatal254 /// If \b true, then the whole decoded thread should be discarded because a255 /// fatal anomaly has been found.256 void AppendCustomError(llvm::StringRef error, bool fatal = false);257 258 /// Append an event.259 void AppendEvent(lldb::TraceEvent);260 261 /// Append an instruction.262 void AppendInstruction(const pt_insn &insn);263 264private:265 /// When adding new members to this class, make sure266 /// to update \a CalculateApproximateMemoryUsage() accordingly.267 lldb::ThreadSP m_thread_sp;268 269 using TraceItemStorage =270 std::variant<std::string, lldb::TraceEvent, lldb::addr_t>;271 272 /// Create a new trace item.273 ///274 /// \return275 /// The index of the new item.276 template <typename Data>277 DecodedThread::TraceItemStorage &CreateNewTraceItem(lldb::TraceItemKind kind,278 Data &&data);279 280 /// Most of the trace data is stored here.281 std::deque<TraceItemStorage> m_item_data;282 283 /// This map contains the TSCs of the decoded trace items. It maps284 /// `item index -> TSC`, where `item index` is the first index285 /// at which the mapped TSC first appears. We use this representation because286 /// TSCs are sporadic and we can think of them as ranges.287 std::map<uint64_t, TSCRange> m_tscs;288 /// This is the chronologically last TSC that has been added.289 std::optional<std::map<uint64_t, TSCRange>::iterator> m_last_tsc =290 std::nullopt;291 /// This map contains the non-interpolated nanoseconds timestamps of the292 /// decoded trace items. It maps `item index -> nanoseconds`, where `item293 /// index` is the first index at which the mapped nanoseconds first appears.294 /// We use this representation because timestamps are sporadic and we think of295 /// them as ranges.296 std::map<uint64_t, NanosecondsRange> m_nanoseconds;297 std::optional<std::map<uint64_t, NanosecondsRange>::iterator>298 m_last_nanoseconds = std::nullopt;299 300 // The cpu information is stored as a map. It maps `item index -> CPU`.301 // A CPU is associated with the next instructions that follow until the next302 // cpu is seen.303 std::map<uint64_t, lldb::cpu_id_t> m_cpus;304 /// This is the chronologically last CPU ID.305 std::optional<uint64_t> m_last_cpu;306 307 // The PSB offsets are stored as a map. It maps `item index -> psb offset`.308 llvm::DenseMap<uint64_t, lldb::addr_t> m_psb_offsets;309 310 /// TSC -> nanos conversion utility.311 std::optional<LinuxPerfZeroTscConversion> m_tsc_conversion;312 313 /// Statistics of all tracing errors.314 ErrorStats m_error_stats;315 316 /// Statistics of all tracing events.317 EventsStats m_events_stats;318 /// Total amount of time spent decoding.319 std::chrono::milliseconds m_total_decoding_time{0};320 321 /// Total number of instructions in the trace.322 uint64_t m_insn_count = 0;323};324 325using DecodedThreadSP = std::shared_ptr<DecodedThread>;326 327} // namespace trace_intel_pt328} // namespace lldb_private329 330#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H331