brintos

brintos / llvm-project-archived public Read only

0
0
Text · 11.1 KiB · a48c55c Raw
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