aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/lldb/source/Plugins/Trace
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/lldb/source/Plugins/Trace')
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Trace/common/ThreadPostMortemTrace.cpp43
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Trace/common/ThreadPostMortemTrace.h59
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.cpp212
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.h134
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp262
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h330
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.cpp783
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.h125
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.cpp332
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.h148
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TaskTimer.cpp32
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TaskTimer.h78
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.cpp67
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.h59
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp146
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h93
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp744
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h293
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.cpp437
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.h130
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.cpp402
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.h48
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTConstants.h36
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp189
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h101
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCpuDecoder.cpp238
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCpuDecoder.h110
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td132
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTProperties.td24
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/forward-declarations.h24
30 files changed, 5811 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/common/ThreadPostMortemTrace.cpp b/contrib/llvm-project/lldb/source/Plugins/Trace/common/ThreadPostMortemTrace.cpp
new file mode 100644
index 000000000000..564a52eaaf82
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Trace/common/ThreadPostMortemTrace.cpp
@@ -0,0 +1,43 @@
+//===-- ThreadPostMortemTrace.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 "ThreadPostMortemTrace.h"
+
+#include <memory>
+#include <optional>
+
+#include "Plugins/Process/Utility/RegisterContextHistory.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace llvm;
+
+void ThreadPostMortemTrace::RefreshStateAfterStop() {}
+
+RegisterContextSP ThreadPostMortemTrace::GetRegisterContext() {
+ if (!m_reg_context_sp)
+ m_reg_context_sp = CreateRegisterContextForFrame(nullptr);
+
+ return m_reg_context_sp;
+}
+
+RegisterContextSP
+ThreadPostMortemTrace::CreateRegisterContextForFrame(StackFrame *frame) {
+ // Eventually this will calculate the register context based on the current
+ // trace position.
+ return std::make_shared<RegisterContextHistory>(
+ *this, 0, GetProcess()->GetAddressByteSize(), LLDB_INVALID_ADDRESS);
+}
+
+bool ThreadPostMortemTrace::CalculateStopInfo() { return false; }
+
+const std::optional<FileSpec> &ThreadPostMortemTrace::GetTraceFile() const {
+ return m_trace_file;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/common/ThreadPostMortemTrace.h b/contrib/llvm-project/lldb/source/Plugins/Trace/common/ThreadPostMortemTrace.h
new file mode 100644
index 000000000000..39f53fe09f88
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Trace/common/ThreadPostMortemTrace.h
@@ -0,0 +1,59 @@
+//===-- ThreadPostMortemTrace.h ---------------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_TARGET_THREADPOSTMORTEMTRACE_H
+#define LLDB_TARGET_THREADPOSTMORTEMTRACE_H
+
+#include "lldb/Target/Thread.h"
+#include <optional>
+
+namespace lldb_private {
+
+/// \class ThreadPostMortemTrace ThreadPostMortemTrace.h
+///
+/// Thread implementation used for representing threads gotten from trace
+/// session files, which are similar to threads from core files.
+///
+class ThreadPostMortemTrace : public Thread {
+public:
+ /// \param[in] process
+ /// The process who owns this thread.
+ ///
+ /// \param[in] tid
+ /// The tid of this thread.
+ ///
+ /// \param[in] trace_file
+ /// The file that contains the list of instructions that were traced when
+ /// this thread was being executed.
+ ThreadPostMortemTrace(Process &process, lldb::tid_t tid,
+ const std::optional<FileSpec> &trace_file)
+ : Thread(process, tid), m_trace_file(trace_file) {}
+
+ void RefreshStateAfterStop() override;
+
+ lldb::RegisterContextSP GetRegisterContext() override;
+
+ lldb::RegisterContextSP
+ CreateRegisterContextForFrame(StackFrame *frame) override;
+
+ /// \return
+ /// The trace file of this thread.
+ const std::optional<FileSpec> &GetTraceFile() const;
+
+protected:
+ bool CalculateStopInfo() override;
+
+ lldb::RegisterContextSP m_thread_reg_ctx_sp;
+
+private:
+ std::optional<FileSpec> m_trace_file;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_TARGET_THREADPOSTMORTEMTRACE_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.cpp b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.cpp
new file mode 100644
index 000000000000..44224229e625
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.cpp
@@ -0,0 +1,212 @@
+//===-- CommandObjectTraceStartIntelPT.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 "CommandObjectTraceStartIntelPT.h"
+#include "TraceIntelPT.h"
+#include "TraceIntelPTConstants.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandOptionArgumentTable.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Trace.h"
+#include <optional>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::trace_intel_pt;
+using namespace llvm;
+
+// CommandObjectThreadTraceStartIntelPT
+
+#define LLDB_OPTIONS_thread_trace_start_intel_pt
+#include "TraceIntelPTCommandOptions.inc"
+
+Status CommandObjectThreadTraceStartIntelPT::CommandOptions::SetOptionValue(
+ uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 's': {
+ if (std::optional<uint64_t> bytes =
+ ParsingUtils::ParseUserFriendlySizeExpression(option_arg))
+ m_ipt_trace_size = *bytes;
+ else
+ error.SetErrorStringWithFormat("invalid bytes expression for '%s'",
+ option_arg.str().c_str());
+ break;
+ }
+ case 't': {
+ m_enable_tsc = true;
+ break;
+ }
+ case 'p': {
+ int64_t psb_period;
+ if (option_arg.empty() || option_arg.getAsInteger(0, psb_period) ||
+ psb_period < 0)
+ error.SetErrorStringWithFormat("invalid integer value for option '%s'",
+ option_arg.str().c_str());
+ else
+ m_psb_period = psb_period;
+ break;
+ }
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+}
+
+void CommandObjectThreadTraceStartIntelPT::CommandOptions::
+ OptionParsingStarting(ExecutionContext *execution_context) {
+ m_ipt_trace_size = kDefaultIptTraceSize;
+ m_enable_tsc = kDefaultEnableTscValue;
+ m_psb_period = kDefaultPsbPeriod;
+}
+
+llvm::ArrayRef<OptionDefinition>
+CommandObjectThreadTraceStartIntelPT::CommandOptions::GetDefinitions() {
+ return llvm::ArrayRef(g_thread_trace_start_intel_pt_options);
+}
+
+bool CommandObjectThreadTraceStartIntelPT::DoExecuteOnThreads(
+ Args &command, CommandReturnObject &result,
+ llvm::ArrayRef<lldb::tid_t> tids) {
+ if (Error err = m_trace.Start(tids, m_options.m_ipt_trace_size,
+ m_options.m_enable_tsc, m_options.m_psb_period))
+ result.SetError(Status(std::move(err)));
+ else
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+
+ return result.Succeeded();
+}
+
+/// CommandObjectProcessTraceStartIntelPT
+
+#define LLDB_OPTIONS_process_trace_start_intel_pt
+#include "TraceIntelPTCommandOptions.inc"
+
+Status CommandObjectProcessTraceStartIntelPT::CommandOptions::SetOptionValue(
+ uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 's': {
+ if (std::optional<uint64_t> bytes =
+ ParsingUtils::ParseUserFriendlySizeExpression(option_arg))
+ m_ipt_trace_size = *bytes;
+ else
+ error.SetErrorStringWithFormat("invalid bytes expression for '%s'",
+ option_arg.str().c_str());
+ break;
+ }
+ case 'l': {
+ if (std::optional<uint64_t> bytes =
+ ParsingUtils::ParseUserFriendlySizeExpression(option_arg))
+ m_process_buffer_size_limit = *bytes;
+ else
+ error.SetErrorStringWithFormat("invalid bytes expression for '%s'",
+ option_arg.str().c_str());
+ break;
+ }
+ case 't': {
+ m_enable_tsc = true;
+ break;
+ }
+ case 'c': {
+ m_per_cpu_tracing = true;
+ break;
+ }
+ case 'd': {
+ m_disable_cgroup_filtering = true;
+ break;
+ }
+ case 'p': {
+ int64_t psb_period;
+ if (option_arg.empty() || option_arg.getAsInteger(0, psb_period) ||
+ psb_period < 0)
+ error.SetErrorStringWithFormat("invalid integer value for option '%s'",
+ option_arg.str().c_str());
+ else
+ m_psb_period = psb_period;
+ break;
+ }
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+}
+
+void CommandObjectProcessTraceStartIntelPT::CommandOptions::
+ OptionParsingStarting(ExecutionContext *execution_context) {
+ m_ipt_trace_size = kDefaultIptTraceSize;
+ m_process_buffer_size_limit = kDefaultProcessBufferSizeLimit;
+ m_enable_tsc = kDefaultEnableTscValue;
+ m_psb_period = kDefaultPsbPeriod;
+ m_per_cpu_tracing = kDefaultPerCpuTracing;
+ m_disable_cgroup_filtering = kDefaultDisableCgroupFiltering;
+}
+
+llvm::ArrayRef<OptionDefinition>
+CommandObjectProcessTraceStartIntelPT::CommandOptions::GetDefinitions() {
+ return llvm::ArrayRef(g_process_trace_start_intel_pt_options);
+}
+
+void CommandObjectProcessTraceStartIntelPT::DoExecute(
+ Args &command, CommandReturnObject &result) {
+ if (Error err = m_trace.Start(
+ m_options.m_ipt_trace_size, m_options.m_process_buffer_size_limit,
+ m_options.m_enable_tsc, m_options.m_psb_period,
+ m_options.m_per_cpu_tracing, m_options.m_disable_cgroup_filtering))
+ result.SetError(Status(std::move(err)));
+ else
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+}
+
+std::optional<uint64_t>
+ParsingUtils::ParseUserFriendlySizeExpression(llvm::StringRef size_expression) {
+ if (size_expression.empty()) {
+ return std::nullopt;
+ }
+ const uint64_t kBytesMultiplier = 1;
+ const uint64_t kKibiBytesMultiplier = 1024;
+ const uint64_t kMebiBytesMultiplier = 1024 * 1024;
+
+ DenseMap<StringRef, uint64_t> multipliers = {
+ {"mib", kMebiBytesMultiplier}, {"mb", kMebiBytesMultiplier},
+ {"m", kMebiBytesMultiplier}, {"kib", kKibiBytesMultiplier},
+ {"kb", kKibiBytesMultiplier}, {"k", kKibiBytesMultiplier},
+ {"b", kBytesMultiplier}, {"", kBytesMultiplier}};
+
+ const auto non_digit_index = size_expression.find_first_not_of("0123456789");
+ if (non_digit_index == 0) { // expression starts from from non-digit char.
+ return std::nullopt;
+ }
+
+ const llvm::StringRef number_part =
+ non_digit_index == llvm::StringRef::npos
+ ? size_expression
+ : size_expression.substr(0, non_digit_index);
+ uint64_t parsed_number;
+ if (number_part.getAsInteger(10, parsed_number)) {
+ return std::nullopt;
+ }
+
+ if (non_digit_index != llvm::StringRef::npos) { // if expression has units.
+ const auto multiplier = size_expression.substr(non_digit_index).lower();
+
+ auto it = multipliers.find(multiplier);
+ if (it == multipliers.end())
+ return std::nullopt;
+
+ return parsed_number * it->second;
+ } else {
+ return parsed_number;
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.h b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.h
new file mode 100644
index 000000000000..82714dea3fcd
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.h
@@ -0,0 +1,134 @@
+//===-- CommandObjectTraceStartIntelPT.h ----------------------*- C++ //-*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_COMMANDOBJECTTRACESTARTINTELPT_H
+#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_COMMANDOBJECTTRACESTARTINTELPT_H
+
+#include "../../../../source/Commands/CommandObjectTrace.h"
+#include "TraceIntelPT.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include <optional>
+
+namespace lldb_private {
+namespace trace_intel_pt {
+
+class CommandObjectThreadTraceStartIntelPT
+ : public CommandObjectMultipleThreads {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() { OptionParsingStarting(nullptr); }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override;
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override;
+
+ uint64_t m_ipt_trace_size;
+ bool m_enable_tsc;
+ std::optional<uint64_t> m_psb_period;
+ };
+
+ CommandObjectThreadTraceStartIntelPT(TraceIntelPT &trace,
+ CommandInterpreter &interpreter)
+ : CommandObjectMultipleThreads(
+ interpreter, "thread trace start",
+ "Start tracing one or more threads with intel-pt. "
+ "Defaults to the current thread. Thread indices can be "
+ "specified as arguments.\n Use the thread-index \"all\" to trace "
+ "all threads including future threads.",
+ "thread trace start [<thread-index> <thread-index> ...] "
+ "[<intel-pt-options>]",
+ lldb::eCommandRequiresProcess | lldb::eCommandTryTargetAPILock |
+ lldb::eCommandProcessMustBeLaunched |
+ lldb::eCommandProcessMustBePaused),
+ m_trace(trace), m_options() {}
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecuteOnThreads(Args &command, CommandReturnObject &result,
+ llvm::ArrayRef<lldb::tid_t> tids) override;
+
+ TraceIntelPT &m_trace;
+ CommandOptions m_options;
+};
+
+class CommandObjectProcessTraceStartIntelPT : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() { OptionParsingStarting(nullptr); }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override;
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override;
+
+ uint64_t m_ipt_trace_size;
+ uint64_t m_process_buffer_size_limit;
+ bool m_enable_tsc;
+ std::optional<uint64_t> m_psb_period;
+ bool m_per_cpu_tracing;
+ bool m_disable_cgroup_filtering;
+ };
+
+ CommandObjectProcessTraceStartIntelPT(TraceIntelPT &trace,
+ CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "process trace start",
+ "Start tracing this process with intel-pt, including future "
+ "threads. If --per-cpu-tracing is not provided, this traces each "
+ "thread independently, thus using a trace buffer per thread. "
+ "Threads traced with the \"thread trace start\" command are left "
+ "unaffected ant not retraced. This is the recommended option "
+ "unless the number of threads is huge. If --per-cpu-tracing is "
+ "passed, each cpu core is traced instead of each thread, which "
+ "uses a fixed number of trace buffers, but might result in less "
+ "data available for less frequent threads.",
+ "process trace start [<intel-pt-options>]",
+ lldb::eCommandRequiresProcess | lldb::eCommandTryTargetAPILock |
+ lldb::eCommandProcessMustBeLaunched |
+ lldb::eCommandProcessMustBePaused),
+ m_trace(trace), m_options() {}
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ void DoExecute(Args &command, CommandReturnObject &result) override;
+
+ TraceIntelPT &m_trace;
+ CommandOptions m_options;
+};
+
+namespace ParsingUtils {
+/// Convert an integral size expression like 12KiB or 4MB into bytes. The units
+/// are taken loosely to help users input sizes into LLDB, e.g. KiB and KB are
+/// considered the same (2^20 bytes) for simplicity.
+///
+/// \param[in] size_expression
+/// String expression which is an integral number plus a unit that can be
+/// lower or upper case. Supported units: K, KB and KiB for 2^10 bytes; M,
+/// MB and MiB for 2^20 bytes; and B for bytes. A single integral number is
+/// considered bytes.
+/// \return
+/// The converted number of bytes or \a std::nullopt if the expression is
+/// invalid.
+std::optional<uint64_t>
+ParseUserFriendlySizeExpression(llvm::StringRef size_expression);
+} // namespace ParsingUtils
+
+} // namespace trace_intel_pt
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_COMMANDOBJECTTRACESTARTINTELPT_H
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();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
new file mode 100644
index 000000000000..a48c55cc76df
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
@@ -0,0 +1,330 @@
+//===-- DecodedThread.h -----------------------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H
+#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H
+
+#include "intel-pt.h"
+#include "lldb/Target/Trace.h"
+#include "lldb/Utility/TraceIntelPTGDBRemotePackets.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
+#include <deque>
+#include <optional>
+#include <utility>
+#include <variant>
+
+namespace lldb_private {
+namespace trace_intel_pt {
+
+/// Class for representing a libipt decoding error.
+class IntelPTError : public llvm::ErrorInfo<IntelPTError> {
+public:
+ static char ID;
+
+ /// \param[in] libipt_error_code
+ /// Negative number returned by libipt when decoding the trace and
+ /// signaling errors.
+ ///
+ /// \param[in] address
+ /// Optional instruction address. When decoding an individual instruction,
+ /// its address might be available in the \a pt_insn object, and should be
+ /// passed to this constructor. Other errors don't have an associated
+ /// address.
+ IntelPTError(int libipt_error_code,
+ lldb::addr_t address = LLDB_INVALID_ADDRESS);
+
+ std::error_code convertToErrorCode() const override {
+ return llvm::errc::not_supported;
+ }
+
+ int GetLibiptErrorCode() const { return m_libipt_error_code; }
+
+ void log(llvm::raw_ostream &OS) const override;
+
+private:
+ int m_libipt_error_code;
+ lldb::addr_t m_address;
+};
+
+/// \class DecodedThread
+/// Class holding the instructions and function call hierarchy obtained from
+/// decoding a trace, as well as a position cursor used when reverse debugging
+/// the trace.
+///
+/// Each decoded thread contains a cursor to the current position the user is
+/// stopped at. See \a Trace::GetCursorPosition for more information.
+class DecodedThread : public std::enable_shared_from_this<DecodedThread> {
+public:
+ using TSC = uint64_t;
+
+ /// A structure that represents a maximal range of trace items associated to
+ /// the same TSC value.
+ struct TSCRange {
+ TSC tsc;
+ /// Number of trace items in this range.
+ uint64_t items_count;
+ /// Index of the first trace item in this range.
+ uint64_t first_item_index;
+
+ /// \return
+ /// \b true if and only if the given \p item_index is covered by this
+ /// range.
+ bool InRange(uint64_t item_index) const;
+ };
+
+ /// A structure that represents a maximal range of trace items associated to
+ /// the same non-interpolated timestamps in nanoseconds.
+ struct NanosecondsRange {
+ /// The nanoseconds value for this range.
+ uint64_t nanos;
+ /// The corresponding TSC value for this range.
+ TSC tsc;
+ /// A nullable pointer to the next range.
+ NanosecondsRange *next_range;
+ /// Number of trace items in this range.
+ uint64_t items_count;
+ /// Index of the first trace item in this range.
+ uint64_t first_item_index;
+
+ /// Calculate an interpolated timestamp in nanoseconds for the given item
+ /// index. It's guaranteed that two different item indices will produce
+ /// different interpolated values.
+ ///
+ /// \param[in] item_index
+ /// The index of the item whose timestamp will be estimated. It has to be
+ /// part of this range.
+ ///
+ /// \param[in] beginning_of_time_nanos
+ /// The timestamp at which tracing started.
+ ///
+ /// \param[in] tsc_conversion
+ /// The tsc -> nanos conversion utility
+ ///
+ /// \return
+ /// An interpolated timestamp value for the given trace item.
+ double
+ GetInterpolatedTime(uint64_t item_index, uint64_t beginning_of_time_nanos,
+ const LinuxPerfZeroTscConversion &tsc_conversion) const;
+
+ /// \return
+ /// \b true if and only if the given \p item_index is covered by this
+ /// range.
+ bool InRange(uint64_t item_index) const;
+ };
+
+ // Struct holding counts for events
+ struct EventsStats {
+ /// A count for each individual event kind. We use an unordered map instead
+ /// of a DenseMap because DenseMap can't understand enums.
+ ///
+ /// Note: We can't use DenseMap because lldb::TraceEvent is not
+ /// automatically handled correctly by DenseMap. We'd need to implement a
+ /// custom DenseMapInfo struct for TraceEvent and that's a bit too much for
+ /// such a simple structure.
+ std::unordered_map<lldb::TraceEvent, uint64_t> events_counts;
+ uint64_t total_count = 0;
+
+ void RecordEvent(lldb::TraceEvent event);
+ };
+
+ // Struct holding counts for errors
+ struct ErrorStats {
+ /// The following counters are mutually exclusive
+ /// \{
+ uint64_t other_errors = 0;
+ uint64_t fatal_errors = 0;
+ // libipt error -> count
+ llvm::DenseMap<const char *, uint64_t> libipt_errors;
+ /// \}
+
+ uint64_t GetTotalCount() const;
+
+ void RecordError(int libipt_error_code);
+
+ void RecordError(bool fatal);
+ };
+
+ DecodedThread(
+ lldb::ThreadSP thread_sp,
+ const std::optional<LinuxPerfZeroTscConversion> &tsc_conversion);
+
+ /// Get the total number of instruction, errors and events from the decoded
+ /// trace.
+ uint64_t GetItemsCount() const;
+
+ /// \return
+ /// The error associated with a given trace item.
+ llvm::StringRef GetErrorByIndex(uint64_t item_index) const;
+
+ /// \return
+ /// The trace item kind given an item index.
+ lldb::TraceItemKind GetItemKindByIndex(uint64_t item_index) const;
+
+ /// \return
+ /// The underlying event type for the given trace item index.
+ lldb::TraceEvent GetEventByIndex(int item_index) const;
+
+ /// Get the most recent CPU id before or at the given trace item index.
+ ///
+ /// \param[in] item_index
+ /// The trace item index to compare with.
+ ///
+ /// \return
+ /// The requested cpu id, or \a LLDB_INVALID_CPU_ID if not available.
+ lldb::cpu_id_t GetCPUByIndex(uint64_t item_index) const;
+
+ /// \return
+ /// The PSB offset associated with the given item index.
+ lldb::addr_t GetSyncPointOffsetByIndex(uint64_t item_index) const;
+
+ /// Get a maximal range of trace items that include the given \p item_index
+ /// that have the same TSC value.
+ ///
+ /// \param[in] item_index
+ /// The trace item index to compare with.
+ ///
+ /// \return
+ /// The requested TSC range, or \a std::nullopt if not available.
+ std::optional<DecodedThread::TSCRange>
+ GetTSCRangeByIndex(uint64_t item_index) const;
+
+ /// Get a maximal range of trace items that include the given \p item_index
+ /// that have the same nanoseconds timestamp without interpolation.
+ ///
+ /// \param[in] item_index
+ /// The trace item index to compare with.
+ ///
+ /// \return
+ /// The requested nanoseconds range, or \a std::nullopt if not available.
+ std::optional<DecodedThread::NanosecondsRange>
+ GetNanosecondsRangeByIndex(uint64_t item_index);
+
+ /// \return
+ /// The load address of the instruction at the given index.
+ lldb::addr_t GetInstructionLoadAddress(uint64_t item_index) const;
+
+ /// \return
+ /// The number of instructions in this trace (not trace items).
+ uint64_t GetTotalInstructionCount() const;
+
+ /// Return an object with statistics of the trace events that happened.
+ ///
+ /// \return
+ /// The stats object of all the events.
+ const EventsStats &GetEventsStats() const;
+
+ /// Return an object with statistics of the trace errors that happened.
+ ///
+ /// \return
+ /// The stats object of all the events.
+ const ErrorStats &GetErrorStats() const;
+
+ /// The approximate size in bytes used by this instance,
+ /// including all the already decoded instructions.
+ size_t CalculateApproximateMemoryUsage() const;
+
+ lldb::ThreadSP GetThread();
+
+ /// Notify this object that a new tsc has been seen.
+ /// If this a new TSC, an event will be created.
+ void NotifyTsc(TSC tsc);
+
+ /// Notify this object that a CPU has been seen.
+ /// If this a new CPU, an event will be created.
+ void NotifyCPU(lldb::cpu_id_t cpu_id);
+
+ /// Notify this object that a new PSB has been seen.
+ void NotifySyncPoint(lldb::addr_t psb_offset);
+
+ /// Append a decoding error.
+ void AppendError(const IntelPTError &error);
+
+ /// Append a custom decoding.
+ ///
+ /// \param[in] error
+ /// The error message.
+ ///
+ /// \param[in] fatal
+ /// If \b true, then the whole decoded thread should be discarded because a
+ /// fatal anomaly has been found.
+ void AppendCustomError(llvm::StringRef error, bool fatal = false);
+
+ /// Append an event.
+ void AppendEvent(lldb::TraceEvent);
+
+ /// Append an instruction.
+ void AppendInstruction(const pt_insn &insn);
+
+private:
+ /// When adding new members to this class, make sure
+ /// to update \a CalculateApproximateMemoryUsage() accordingly.
+ lldb::ThreadSP m_thread_sp;
+
+ using TraceItemStorage =
+ std::variant<std::string, lldb::TraceEvent, lldb::addr_t>;
+
+ /// Create a new trace item.
+ ///
+ /// \return
+ /// The index of the new item.
+ template <typename Data>
+ DecodedThread::TraceItemStorage &CreateNewTraceItem(lldb::TraceItemKind kind,
+ Data &&data);
+
+ /// Most of the trace data is stored here.
+ std::deque<TraceItemStorage> m_item_data;
+
+ /// This map contains the TSCs of the decoded trace items. It maps
+ /// `item index -> TSC`, where `item index` is the first index
+ /// at which the mapped TSC first appears. We use this representation because
+ /// TSCs are sporadic and we can think of them as ranges.
+ std::map<uint64_t, TSCRange> m_tscs;
+ /// This is the chronologically last TSC that has been added.
+ std::optional<std::map<uint64_t, TSCRange>::iterator> m_last_tsc =
+ std::nullopt;
+ /// This map contains the non-interpolated nanoseconds timestamps of the
+ /// decoded trace items. It maps `item index -> nanoseconds`, where `item
+ /// index` is the first index at which the mapped nanoseconds first appears.
+ /// We use this representation because timestamps are sporadic and we think of
+ /// them as ranges.
+ std::map<uint64_t, NanosecondsRange> m_nanoseconds;
+ std::optional<std::map<uint64_t, NanosecondsRange>::iterator>
+ m_last_nanoseconds = std::nullopt;
+
+ // The cpu information is stored as a map. It maps `item index -> CPU`.
+ // A CPU is associated with the next instructions that follow until the next
+ // cpu is seen.
+ std::map<uint64_t, lldb::cpu_id_t> m_cpus;
+ /// This is the chronologically last CPU ID.
+ std::optional<uint64_t> m_last_cpu;
+
+ // The PSB offsets are stored as a map. It maps `item index -> psb offset`.
+ llvm::DenseMap<uint64_t, lldb::addr_t> m_psb_offsets;
+
+ /// TSC -> nanos conversion utility.
+ std::optional<LinuxPerfZeroTscConversion> m_tsc_conversion;
+
+ /// Statistics of all tracing errors.
+ ErrorStats m_error_stats;
+
+ /// Statistics of all tracing events.
+ EventsStats m_events_stats;
+ /// Total amount of time spent decoding.
+ std::chrono::milliseconds m_total_decoding_time{0};
+
+ /// Total number of instructions in the trace.
+ uint64_t m_insn_count = 0;
+};
+
+using DecodedThreadSP = std::shared_ptr<DecodedThread>;
+
+} // namespace trace_intel_pt
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.cpp b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.cpp
new file mode 100644
index 000000000000..f8241ef6a793
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.cpp
@@ -0,0 +1,783 @@
+//===-- LibiptDecoder.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 "LibiptDecoder.h"
+#include "TraceIntelPT.h"
+#include "lldb/Target/Process.h"
+#include <optional>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::trace_intel_pt;
+using namespace llvm;
+
+bool IsLibiptError(int status) { return status < 0; }
+
+bool IsEndOfStream(int status) {
+ assert(status >= 0 && "We can't check if we reached the end of the stream if "
+ "we got a failed status");
+ return status & pts_eos;
+}
+
+bool HasEvents(int status) {
+ assert(status >= 0 && "We can't check for events if we got a failed status");
+ return status & pts_event_pending;
+}
+
+// RAII deleter for libipt's decoders
+auto InsnDecoderDeleter = [](pt_insn_decoder *decoder) {
+ pt_insn_free_decoder(decoder);
+};
+
+auto QueryDecoderDeleter = [](pt_query_decoder *decoder) {
+ pt_qry_free_decoder(decoder);
+};
+
+using PtInsnDecoderUP =
+ std::unique_ptr<pt_insn_decoder, decltype(InsnDecoderDeleter)>;
+
+using PtQueryDecoderUP =
+ std::unique_ptr<pt_query_decoder, decltype(QueryDecoderDeleter)>;
+
+/// Create a basic configuration object limited to a given buffer that can be
+/// used for many different decoders.
+static Expected<pt_config> CreateBasicLibiptConfig(TraceIntelPT &trace_intel_pt,
+ ArrayRef<uint8_t> buffer) {
+ Expected<pt_cpu> cpu_info = trace_intel_pt.GetCPUInfo();
+ if (!cpu_info)
+ return cpu_info.takeError();
+
+ pt_config config;
+ pt_config_init(&config);
+ config.cpu = *cpu_info;
+
+ int status = pt_cpu_errata(&config.errata, &config.cpu);
+ if (IsLibiptError(status))
+ return make_error<IntelPTError>(status);
+
+ // The libipt library does not modify the trace buffer, hence the
+ // following casts are safe.
+ config.begin = const_cast<uint8_t *>(buffer.data());
+ config.end = const_cast<uint8_t *>(buffer.data() + buffer.size());
+ return config;
+}
+
+/// Callback used by libipt for reading the process memory.
+///
+/// More information can be found in
+/// https://github.com/intel/libipt/blob/master/doc/man/pt_image_set_callback.3.md
+static int ReadProcessMemory(uint8_t *buffer, size_t size,
+ const pt_asid * /* unused */, uint64_t pc,
+ void *context) {
+ Process *process = static_cast<Process *>(context);
+
+ Status error;
+ int bytes_read = process->ReadMemory(pc, buffer, size, error);
+ if (error.Fail())
+ return -pte_nomap;
+ return bytes_read;
+}
+
+/// Set up the memory image callback for the given decoder.
+static Error SetupMemoryImage(pt_insn_decoder *decoder, Process &process) {
+ pt_image *image = pt_insn_get_image(decoder);
+
+ int status = pt_image_set_callback(image, ReadProcessMemory, &process);
+ if (IsLibiptError(status))
+ return make_error<IntelPTError>(status);
+ return Error::success();
+}
+
+/// Create an instruction decoder for the given buffer and the given process.
+static Expected<PtInsnDecoderUP>
+CreateInstructionDecoder(TraceIntelPT &trace_intel_pt, ArrayRef<uint8_t> buffer,
+ Process &process) {
+ Expected<pt_config> config = CreateBasicLibiptConfig(trace_intel_pt, buffer);
+ if (!config)
+ return config.takeError();
+
+ pt_insn_decoder *decoder_ptr = pt_insn_alloc_decoder(&*config);
+ if (!decoder_ptr)
+ return make_error<IntelPTError>(-pte_nomem);
+
+ PtInsnDecoderUP decoder_up(decoder_ptr, InsnDecoderDeleter);
+
+ if (Error err = SetupMemoryImage(decoder_ptr, process))
+ return std::move(err);
+
+ return decoder_up;
+}
+
+/// Create a query decoder for the given buffer. The query decoder is the
+/// highest level decoder that operates directly on packets and doesn't perform
+/// actual instruction decoding. That's why it can be useful for inspecting a
+/// raw trace without pinning it to a particular process.
+static Expected<PtQueryDecoderUP>
+CreateQueryDecoder(TraceIntelPT &trace_intel_pt, ArrayRef<uint8_t> buffer) {
+ Expected<pt_config> config = CreateBasicLibiptConfig(trace_intel_pt, buffer);
+ if (!config)
+ return config.takeError();
+
+ pt_query_decoder *decoder_ptr = pt_qry_alloc_decoder(&*config);
+ if (!decoder_ptr)
+ return make_error<IntelPTError>(-pte_nomem);
+
+ return PtQueryDecoderUP(decoder_ptr, QueryDecoderDeleter);
+}
+
+/// Class used to identify anomalies in traces, which should often indicate a
+/// fatal error in the trace.
+class PSBBlockAnomalyDetector {
+public:
+ PSBBlockAnomalyDetector(pt_insn_decoder &decoder,
+ TraceIntelPT &trace_intel_pt,
+ DecodedThread &decoded_thread)
+ : m_decoder(decoder), m_decoded_thread(decoded_thread) {
+ m_infinite_decoding_loop_threshold =
+ trace_intel_pt.GetGlobalProperties()
+ .GetInfiniteDecodingLoopVerificationThreshold();
+ m_extremely_large_decoding_threshold =
+ trace_intel_pt.GetGlobalProperties()
+ .GetExtremelyLargeDecodingThreshold();
+ m_next_infinite_decoding_loop_threshold =
+ m_infinite_decoding_loop_threshold;
+ }
+
+ /// \return
+ /// An \a llvm::Error if an anomaly that includes the last instruction item
+ /// in the trace, or \a llvm::Error::success otherwise.
+ Error DetectAnomaly() {
+ RefreshPacketOffset();
+ uint64_t insn_added_since_last_packet_offset =
+ m_decoded_thread.GetTotalInstructionCount() -
+ m_insn_count_at_last_packet_offset;
+
+ // We want to check if we might have fallen in an infinite loop. As this
+ // check is not a no-op, we want to do it when we have a strong suggestion
+ // that things went wrong. First, we check how many instructions we have
+ // decoded since we processed an Intel PT packet for the last time. This
+ // number should be low, because at some point we should see branches, jumps
+ // or interrupts that require a new packet to be processed. Once we reach
+ // certain threshold we start analyzing the trace.
+ //
+ // We use the number of decoded instructions since the last Intel PT packet
+ // as a proxy because, in fact, we don't expect a single packet to give,
+ // say, 100k instructions. That would mean that there are 100k sequential
+ // instructions without any single branch, which is highly unlikely, or that
+ // we found an infinite loop using direct jumps, e.g.
+ //
+ // 0x0A: nop or pause
+ // 0x0C: jump to 0x0A
+ //
+ // which is indeed code that is found in the kernel. I presume we reach
+ // this kind of code in the decoder because we don't handle self-modified
+ // code in post-mortem kernel traces.
+ //
+ // We are right now only signaling the anomaly as a trace error, but it
+ // would be more conservative to also discard all the trace items found in
+ // this PSB. I prefer not to do that for the time being to give more
+ // exposure to this kind of anomalies and help debugging. Discarding the
+ // trace items would just make investigation harded.
+ //
+ // Finally, if the user wants to see if a specific thread has an anomaly,
+ // it's enough to run the `thread trace dump info` command and look for the
+ // count of this kind of errors.
+
+ if (insn_added_since_last_packet_offset >=
+ m_extremely_large_decoding_threshold) {
+ // In this case, we have decoded a massive amount of sequential
+ // instructions that don't loop. Honestly I wonder if this will ever
+ // happen, but better safe than sorry.
+ return createStringError(
+ inconvertibleErrorCode(),
+ "anomalous trace: possible infinite trace detected");
+ }
+ if (insn_added_since_last_packet_offset ==
+ m_next_infinite_decoding_loop_threshold) {
+ if (std::optional<uint64_t> loop_size = TryIdentifyInfiniteLoop()) {
+ return createStringError(
+ inconvertibleErrorCode(),
+ "anomalous trace: possible infinite loop detected of size %" PRIu64,
+ *loop_size);
+ }
+ m_next_infinite_decoding_loop_threshold *= 2;
+ }
+ return Error::success();
+ }
+
+private:
+ std::optional<uint64_t> TryIdentifyInfiniteLoop() {
+ // The infinite decoding loops we'll encounter are due to sequential
+ // instructions that repeat themselves due to direct jumps, therefore in a
+ // cycle each individual address will only appear once. We use this
+ // information to detect cycles by finding the last 2 ocurrences of the last
+ // instruction added to the trace. Then we traverse the trace making sure
+ // that these two instructions where the ends of a repeating loop.
+
+ // This is a utility that returns the most recent instruction index given a
+ // position in the trace. If the given position is an instruction, that
+ // position is returned. It skips non-instruction items.
+ auto most_recent_insn_index =
+ [&](uint64_t item_index) -> std::optional<uint64_t> {
+ while (true) {
+ if (m_decoded_thread.GetItemKindByIndex(item_index) ==
+ lldb::eTraceItemKindInstruction) {
+ return item_index;
+ }
+ if (item_index == 0)
+ return std::nullopt;
+ item_index--;
+ }
+ return std::nullopt;
+ };
+ // Similar to most_recent_insn_index but skips the starting position.
+ auto prev_insn_index = [&](uint64_t item_index) -> std::optional<uint64_t> {
+ if (item_index == 0)
+ return std::nullopt;
+ return most_recent_insn_index(item_index - 1);
+ };
+
+ // We first find the most recent instruction.
+ std::optional<uint64_t> last_insn_index_opt =
+ *prev_insn_index(m_decoded_thread.GetItemsCount());
+ if (!last_insn_index_opt)
+ return std::nullopt;
+ uint64_t last_insn_index = *last_insn_index_opt;
+
+ // We then find the most recent previous occurrence of that last
+ // instruction.
+ std::optional<uint64_t> last_insn_copy_index =
+ prev_insn_index(last_insn_index);
+ uint64_t loop_size = 1;
+ while (last_insn_copy_index &&
+ m_decoded_thread.GetInstructionLoadAddress(*last_insn_copy_index) !=
+ m_decoded_thread.GetInstructionLoadAddress(last_insn_index)) {
+ last_insn_copy_index = prev_insn_index(*last_insn_copy_index);
+ loop_size++;
+ }
+ if (!last_insn_copy_index)
+ return std::nullopt;
+
+ // Now we check if the segment between these last positions of the last
+ // instruction address is in fact a repeating loop.
+ uint64_t loop_elements_visited = 1;
+ uint64_t insn_index_a = last_insn_index,
+ insn_index_b = *last_insn_copy_index;
+ while (loop_elements_visited < loop_size) {
+ if (std::optional<uint64_t> prev = prev_insn_index(insn_index_a))
+ insn_index_a = *prev;
+ else
+ return std::nullopt;
+ if (std::optional<uint64_t> prev = prev_insn_index(insn_index_b))
+ insn_index_b = *prev;
+ else
+ return std::nullopt;
+ if (m_decoded_thread.GetInstructionLoadAddress(insn_index_a) !=
+ m_decoded_thread.GetInstructionLoadAddress(insn_index_b))
+ return std::nullopt;
+ loop_elements_visited++;
+ }
+ return loop_size;
+ }
+
+ // Refresh the internal counters if a new packet offset has been visited
+ void RefreshPacketOffset() {
+ lldb::addr_t new_packet_offset;
+ if (!IsLibiptError(pt_insn_get_offset(&m_decoder, &new_packet_offset)) &&
+ new_packet_offset != m_last_packet_offset) {
+ m_last_packet_offset = new_packet_offset;
+ m_next_infinite_decoding_loop_threshold =
+ m_infinite_decoding_loop_threshold;
+ m_insn_count_at_last_packet_offset =
+ m_decoded_thread.GetTotalInstructionCount();
+ }
+ }
+
+ pt_insn_decoder &m_decoder;
+ DecodedThread &m_decoded_thread;
+ lldb::addr_t m_last_packet_offset = LLDB_INVALID_ADDRESS;
+ uint64_t m_insn_count_at_last_packet_offset = 0;
+ uint64_t m_infinite_decoding_loop_threshold;
+ uint64_t m_next_infinite_decoding_loop_threshold;
+ uint64_t m_extremely_large_decoding_threshold;
+};
+
+/// Class that decodes a raw buffer for a single PSB block using the low level
+/// libipt library. It assumes that kernel and user mode instructions are not
+/// mixed in the same PSB block.
+///
+/// Throughout this code, the status of the decoder will be used to identify
+/// events needed to be processed or errors in the decoder. The values can be
+/// - negative: actual errors
+/// - positive or zero: not an error, but a list of bits signaling the status
+/// of the decoder, e.g. whether there are events that need to be decoded or
+/// not.
+class PSBBlockDecoder {
+public:
+ /// \param[in] decoder
+ /// A decoder configured to start and end within the boundaries of the
+ /// given \p psb_block.
+ ///
+ /// \param[in] psb_block
+ /// The PSB block to decode.
+ ///
+ /// \param[in] next_block_ip
+ /// The starting ip at the next PSB block of the same thread if available.
+ ///
+ /// \param[in] decoded_thread
+ /// A \a DecodedThread object where the decoded instructions will be
+ /// appended to. It might have already some instructions.
+ ///
+ /// \param[in] tsc_upper_bound
+ /// Maximum allowed value of TSCs decoded from this PSB block.
+ /// Any of this PSB's data occurring after this TSC will be excluded.
+ PSBBlockDecoder(PtInsnDecoderUP &&decoder_up, const PSBBlock &psb_block,
+ std::optional<lldb::addr_t> next_block_ip,
+ DecodedThread &decoded_thread, TraceIntelPT &trace_intel_pt,
+ std::optional<DecodedThread::TSC> tsc_upper_bound)
+ : m_decoder_up(std::move(decoder_up)), m_psb_block(psb_block),
+ m_next_block_ip(next_block_ip), m_decoded_thread(decoded_thread),
+ m_anomaly_detector(*m_decoder_up, trace_intel_pt, decoded_thread),
+ m_tsc_upper_bound(tsc_upper_bound) {}
+
+ /// \param[in] trace_intel_pt
+ /// The main Trace object that own the PSB block.
+ ///
+ /// \param[in] decoder
+ /// A decoder configured to start and end within the boundaries of the
+ /// given \p psb_block.
+ ///
+ /// \param[in] psb_block
+ /// The PSB block to decode.
+ ///
+ /// \param[in] buffer
+ /// The raw intel pt trace for this block.
+ ///
+ /// \param[in] process
+ /// The process to decode. It provides the memory image to use for
+ /// decoding.
+ ///
+ /// \param[in] next_block_ip
+ /// The starting ip at the next PSB block of the same thread if available.
+ ///
+ /// \param[in] decoded_thread
+ /// A \a DecodedThread object where the decoded instructions will be
+ /// appended to. It might have already some instructions.
+ static Expected<PSBBlockDecoder>
+ Create(TraceIntelPT &trace_intel_pt, const PSBBlock &psb_block,
+ ArrayRef<uint8_t> buffer, Process &process,
+ std::optional<lldb::addr_t> next_block_ip,
+ DecodedThread &decoded_thread,
+ std::optional<DecodedThread::TSC> tsc_upper_bound) {
+ Expected<PtInsnDecoderUP> decoder_up =
+ CreateInstructionDecoder(trace_intel_pt, buffer, process);
+ if (!decoder_up)
+ return decoder_up.takeError();
+
+ return PSBBlockDecoder(std::move(*decoder_up), psb_block, next_block_ip,
+ decoded_thread, trace_intel_pt, tsc_upper_bound);
+ }
+
+ void DecodePSBBlock() {
+ int status = pt_insn_sync_forward(m_decoder_up.get());
+ assert(status >= 0 &&
+ "Synchronization shouldn't fail because this PSB was previously "
+ "decoded correctly.");
+
+ // We emit a TSC before a sync event to more easily associate a timestamp to
+ // the sync event. If present, the current block's TSC would be the first
+ // TSC we'll see when processing events.
+ if (m_psb_block.tsc)
+ m_decoded_thread.NotifyTsc(*m_psb_block.tsc);
+
+ m_decoded_thread.NotifySyncPoint(m_psb_block.psb_offset);
+
+ DecodeInstructionsAndEvents(status);
+ }
+
+private:
+ /// Append an instruction and return \b false if and only if a serious anomaly
+ /// has been detected.
+ bool AppendInstructionAndDetectAnomalies(const pt_insn &insn) {
+ m_decoded_thread.AppendInstruction(insn);
+
+ if (Error err = m_anomaly_detector.DetectAnomaly()) {
+ m_decoded_thread.AppendCustomError(toString(std::move(err)),
+ /*fatal=*/true);
+ return false;
+ }
+ return true;
+ }
+ /// Decode all the instructions and events of the given PSB block. The
+ /// decoding loop might stop abruptly if an infinite decoding loop is
+ /// detected.
+ void DecodeInstructionsAndEvents(int status) {
+ pt_insn insn;
+
+ while (true) {
+ status = ProcessPTEvents(status);
+
+ if (IsLibiptError(status))
+ return;
+ else if (IsEndOfStream(status))
+ break;
+
+ // The status returned by pt_insn_next will need to be processed
+ // by ProcessPTEvents in the next loop if it is not an error.
+ std::memset(&insn, 0, sizeof insn);
+ status = pt_insn_next(m_decoder_up.get(), &insn, sizeof(insn));
+
+ if (IsLibiptError(status)) {
+ m_decoded_thread.AppendError(IntelPTError(status, insn.ip));
+ return;
+ } else if (IsEndOfStream(status)) {
+ break;
+ }
+
+ if (!AppendInstructionAndDetectAnomalies(insn))
+ return;
+ }
+
+ // We need to keep querying non-branching instructions until we hit the
+ // starting point of the next PSB. We won't see events at this point. This
+ // is based on
+ // https://github.com/intel/libipt/blob/master/doc/howto_libipt.md#parallel-decode
+ if (m_next_block_ip && insn.ip != 0) {
+ while (insn.ip != *m_next_block_ip) {
+ if (!AppendInstructionAndDetectAnomalies(insn))
+ return;
+
+ status = pt_insn_next(m_decoder_up.get(), &insn, sizeof(insn));
+
+ if (IsLibiptError(status)) {
+ m_decoded_thread.AppendError(IntelPTError(status, insn.ip));
+ return;
+ }
+ }
+ }
+ }
+
+ /// Process the TSC of a decoded PT event. Specifically, check if this TSC
+ /// is below the TSC upper bound for this PSB. If the TSC exceeds the upper
+ /// bound, return an error to abort decoding. Otherwise add the it to the
+ /// underlying DecodedThread and decoding should continue as expected.
+ ///
+ /// \param[in] tsc
+ /// The TSC of the a decoded event.
+ Error ProcessPTEventTSC(DecodedThread::TSC tsc) {
+ if (m_tsc_upper_bound && tsc >= *m_tsc_upper_bound) {
+ // This event and all the remaining events of this PSB have a TSC
+ // outside the range of the "owning" ThreadContinuousExecution. For
+ // now we drop all of these events/instructions, future work can
+ // improve upon this by determining the "owning"
+ // ThreadContinuousExecution of the remaining PSB data.
+ std::string err_msg = formatv("decoding truncated: TSC {0} exceeds "
+ "maximum TSC value {1}, will skip decoding"
+ " the remaining data of the PSB",
+ tsc, *m_tsc_upper_bound)
+ .str();
+
+ uint64_t offset;
+ int status = pt_insn_get_offset(m_decoder_up.get(), &offset);
+ if (!IsLibiptError(status)) {
+ err_msg = formatv("{2} (skipping {0} of {1} bytes)", offset,
+ m_psb_block.size, err_msg)
+ .str();
+ }
+ m_decoded_thread.AppendCustomError(err_msg);
+ return createStringError(inconvertibleErrorCode(), err_msg);
+ } else {
+ m_decoded_thread.NotifyTsc(tsc);
+ return Error::success();
+ }
+ }
+
+ /// Before querying instructions, we need to query the events associated with
+ /// that instruction, e.g. timing and trace disablement events.
+ ///
+ /// \param[in] status
+ /// The status gotten from the previous instruction decoding or PSB
+ /// synchronization.
+ ///
+ /// \return
+ /// The pte_status after decoding events.
+ int ProcessPTEvents(int status) {
+ while (HasEvents(status)) {
+ pt_event event;
+ std::memset(&event, 0, sizeof event);
+ status = pt_insn_event(m_decoder_up.get(), &event, sizeof(event));
+
+ if (IsLibiptError(status)) {
+ m_decoded_thread.AppendError(IntelPTError(status));
+ return status;
+ }
+
+ if (event.has_tsc) {
+ if (Error err = ProcessPTEventTSC(event.tsc)) {
+ consumeError(std::move(err));
+ return -pte_internal;
+ }
+ }
+
+ switch (event.type) {
+ case ptev_disabled:
+ // The CPU paused tracing the program, e.g. due to ip filtering.
+ m_decoded_thread.AppendEvent(lldb::eTraceEventDisabledHW);
+ break;
+ case ptev_async_disabled:
+ // The kernel or user code paused tracing the program, e.g.
+ // a breakpoint or a ioctl invocation pausing the trace, or a
+ // context switch happened.
+ m_decoded_thread.AppendEvent(lldb::eTraceEventDisabledSW);
+ break;
+ case ptev_overflow:
+ // The CPU internal buffer had an overflow error and some instructions
+ // were lost. A OVF packet comes with an FUP packet (harcoded address)
+ // according to the documentation, so we'll continue seeing instructions
+ // after this event.
+ m_decoded_thread.AppendError(IntelPTError(-pte_overflow));
+ break;
+ default:
+ break;
+ }
+ }
+
+ return status;
+ }
+
+private:
+ PtInsnDecoderUP m_decoder_up;
+ PSBBlock m_psb_block;
+ std::optional<lldb::addr_t> m_next_block_ip;
+ DecodedThread &m_decoded_thread;
+ PSBBlockAnomalyDetector m_anomaly_detector;
+ std::optional<DecodedThread::TSC> m_tsc_upper_bound;
+};
+
+Error lldb_private::trace_intel_pt::DecodeSingleTraceForThread(
+ DecodedThread &decoded_thread, TraceIntelPT &trace_intel_pt,
+ ArrayRef<uint8_t> buffer) {
+ Expected<std::vector<PSBBlock>> blocks =
+ SplitTraceIntoPSBBlock(trace_intel_pt, buffer, /*expect_tscs=*/false);
+ if (!blocks)
+ return blocks.takeError();
+
+ for (size_t i = 0; i < blocks->size(); i++) {
+ PSBBlock &block = blocks->at(i);
+
+ Expected<PSBBlockDecoder> decoder = PSBBlockDecoder::Create(
+ trace_intel_pt, block, buffer.slice(block.psb_offset, block.size),
+ *decoded_thread.GetThread()->GetProcess(),
+ i + 1 < blocks->size() ? blocks->at(i + 1).starting_ip : std::nullopt,
+ decoded_thread, std::nullopt);
+ if (!decoder)
+ return decoder.takeError();
+
+ decoder->DecodePSBBlock();
+ }
+
+ return Error::success();
+}
+
+Error lldb_private::trace_intel_pt::DecodeSystemWideTraceForThread(
+ DecodedThread &decoded_thread, TraceIntelPT &trace_intel_pt,
+ const DenseMap<lldb::cpu_id_t, llvm::ArrayRef<uint8_t>> &buffers,
+ const std::vector<IntelPTThreadContinousExecution> &executions) {
+ bool has_seen_psbs = false;
+ for (size_t i = 0; i < executions.size(); i++) {
+ const IntelPTThreadContinousExecution &execution = executions[i];
+
+ auto variant = execution.thread_execution.variant;
+
+ // We emit the first valid tsc
+ if (execution.psb_blocks.empty()) {
+ decoded_thread.NotifyTsc(execution.thread_execution.GetLowestKnownTSC());
+ } else {
+ assert(execution.psb_blocks.front().tsc &&
+ "per cpu decoding expects TSCs");
+ decoded_thread.NotifyTsc(
+ std::min(execution.thread_execution.GetLowestKnownTSC(),
+ *execution.psb_blocks.front().tsc));
+ }
+
+ // We then emit the CPU, which will be correctly associated with a tsc.
+ decoded_thread.NotifyCPU(execution.thread_execution.cpu_id);
+
+ // If we haven't seen a PSB yet, then it's fine not to show errors
+ if (has_seen_psbs) {
+ if (execution.psb_blocks.empty()) {
+ decoded_thread.AppendCustomError(
+ formatv("Unable to find intel pt data a thread "
+ "execution on cpu id = {0}",
+ execution.thread_execution.cpu_id)
+ .str());
+ }
+
+ // A hinted start is a non-initial execution that doesn't have a switch
+ // in. An only end is an initial execution that doesn't have a switch in.
+ // Any of those cases represent a gap because we have seen a PSB before.
+ if (variant == ThreadContinuousExecution::Variant::HintedStart ||
+ variant == ThreadContinuousExecution::Variant::OnlyEnd) {
+ decoded_thread.AppendCustomError(
+ formatv("Unable to find the context switch in for a thread "
+ "execution on cpu id = {0}",
+ execution.thread_execution.cpu_id)
+ .str());
+ }
+ }
+
+ for (size_t j = 0; j < execution.psb_blocks.size(); j++) {
+ const PSBBlock &psb_block = execution.psb_blocks[j];
+
+ Expected<PSBBlockDecoder> decoder = PSBBlockDecoder::Create(
+ trace_intel_pt, psb_block,
+ buffers.lookup(execution.thread_execution.cpu_id)
+ .slice(psb_block.psb_offset, psb_block.size),
+ *decoded_thread.GetThread()->GetProcess(),
+ j + 1 < execution.psb_blocks.size()
+ ? execution.psb_blocks[j + 1].starting_ip
+ : std::nullopt,
+ decoded_thread, execution.thread_execution.GetEndTSC());
+ if (!decoder)
+ return decoder.takeError();
+
+ has_seen_psbs = true;
+ decoder->DecodePSBBlock();
+ }
+
+ // If we haven't seen a PSB yet, then it's fine not to show errors
+ if (has_seen_psbs) {
+ // A hinted end is a non-ending execution that doesn't have a switch out.
+ // An only start is an ending execution that doesn't have a switch out.
+ // Any of those cases represent a gap if we still have executions to
+ // process and we have seen a PSB before.
+ if (i + 1 != executions.size() &&
+ (variant == ThreadContinuousExecution::Variant::OnlyStart ||
+ variant == ThreadContinuousExecution::Variant::HintedEnd)) {
+ decoded_thread.AppendCustomError(
+ formatv("Unable to find the context switch out for a thread "
+ "execution on cpu id = {0}",
+ execution.thread_execution.cpu_id)
+ .str());
+ }
+ }
+ }
+ return Error::success();
+}
+
+bool IntelPTThreadContinousExecution::operator<(
+ const IntelPTThreadContinousExecution &o) const {
+ // As the context switch might be incomplete, we look first for the first real
+ // PSB packet, which is a valid TSC. Otherwise, We query the thread execution
+ // itself for some tsc.
+ auto get_tsc = [](const IntelPTThreadContinousExecution &exec) {
+ return exec.psb_blocks.empty() ? exec.thread_execution.GetLowestKnownTSC()
+ : exec.psb_blocks.front().tsc;
+ };
+
+ return get_tsc(*this) < get_tsc(o);
+}
+
+Expected<std::vector<PSBBlock>>
+lldb_private::trace_intel_pt::SplitTraceIntoPSBBlock(
+ TraceIntelPT &trace_intel_pt, llvm::ArrayRef<uint8_t> buffer,
+ bool expect_tscs) {
+ // This follows
+ // https://github.com/intel/libipt/blob/master/doc/howto_libipt.md#parallel-decode
+
+ Expected<PtQueryDecoderUP> decoder_up =
+ CreateQueryDecoder(trace_intel_pt, buffer);
+ if (!decoder_up)
+ return decoder_up.takeError();
+
+ pt_query_decoder *decoder = decoder_up.get().get();
+
+ std::vector<PSBBlock> executions;
+
+ while (true) {
+ uint64_t maybe_ip = LLDB_INVALID_ADDRESS;
+ int decoding_status = pt_qry_sync_forward(decoder, &maybe_ip);
+ if (IsLibiptError(decoding_status))
+ break;
+
+ uint64_t psb_offset;
+ int offset_status = pt_qry_get_sync_offset(decoder, &psb_offset);
+ assert(offset_status >= 0 &&
+ "This can't fail because we were able to synchronize");
+
+ std::optional<uint64_t> ip;
+ if (!(pts_ip_suppressed & decoding_status))
+ ip = maybe_ip;
+
+ std::optional<uint64_t> tsc;
+ // Now we fetch the first TSC that comes after the PSB.
+ while (HasEvents(decoding_status)) {
+ pt_event event;
+ decoding_status = pt_qry_event(decoder, &event, sizeof(event));
+ if (IsLibiptError(decoding_status))
+ break;
+ if (event.has_tsc) {
+ tsc = event.tsc;
+ break;
+ }
+ }
+ if (IsLibiptError(decoding_status)) {
+ // We continue to the next PSB. This effectively merges this PSB with the
+ // previous one, and that should be fine because this PSB might be the
+ // direct continuation of the previous thread and it's better to show an
+ // error in the decoded thread than to hide it. If this is the first PSB,
+ // we are okay losing it. Besides that, an error at processing events
+ // means that we wouldn't be able to get any instruction out of it.
+ continue;
+ }
+
+ if (expect_tscs && !tsc)
+ return createStringError(inconvertibleErrorCode(),
+ "Found a PSB without TSC.");
+
+ executions.push_back({
+ psb_offset,
+ tsc,
+ 0,
+ ip,
+ });
+ }
+ if (!executions.empty()) {
+ // We now adjust the sizes of each block
+ executions.back().size = buffer.size() - executions.back().psb_offset;
+ for (int i = (int)executions.size() - 2; i >= 0; i--) {
+ executions[i].size =
+ executions[i + 1].psb_offset - executions[i].psb_offset;
+ }
+ }
+ return executions;
+}
+
+Expected<std::optional<uint64_t>>
+lldb_private::trace_intel_pt::FindLowestTSCInTrace(TraceIntelPT &trace_intel_pt,
+ ArrayRef<uint8_t> buffer) {
+ Expected<PtQueryDecoderUP> decoder_up =
+ CreateQueryDecoder(trace_intel_pt, buffer);
+ if (!decoder_up)
+ return decoder_up.takeError();
+
+ pt_query_decoder *decoder = decoder_up.get().get();
+ uint64_t ip = LLDB_INVALID_ADDRESS;
+ int status = pt_qry_sync_forward(decoder, &ip);
+ if (IsLibiptError(status))
+ return std::nullopt;
+
+ while (HasEvents(status)) {
+ pt_event event;
+ status = pt_qry_event(decoder, &event, sizeof(event));
+ if (IsLibiptError(status))
+ return std::nullopt;
+ if (event.has_tsc)
+ return event.tsc;
+ }
+ return std::nullopt;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.h b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.h
new file mode 100644
index 000000000000..bc8a5b963d6f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.h
@@ -0,0 +1,125 @@
+//===-- LibiptDecoder.h --======---------------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_PLUGINS_TRACE_LIBIPT_DECODER_H
+#define LLDB_SOURCE_PLUGINS_TRACE_LIBIPT_DECODER_H
+
+#include "DecodedThread.h"
+#include "PerfContextSwitchDecoder.h"
+#include "forward-declarations.h"
+#include "intel-pt.h"
+#include <optional>
+
+namespace lldb_private {
+namespace trace_intel_pt {
+
+/// This struct represents a contiguous section of a trace that starts at a PSB
+/// and ends right before the next PSB or the end of the trace.
+struct PSBBlock {
+ /// The memory offset of a PSB packet that is a synchronization point for the
+ /// decoder. A decoder normally looks first for a PSB packet and then it
+ /// starts decoding.
+ uint64_t psb_offset;
+ /// The timestamp associated with the PSB packet above.
+ std::optional<uint64_t> tsc;
+ /// Size in bytes of this block
+ uint64_t size;
+ /// The first ip for this PSB block.
+ /// This is \a std::nullopt if tracing was disabled when the PSB block was
+ /// emitted. This means that eventually there's be an enablement event that
+ /// will come with an ip.
+ std::optional<lldb::addr_t> starting_ip;
+};
+
+/// This struct represents a continuous execution of a thread in a cpu,
+/// delimited by a context switch in and out, and a list of Intel PT subtraces
+/// that belong to this execution.
+struct IntelPTThreadContinousExecution {
+ ThreadContinuousExecution thread_execution;
+ std::vector<PSBBlock> psb_blocks;
+
+ IntelPTThreadContinousExecution(
+ const ThreadContinuousExecution &thread_execution)
+ : thread_execution(thread_execution) {}
+
+ /// Comparator by time
+ bool operator<(const IntelPTThreadContinousExecution &o) const;
+};
+
+/// Decode a raw Intel PT trace for a single thread given in \p buffer and
+/// append the decoded instructions and errors in \p decoded_thread. It uses the
+/// low level libipt library underneath.
+///
+/// \return
+/// An \a llvm::Error if the decoder couldn't be properly set up.
+llvm::Error DecodeSingleTraceForThread(DecodedThread &decoded_thread,
+ TraceIntelPT &trace_intel_pt,
+ llvm::ArrayRef<uint8_t> buffer);
+
+/// Decode a raw Intel PT trace for a single thread that was collected in a per
+/// cpu core basis.
+///
+/// \param[out] decoded_thread
+/// All decoded instructions, errors and events will be appended to this
+/// object.
+///
+/// \param[in] trace_intel_pt
+/// The main Trace object that contains all the information related to the
+/// trace session.
+///
+/// \param[in] buffers
+/// A map from cpu core id to raw intel pt buffers.
+///
+/// \param[in] executions
+/// A list of chunks of timed executions of the same given thread. It is used
+/// to identify if some executions have missing intel pt data and also to
+/// determine in which core a certain part of the execution ocurred.
+///
+/// \return
+/// An \a llvm::Error if the decoder couldn't be properly set up, i.e. no
+/// instructions were attempted to be decoded.
+llvm::Error DecodeSystemWideTraceForThread(
+ DecodedThread &decoded_thread, TraceIntelPT &trace_intel_pt,
+ const llvm::DenseMap<lldb::cpu_id_t, llvm::ArrayRef<uint8_t>> &buffers,
+ const std::vector<IntelPTThreadContinousExecution> &executions);
+
+/// Given an intel pt trace, split it in chunks delimited by PSB packets. Each
+/// of these chunks is guaranteed to have been executed continuously.
+///
+/// \param[in] trace_intel_pt
+/// The main Trace object that contains all the information related to the
+/// trace session.
+///
+/// \param[in] buffer
+/// The intel pt buffer that belongs to a single thread or to a single cpu
+/// core.
+///
+/// \param[in] expect_tscs
+/// If \b true, an error is return if a packet without TSC is found.
+///
+/// \return
+/// A list of continuous executions sorted by time, or an \a llvm::Error in
+/// case of failures.
+llvm::Expected<std::vector<PSBBlock>>
+SplitTraceIntoPSBBlock(TraceIntelPT &trace_intel_pt,
+ llvm::ArrayRef<uint8_t> buffer, bool expect_tscs);
+
+/// Find the lowest TSC in the given trace.
+///
+/// \return
+/// The lowest TSC value in this trace if available, \a std::nullopt if the
+/// trace is empty or the trace contains no timing information, or an \a
+/// llvm::Error if it was not possible to set up the decoder.
+llvm::Expected<std::optional<uint64_t>>
+FindLowestTSCInTrace(TraceIntelPT &trace_intel_pt,
+ llvm::ArrayRef<uint8_t> buffer);
+
+} // namespace trace_intel_pt
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_TRACE_LIBIPT_DECODER_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.cpp b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.cpp
new file mode 100644
index 000000000000..1aa2a3cc097b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.cpp
@@ -0,0 +1,332 @@
+//===-- PerfContextSwitchDecoder.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 "PerfContextSwitchDecoder.h"
+#include <optional>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::trace_intel_pt;
+using namespace llvm;
+
+/// Copied from <linux/perf_event.h> to avoid depending on perf_event.h on
+/// non-linux platforms.
+/// \{
+#define PERF_RECORD_MISC_SWITCH_OUT (1 << 13)
+
+#define PERF_RECORD_LOST 2
+#define PERF_RECORD_THROTTLE 5
+#define PERF_RECORD_UNTHROTTLE 6
+#define PERF_RECORD_LOST_SAMPLES 13
+#define PERF_RECORD_SWITCH_CPU_WIDE 15
+#define PERF_RECORD_MAX 19
+
+struct perf_event_header {
+ uint32_t type;
+ uint16_t misc;
+ uint16_t size;
+
+ /// \return
+ /// An \a llvm::Error if the record looks obviously wrong, or \a
+ /// llvm::Error::success() otherwise.
+ Error SanityCheck() const {
+ // The following checks are based on visual inspection of the records and
+ // enums in
+ // https://elixir.bootlin.com/linux/v4.8/source/include/uapi/linux/perf_event.h
+ // See PERF_RECORD_MAX, PERF_RECORD_SWITCH and the data similar records
+ // hold.
+
+ // A record of too many uint64_t's or more should mean that the data is
+ // wrong
+ const uint64_t max_valid_size_bytes = 8000;
+ if (size == 0 || size > max_valid_size_bytes)
+ return createStringError(
+ inconvertibleErrorCode(),
+ formatv("A record of {0} bytes was found.", size));
+
+ // We add some numbers to PERF_RECORD_MAX because some systems might have
+ // custom records. In any case, we are looking only for abnormal data.
+ if (type >= PERF_RECORD_MAX + 100)
+ return createStringError(
+ inconvertibleErrorCode(),
+ formatv("Invalid record type {0} was found.", type));
+ return Error::success();
+ }
+
+ bool IsContextSwitchRecord() const {
+ return type == PERF_RECORD_SWITCH_CPU_WIDE;
+ }
+
+ bool IsErrorRecord() const {
+ return type == PERF_RECORD_LOST || type == PERF_RECORD_THROTTLE ||
+ type == PERF_RECORD_UNTHROTTLE || type == PERF_RECORD_LOST_SAMPLES;
+ }
+};
+/// \}
+
+/// Record found in the perf_event context switch traces. It might contain
+/// additional fields in memory, but header.size should have the actual size
+/// of the record.
+struct PerfContextSwitchRecord {
+ struct perf_event_header header;
+ uint32_t next_prev_pid;
+ uint32_t next_prev_tid;
+ uint32_t pid, tid;
+ uint64_t time_in_nanos;
+
+ bool IsOut() const { return header.misc & PERF_RECORD_MISC_SWITCH_OUT; }
+};
+
+/// Record produced after parsing the raw context switch trace produce by
+/// perf_event. A major difference between this struct and
+/// PerfContextSwitchRecord is that this one uses tsc instead of nanos.
+struct ContextSwitchRecord {
+ uint64_t tsc;
+ /// Whether the switch is in or out
+ bool is_out;
+ /// pid = 0 and tid = 0 indicate the swapper or idle process, which normally
+ /// runs after a context switch out of a normal user thread.
+ lldb::pid_t pid;
+ lldb::tid_t tid;
+
+ bool IsOut() const { return is_out; }
+
+ bool IsIn() const { return !is_out; }
+};
+
+uint64_t ThreadContinuousExecution::GetLowestKnownTSC() const {
+ switch (variant) {
+ case Variant::Complete:
+ return tscs.complete.start;
+ case Variant::OnlyStart:
+ return tscs.only_start.start;
+ case Variant::OnlyEnd:
+ return tscs.only_end.end;
+ case Variant::HintedEnd:
+ return tscs.hinted_end.start;
+ case Variant::HintedStart:
+ return tscs.hinted_start.end;
+ }
+}
+
+uint64_t ThreadContinuousExecution::GetStartTSC() const {
+ switch (variant) {
+ case Variant::Complete:
+ return tscs.complete.start;
+ case Variant::OnlyStart:
+ return tscs.only_start.start;
+ case Variant::OnlyEnd:
+ return 0;
+ case Variant::HintedEnd:
+ return tscs.hinted_end.start;
+ case Variant::HintedStart:
+ return tscs.hinted_start.hinted_start;
+ }
+}
+
+uint64_t ThreadContinuousExecution::GetEndTSC() const {
+ switch (variant) {
+ case Variant::Complete:
+ return tscs.complete.end;
+ case Variant::OnlyStart:
+ return std::numeric_limits<uint64_t>::max();
+ case Variant::OnlyEnd:
+ return tscs.only_end.end;
+ case Variant::HintedEnd:
+ return tscs.hinted_end.hinted_end;
+ case Variant::HintedStart:
+ return tscs.hinted_start.end;
+ }
+}
+
+ThreadContinuousExecution ThreadContinuousExecution::CreateCompleteExecution(
+ lldb::cpu_id_t cpu_id, lldb::tid_t tid, lldb::pid_t pid, uint64_t start,
+ uint64_t end) {
+ ThreadContinuousExecution o(cpu_id, tid, pid);
+ o.variant = Variant::Complete;
+ o.tscs.complete.start = start;
+ o.tscs.complete.end = end;
+ return o;
+}
+
+ThreadContinuousExecution ThreadContinuousExecution::CreateHintedStartExecution(
+ lldb::cpu_id_t cpu_id, lldb::tid_t tid, lldb::pid_t pid,
+ uint64_t hinted_start, uint64_t end) {
+ ThreadContinuousExecution o(cpu_id, tid, pid);
+ o.variant = Variant::HintedStart;
+ o.tscs.hinted_start.hinted_start = hinted_start;
+ o.tscs.hinted_start.end = end;
+ return o;
+}
+
+ThreadContinuousExecution ThreadContinuousExecution::CreateHintedEndExecution(
+ lldb::cpu_id_t cpu_id, lldb::tid_t tid, lldb::pid_t pid, uint64_t start,
+ uint64_t hinted_end) {
+ ThreadContinuousExecution o(cpu_id, tid, pid);
+ o.variant = Variant::HintedEnd;
+ o.tscs.hinted_end.start = start;
+ o.tscs.hinted_end.hinted_end = hinted_end;
+ return o;
+}
+
+ThreadContinuousExecution ThreadContinuousExecution::CreateOnlyEndExecution(
+ lldb::cpu_id_t cpu_id, lldb::tid_t tid, lldb::pid_t pid, uint64_t end) {
+ ThreadContinuousExecution o(cpu_id, tid, pid);
+ o.variant = Variant::OnlyEnd;
+ o.tscs.only_end.end = end;
+ return o;
+}
+
+ThreadContinuousExecution ThreadContinuousExecution::CreateOnlyStartExecution(
+ lldb::cpu_id_t cpu_id, lldb::tid_t tid, lldb::pid_t pid, uint64_t start) {
+ ThreadContinuousExecution o(cpu_id, tid, pid);
+ o.variant = Variant::OnlyStart;
+ o.tscs.only_start.start = start;
+ return o;
+}
+
+static Error RecoverExecutionsFromConsecutiveRecords(
+ cpu_id_t cpu_id, const LinuxPerfZeroTscConversion &tsc_conversion,
+ const ContextSwitchRecord &current_record,
+ const std::optional<ContextSwitchRecord> &prev_record,
+ std::function<void(const ThreadContinuousExecution &execution)>
+ on_new_execution) {
+ if (!prev_record) {
+ if (current_record.IsOut()) {
+ on_new_execution(ThreadContinuousExecution::CreateOnlyEndExecution(
+ cpu_id, current_record.tid, current_record.pid, current_record.tsc));
+ }
+ // The 'in' case will be handled later when we try to look for its end
+ return Error::success();
+ }
+
+ const ContextSwitchRecord &prev = *prev_record;
+ if (prev.tsc >= current_record.tsc)
+ return createStringError(
+ inconvertibleErrorCode(),
+ formatv("A context switch record doesn't happen after the previous "
+ "record. Previous TSC= {0}, current TSC = {1}.",
+ prev.tsc, current_record.tsc));
+
+ if (current_record.IsIn() && prev.IsIn()) {
+ // We found two consecutive ins, which means that we didn't capture
+ // the end of the previous execution.
+ on_new_execution(ThreadContinuousExecution::CreateHintedEndExecution(
+ cpu_id, prev.tid, prev.pid, prev.tsc, current_record.tsc - 1));
+ } else if (current_record.IsOut() && prev.IsOut()) {
+ // We found two consecutive outs, that means that we didn't capture
+ // the beginning of the current execution.
+ on_new_execution(ThreadContinuousExecution::CreateHintedStartExecution(
+ cpu_id, current_record.tid, current_record.pid, prev.tsc + 1,
+ current_record.tsc));
+ } else if (current_record.IsOut() && prev.IsIn()) {
+ if (current_record.pid == prev.pid && current_record.tid == prev.tid) {
+ /// A complete execution
+ on_new_execution(ThreadContinuousExecution::CreateCompleteExecution(
+ cpu_id, current_record.tid, current_record.pid, prev.tsc,
+ current_record.tsc));
+ } else {
+ // An out after the in of a different thread. The first one doesn't
+ // have an end, and the second one doesn't have a start.
+ on_new_execution(ThreadContinuousExecution::CreateHintedEndExecution(
+ cpu_id, prev.tid, prev.pid, prev.tsc, current_record.tsc - 1));
+ on_new_execution(ThreadContinuousExecution::CreateHintedStartExecution(
+ cpu_id, current_record.tid, current_record.pid, prev.tsc + 1,
+ current_record.tsc));
+ }
+ }
+ return Error::success();
+}
+
+Expected<std::vector<ThreadContinuousExecution>>
+lldb_private::trace_intel_pt::DecodePerfContextSwitchTrace(
+ ArrayRef<uint8_t> data, cpu_id_t cpu_id,
+ const LinuxPerfZeroTscConversion &tsc_conversion) {
+
+ std::vector<ThreadContinuousExecution> executions;
+
+ // This offset is used to create the error message in case of failures.
+ size_t offset = 0;
+
+ auto do_decode = [&]() -> Error {
+ std::optional<ContextSwitchRecord> prev_record;
+ while (offset < data.size()) {
+ const perf_event_header &perf_record =
+ *reinterpret_cast<const perf_event_header *>(data.data() + offset);
+ if (Error err = perf_record.SanityCheck())
+ return err;
+
+ if (perf_record.IsContextSwitchRecord()) {
+ const PerfContextSwitchRecord &context_switch_record =
+ *reinterpret_cast<const PerfContextSwitchRecord *>(data.data() +
+ offset);
+ ContextSwitchRecord record{
+ tsc_conversion.ToTSC(context_switch_record.time_in_nanos),
+ context_switch_record.IsOut(),
+ static_cast<lldb::pid_t>(context_switch_record.pid),
+ static_cast<lldb::tid_t>(context_switch_record.tid)};
+
+ if (Error err = RecoverExecutionsFromConsecutiveRecords(
+ cpu_id, tsc_conversion, record, prev_record,
+ [&](const ThreadContinuousExecution &execution) {
+ executions.push_back(execution);
+ }))
+ return err;
+
+ prev_record = record;
+ }
+ offset += perf_record.size;
+ }
+
+ // We might have an incomplete last record
+ if (prev_record && prev_record->IsIn())
+ executions.push_back(ThreadContinuousExecution::CreateOnlyStartExecution(
+ cpu_id, prev_record->tid, prev_record->pid, prev_record->tsc));
+ return Error::success();
+ };
+
+ if (Error err = do_decode())
+ return createStringError(inconvertibleErrorCode(),
+ formatv("Malformed perf context switch trace for "
+ "cpu {0} at offset {1}. {2}",
+ cpu_id, offset, toString(std::move(err))));
+
+ return executions;
+}
+
+Expected<std::vector<uint8_t>>
+lldb_private::trace_intel_pt::FilterProcessesFromContextSwitchTrace(
+ llvm::ArrayRef<uint8_t> data, const std::set<lldb::pid_t> &pids) {
+ size_t offset = 0;
+ std::vector<uint8_t> out_data;
+
+ while (offset < data.size()) {
+ const perf_event_header &perf_record =
+ *reinterpret_cast<const perf_event_header *>(data.data() + offset);
+ if (Error err = perf_record.SanityCheck())
+ return std::move(err);
+ bool should_copy = false;
+ if (perf_record.IsContextSwitchRecord()) {
+ const PerfContextSwitchRecord &context_switch_record =
+ *reinterpret_cast<const PerfContextSwitchRecord *>(data.data() +
+ offset);
+ if (pids.count(context_switch_record.pid))
+ should_copy = true;
+ } else if (perf_record.IsErrorRecord()) {
+ should_copy = true;
+ }
+
+ if (should_copy) {
+ for (size_t i = 0; i < perf_record.size; i++) {
+ out_data.push_back(data[offset + i]);
+ }
+ }
+
+ offset += perf_record.size;
+ }
+ return out_data;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.h b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.h
new file mode 100644
index 000000000000..4ea7738810ce
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.h
@@ -0,0 +1,148 @@
+//===-- PerfContextSwitchDecoder.h --======----------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_PERFCONTEXTSWITCHDECODER_H
+#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_PERFCONTEXTSWITCHDECODER_H
+
+#include "lldb/Utility/TraceIntelPTGDBRemotePackets.h"
+#include "lldb/lldb-types.h"
+#include "llvm/Support/Error.h"
+#include <set>
+#include <vector>
+
+namespace lldb_private {
+namespace trace_intel_pt {
+
+/// This class indicates the time interval in which a thread was running
+/// continuously on a cpu core.
+struct ThreadContinuousExecution {
+
+ /// In most cases both the start and end of a continuous execution can be
+ /// accurately recovered from the context switch trace, but in some cases one
+ /// of these endpoints might be guessed or not known at all, due to contention
+ /// problems in the trace or because tracing was interrupted, e.g. with ioctl
+ /// calls, which causes gaps in the trace. Because of that, we identify which
+ /// situation we fall into with the following variants.
+ enum class Variant {
+ /// Both endpoints are known.
+ Complete,
+ /// The end is known and we have a lower bound for the start, i.e. the
+ /// previous execution in the same cpu happens strictly before the hinted
+ /// start.
+ HintedStart,
+ /// The start is known and we have an upper bound for the end, i.e. the next
+ /// execution in the same cpu happens strictly after the hinted end.
+ HintedEnd,
+ /// We only know the start. This might be the last entry of a cpu trace.
+ OnlyStart,
+ /// We only know the end. This might be the first entry or a cpu trace.
+ OnlyEnd,
+ } variant;
+
+ /// \return
+ /// The lowest tsc that we are sure of, i.e. not hinted.
+ uint64_t GetLowestKnownTSC() const;
+
+ /// \return
+ /// The known or hinted start tsc, or 0 if the variant is \a OnlyEnd.
+ uint64_t GetStartTSC() const;
+
+ /// \return
+ /// The known or hinted end tsc, or max \a uint64_t if the variant is \a
+ /// OnlyStart.
+ uint64_t GetEndTSC() const;
+
+ /// Constructors for the different variants of this object
+ ///
+ /// \{
+ static ThreadContinuousExecution
+ CreateCompleteExecution(lldb::cpu_id_t cpu_id, lldb::tid_t tid,
+ lldb::pid_t pid, uint64_t start, uint64_t end);
+
+ static ThreadContinuousExecution
+ CreateHintedStartExecution(lldb::cpu_id_t cpu_id, lldb::tid_t tid,
+ lldb::pid_t pid, uint64_t hinted_start,
+ uint64_t end);
+
+ static ThreadContinuousExecution
+ CreateHintedEndExecution(lldb::cpu_id_t cpu_id, lldb::tid_t tid,
+ lldb::pid_t pid, uint64_t start,
+ uint64_t hinted_end);
+
+ static ThreadContinuousExecution CreateOnlyEndExecution(lldb::cpu_id_t cpu_id,
+ lldb::tid_t tid,
+ lldb::pid_t pid,
+ uint64_t end);
+
+ static ThreadContinuousExecution
+ CreateOnlyStartExecution(lldb::cpu_id_t cpu_id, lldb::tid_t tid,
+ lldb::pid_t pid, uint64_t start);
+ /// \}
+
+ union {
+ struct {
+ uint64_t start;
+ uint64_t end;
+ } complete;
+ struct {
+ uint64_t start;
+ } only_start;
+ struct {
+ uint64_t end;
+ } only_end;
+ /// The following 'hinted' structures are useful when there are contention
+ /// problems in the trace
+ struct {
+ uint64_t hinted_start;
+ uint64_t end;
+ } hinted_start;
+ struct {
+ uint64_t start;
+ uint64_t hinted_end;
+ } hinted_end;
+ } tscs;
+
+ lldb::cpu_id_t cpu_id;
+ lldb::tid_t tid;
+ lldb::pid_t pid;
+
+private:
+ /// We keep this constructor private to force the usage of the static named
+ /// constructors.
+ ThreadContinuousExecution(lldb::cpu_id_t cpu_id, lldb::tid_t tid,
+ lldb::pid_t pid)
+ : cpu_id(cpu_id), tid(tid), pid(pid) {}
+};
+
+/// Decodes a context switch trace collected with perf_event_open.
+///
+/// \param[in] data
+/// The context switch trace in binary format.
+///
+/// \param[i] cpu_id
+/// The cpu_id where the trace were gotten from.
+///
+/// \param[in] tsc_conversion
+/// The conversion values used to confert nanoseconds to TSC.
+///
+/// \return
+/// A list of continuous executions recovered from the raw trace sorted by
+/// time, or an \a llvm::Error if the data is malformed.
+llvm::Expected<std::vector<ThreadContinuousExecution>>
+DecodePerfContextSwitchTrace(llvm::ArrayRef<uint8_t> data,
+ lldb::cpu_id_t cpu_id,
+ const LinuxPerfZeroTscConversion &tsc_conversion);
+
+llvm::Expected<std::vector<uint8_t>>
+FilterProcessesFromContextSwitchTrace(llvm::ArrayRef<uint8_t> data,
+ const std::set<lldb::pid_t> &pids);
+
+} // namespace trace_intel_pt
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_PERFCONTEXTSWITCHDECODER_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TaskTimer.cpp b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TaskTimer.cpp
new file mode 100644
index 000000000000..55d48ae35ff0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TaskTimer.cpp
@@ -0,0 +1,32 @@
+//===-- TaskTimer.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 "TaskTimer.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::trace_intel_pt;
+using namespace llvm;
+
+void ScopedTaskTimer::ForEachTimedTask(
+ std::function<void(const std::string &event,
+ std::chrono::milliseconds duration)>
+ callback) {
+ for (const auto &kv : m_timed_tasks) {
+ callback(kv.first, kv.second);
+ }
+}
+
+ScopedTaskTimer &TaskTimer::ForThread(lldb::tid_t tid) {
+ auto it = m_thread_timers.find(tid);
+ if (it == m_thread_timers.end())
+ it = m_thread_timers.try_emplace(tid, ScopedTaskTimer{}).first;
+ return it->second;
+}
+
+ScopedTaskTimer &TaskTimer::ForGlobal() { return m_global_timer; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TaskTimer.h b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TaskTimer.h
new file mode 100644
index 000000000000..fb196e8fc32a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TaskTimer.h
@@ -0,0 +1,78 @@
+//===-- TaskTimer.h ---------------------------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TASKTIMER_H
+#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TASKTIMER_H
+
+#include "lldb/lldb-types.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringRef.h"
+#include <chrono>
+#include <functional>
+#include <unordered_map>
+
+namespace lldb_private {
+namespace trace_intel_pt {
+
+/// Class used to track the duration of long running tasks related to a single
+/// scope for reporting.
+class ScopedTaskTimer {
+public:
+ /// Execute the given \p task and record its duration.
+ ///
+ /// \param[in] name
+ /// The name used to identify this task for reporting.
+ ///
+ /// \param[in] task
+ /// The task function.
+ ///
+ /// \return
+ /// The return value of the task.
+ template <typename C> auto TimeTask(llvm::StringRef name, C task) {
+ auto start = std::chrono::steady_clock::now();
+ auto result = task();
+ auto end = std::chrono::steady_clock::now();
+ std::chrono::milliseconds duration =
+ std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
+ m_timed_tasks.insert({name.str(), duration});
+ return result;
+ }
+
+ /// Executive the given \p callback on each recorded task.
+ ///
+ /// \param[in] callback
+ /// The first parameter of the callback is the name of the recorded task,
+ /// and the second parameter is the duration of that task.
+ void ForEachTimedTask(std::function<void(const std::string &name,
+ std::chrono::milliseconds duration)>
+ callback);
+
+private:
+ std::unordered_map<std::string, std::chrono::milliseconds> m_timed_tasks;
+};
+
+/// Class used to track the duration of long running tasks for reporting.
+class TaskTimer {
+public:
+ /// \return
+ /// The timer object for the given thread.
+ ScopedTaskTimer &ForThread(lldb::tid_t tid);
+
+ /// \return
+ /// The timer object for global tasks.
+ ScopedTaskTimer &ForGlobal();
+
+private:
+ llvm::DenseMap<lldb::tid_t, ScopedTaskTimer> m_thread_timers;
+ ScopedTaskTimer m_global_timer;
+};
+
+} // namespace trace_intel_pt
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TASKTIMER_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.cpp b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.cpp
new file mode 100644
index 000000000000..75d74edd44a1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.cpp
@@ -0,0 +1,67 @@
+//===-- ThreadDecoder.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 "ThreadDecoder.h"
+#include "../common/ThreadPostMortemTrace.h"
+#include "LibiptDecoder.h"
+#include "TraceIntelPT.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include <optional>
+#include <utility>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::trace_intel_pt;
+using namespace llvm;
+
+ThreadDecoder::ThreadDecoder(const ThreadSP &thread_sp, TraceIntelPT &trace)
+ : m_thread_sp(thread_sp), m_trace(trace) {}
+
+Expected<std::optional<uint64_t>> ThreadDecoder::FindLowestTSC() {
+ std::optional<uint64_t> lowest_tsc;
+ Error err = m_trace.OnThreadBufferRead(
+ m_thread_sp->GetID(), [&](llvm::ArrayRef<uint8_t> data) -> llvm::Error {
+ Expected<std::optional<uint64_t>> tsc =
+ FindLowestTSCInTrace(m_trace, data);
+ if (!tsc)
+ return tsc.takeError();
+ lowest_tsc = *tsc;
+ return Error::success();
+ });
+ if (err)
+ return std::move(err);
+ return lowest_tsc;
+}
+
+Expected<DecodedThreadSP> ThreadDecoder::Decode() {
+ if (!m_decoded_thread.has_value()) {
+ if (Expected<DecodedThreadSP> decoded_thread = DoDecode()) {
+ m_decoded_thread = *decoded_thread;
+ } else {
+ return decoded_thread.takeError();
+ }
+ }
+ return *m_decoded_thread;
+}
+
+llvm::Expected<DecodedThreadSP> ThreadDecoder::DoDecode() {
+ return m_trace.GetThreadTimer(m_thread_sp->GetID())
+ .TimeTask("Decoding instructions", [&]() -> Expected<DecodedThreadSP> {
+ DecodedThreadSP decoded_thread_sp = std::make_shared<DecodedThread>(
+ m_thread_sp, m_trace.GetPerfZeroTscConversion());
+
+ Error err = m_trace.OnThreadBufferRead(
+ m_thread_sp->GetID(), [&](llvm::ArrayRef<uint8_t> data) {
+ return DecodeSingleTraceForThread(*decoded_thread_sp, m_trace,
+ data);
+ });
+
+ if (err)
+ return std::move(err);
+ return decoded_thread_sp;
+ });
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.h b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.h
new file mode 100644
index 000000000000..289915c02810
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.h
@@ -0,0 +1,59 @@
+//===-- ThreadDecoder.h --======---------------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_PLUGINS_TRACE_THREAD_DECODER_H
+#define LLDB_SOURCE_PLUGINS_TRACE_THREAD_DECODER_H
+
+#include "DecodedThread.h"
+#include "forward-declarations.h"
+#include "intel-pt.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/FileSpec.h"
+#include <optional>
+
+namespace lldb_private {
+namespace trace_intel_pt {
+
+/// Class that handles the decoding of a thread and caches the result.
+class ThreadDecoder {
+public:
+ /// \param[in] thread_sp
+ /// The thread whose intel pt trace buffer will be decoded.
+ ///
+ /// \param[in] trace
+ /// The main Trace object who owns this decoder and its data.
+ ThreadDecoder(const lldb::ThreadSP &thread_sp, TraceIntelPT &trace);
+
+ /// Decode the thread and store the result internally, to avoid
+ /// recomputations.
+ ///
+ /// \return
+ /// A \a DecodedThread instance.
+ llvm::Expected<DecodedThreadSP> Decode();
+
+ /// \return
+ /// The lowest TSC value in this trace if available, \a std::nullopt if
+ /// the trace is empty or the trace contains no timing information, or an
+ /// \a llvm::Error if it was not possible to set up the decoder.
+ llvm::Expected<std::optional<uint64_t>> FindLowestTSC();
+
+ ThreadDecoder(const ThreadDecoder &other) = delete;
+ ThreadDecoder &operator=(const ThreadDecoder &other) = delete;
+
+private:
+ llvm::Expected<DecodedThreadSP> DoDecode();
+
+ lldb::ThreadSP m_thread_sp;
+ TraceIntelPT &m_trace;
+ std::optional<DecodedThreadSP> m_decoded_thread;
+};
+
+} // namespace trace_intel_pt
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_TRACE_THREAD_DECODER_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp
new file mode 100644
index 000000000000..dda6cd74343f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp
@@ -0,0 +1,146 @@
+//===-- TraceCursorIntelPT.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 "TraceCursorIntelPT.h"
+#include "DecodedThread.h"
+#include "TraceIntelPT.h"
+#include <cstdlib>
+#include <optional>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::trace_intel_pt;
+using namespace llvm;
+
+TraceCursorIntelPT::TraceCursorIntelPT(
+ ThreadSP thread_sp, DecodedThreadSP decoded_thread_sp,
+ const std::optional<LinuxPerfZeroTscConversion> &tsc_conversion,
+ std::optional<uint64_t> beginning_of_time_nanos)
+ : TraceCursor(thread_sp), m_decoded_thread_sp(decoded_thread_sp),
+ m_tsc_conversion(tsc_conversion),
+ m_beginning_of_time_nanos(beginning_of_time_nanos) {
+ Seek(0, lldb::eTraceCursorSeekTypeEnd);
+}
+
+void TraceCursorIntelPT::Next() {
+ m_pos += IsForwards() ? 1 : -1;
+ ClearTimingRangesIfInvalid();
+}
+
+void TraceCursorIntelPT::ClearTimingRangesIfInvalid() {
+ if (m_tsc_range_calculated) {
+ if (!m_tsc_range || m_pos < 0 || !m_tsc_range->InRange(m_pos)) {
+ m_tsc_range = std::nullopt;
+ m_tsc_range_calculated = false;
+ }
+ }
+
+ if (m_nanoseconds_range_calculated) {
+ if (!m_nanoseconds_range || m_pos < 0 ||
+ !m_nanoseconds_range->InRange(m_pos)) {
+ m_nanoseconds_range = std::nullopt;
+ m_nanoseconds_range_calculated = false;
+ }
+ }
+}
+
+const std::optional<DecodedThread::TSCRange> &
+TraceCursorIntelPT::GetTSCRange() const {
+ if (!m_tsc_range_calculated) {
+ m_tsc_range_calculated = true;
+ m_tsc_range = m_decoded_thread_sp->GetTSCRangeByIndex(m_pos);
+ }
+ return m_tsc_range;
+}
+
+const std::optional<DecodedThread::NanosecondsRange> &
+TraceCursorIntelPT::GetNanosecondsRange() const {
+ if (!m_nanoseconds_range_calculated) {
+ m_nanoseconds_range_calculated = true;
+ m_nanoseconds_range =
+ m_decoded_thread_sp->GetNanosecondsRangeByIndex(m_pos);
+ }
+ return m_nanoseconds_range;
+}
+
+bool TraceCursorIntelPT::Seek(int64_t offset,
+ lldb::TraceCursorSeekType origin) {
+ switch (origin) {
+ case lldb::eTraceCursorSeekTypeBeginning:
+ m_pos = offset;
+ break;
+ case lldb::eTraceCursorSeekTypeEnd:
+ m_pos = m_decoded_thread_sp->GetItemsCount() - 1 + offset;
+ break;
+ case lldb::eTraceCursorSeekTypeCurrent:
+ m_pos += offset;
+ }
+
+ ClearTimingRangesIfInvalid();
+
+ return HasValue();
+}
+
+bool TraceCursorIntelPT::HasValue() const {
+ return m_pos >= 0 &&
+ static_cast<uint64_t>(m_pos) < m_decoded_thread_sp->GetItemsCount();
+}
+
+lldb::TraceItemKind TraceCursorIntelPT::GetItemKind() const {
+ return m_decoded_thread_sp->GetItemKindByIndex(m_pos);
+}
+
+llvm::StringRef TraceCursorIntelPT::GetError() const {
+ return m_decoded_thread_sp->GetErrorByIndex(m_pos);
+}
+
+lldb::addr_t TraceCursorIntelPT::GetLoadAddress() const {
+ return m_decoded_thread_sp->GetInstructionLoadAddress(m_pos);
+}
+
+std::optional<uint64_t> TraceCursorIntelPT::GetHWClock() const {
+ if (const std::optional<DecodedThread::TSCRange> &range = GetTSCRange())
+ return range->tsc;
+ return std::nullopt;
+}
+
+std::optional<double> TraceCursorIntelPT::GetWallClockTime() const {
+ if (const std::optional<DecodedThread::NanosecondsRange> &range =
+ GetNanosecondsRange())
+ return range->GetInterpolatedTime(m_pos, *m_beginning_of_time_nanos,
+ *m_tsc_conversion);
+ return std::nullopt;
+}
+
+lldb::cpu_id_t TraceCursorIntelPT::GetCPU() const {
+ return m_decoded_thread_sp->GetCPUByIndex(m_pos);
+}
+
+lldb::TraceEvent TraceCursorIntelPT::GetEventType() const {
+ return m_decoded_thread_sp->GetEventByIndex(m_pos);
+}
+
+bool TraceCursorIntelPT::GoToId(user_id_t id) {
+ if (!HasId(id))
+ return false;
+ m_pos = id;
+ ClearTimingRangesIfInvalid();
+ return true;
+}
+
+bool TraceCursorIntelPT::HasId(lldb::user_id_t id) const {
+ return id < m_decoded_thread_sp->GetItemsCount();
+}
+
+user_id_t TraceCursorIntelPT::GetId() const { return m_pos; }
+
+std::optional<std::string> TraceCursorIntelPT::GetSyncPointMetadata() const {
+ return formatv("offset = 0x{0:x}",
+ m_decoded_thread_sp->GetSyncPointOffsetByIndex(m_pos))
+ .str();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
new file mode 100644
index 000000000000..14240d9d0028
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
@@ -0,0 +1,93 @@
+//===-- TraceCursorIntelPT.h ------------------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACECURSORINTELPT_H
+#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACECURSORINTELPT_H
+
+#include "ThreadDecoder.h"
+#include <optional>
+
+namespace lldb_private {
+namespace trace_intel_pt {
+
+class TraceCursorIntelPT : public TraceCursor {
+public:
+ TraceCursorIntelPT(
+ lldb::ThreadSP thread_sp, DecodedThreadSP decoded_thread_sp,
+ const std::optional<LinuxPerfZeroTscConversion> &tsc_conversion,
+ std::optional<uint64_t> beginning_of_time_nanos);
+
+ bool Seek(int64_t offset, lldb::TraceCursorSeekType origin) override;
+
+ void Next() override;
+
+ bool HasValue() const override;
+
+ llvm::StringRef GetError() const override;
+
+ lldb::addr_t GetLoadAddress() const override;
+
+ lldb::TraceEvent GetEventType() const override;
+
+ lldb::cpu_id_t GetCPU() const override;
+
+ std::optional<uint64_t> GetHWClock() const override;
+
+ lldb::TraceItemKind GetItemKind() const override;
+
+ bool GoToId(lldb::user_id_t id) override;
+
+ lldb::user_id_t GetId() const override;
+
+ bool HasId(lldb::user_id_t id) const override;
+
+ std::optional<double> GetWallClockTime() const override;
+
+ std::optional<std::string> GetSyncPointMetadata() const override;
+
+private:
+ /// Clear the current TSC and nanoseconds ranges if after moving they are not
+ /// valid anymore.
+ void ClearTimingRangesIfInvalid();
+
+ /// Get or calculate the TSC range that includes the current trace item.
+ const std::optional<DecodedThread::TSCRange> &GetTSCRange() const;
+
+ /// Get or calculate the TSC range that includes the current trace item.
+ const std::optional<DecodedThread::NanosecondsRange> &
+ GetNanosecondsRange() const;
+
+ /// Storage of the actual instructions
+ DecodedThreadSP m_decoded_thread_sp;
+ /// Internal instruction index currently pointing at.
+ int64_t m_pos;
+
+ /// Timing information and cached values.
+ /// \{
+
+ /// TSC -> nanos conversion utility. \a std::nullopt if not available at all.
+ std::optional<LinuxPerfZeroTscConversion> m_tsc_conversion;
+ /// Lowest nanoseconds timestamp seen in any thread trace, \a std::nullopt if
+ /// not available at all.
+ std::optional<uint64_t> m_beginning_of_time_nanos;
+ /// Range of trace items with the same TSC that includes the current trace
+ /// item, \a std::nullopt if not calculated or not available.
+ std::optional<DecodedThread::TSCRange> mutable m_tsc_range;
+ bool mutable m_tsc_range_calculated = false;
+ /// Range of trace items with the same non-interpolated timestamps in
+ /// nanoseconds that includes the current trace item, \a std::nullopt if not
+ /// calculated or not available.
+ std::optional<DecodedThread::NanosecondsRange> mutable m_nanoseconds_range;
+ bool mutable m_nanoseconds_range_calculated = false;
+ /// \}
+};
+
+} // namespace trace_intel_pt
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACECURSORINTELPT_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
new file mode 100644
index 000000000000..72e9948ffe81
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
@@ -0,0 +1,744 @@
+//===-- TraceIntelPT.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 "TraceIntelPT.h"
+
+#include "../common/ThreadPostMortemTrace.h"
+#include "CommandObjectTraceStartIntelPT.h"
+#include "DecodedThread.h"
+#include "TraceCursorIntelPT.h"
+#include "TraceIntelPTBundleLoader.h"
+#include "TraceIntelPTBundleSaver.h"
+#include "TraceIntelPTConstants.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Interpreter/OptionValueProperties.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include <optional>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::trace_intel_pt;
+using namespace llvm;
+
+LLDB_PLUGIN_DEFINE(TraceIntelPT)
+
+lldb::CommandObjectSP
+TraceIntelPT::GetProcessTraceStartCommand(CommandInterpreter &interpreter) {
+ return CommandObjectSP(
+ new CommandObjectProcessTraceStartIntelPT(*this, interpreter));
+}
+
+lldb::CommandObjectSP
+TraceIntelPT::GetThreadTraceStartCommand(CommandInterpreter &interpreter) {
+ return CommandObjectSP(
+ new CommandObjectThreadTraceStartIntelPT(*this, interpreter));
+}
+
+#define LLDB_PROPERTIES_traceintelpt
+#include "TraceIntelPTProperties.inc"
+
+enum {
+#define LLDB_PROPERTIES_traceintelpt
+#include "TraceIntelPTPropertiesEnum.inc"
+};
+
+llvm::StringRef TraceIntelPT::PluginProperties::GetSettingName() {
+ return TraceIntelPT::GetPluginNameStatic();
+}
+
+TraceIntelPT::PluginProperties::PluginProperties() : Properties() {
+ m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName());
+ m_collection_sp->Initialize(g_traceintelpt_properties);
+}
+
+uint64_t
+TraceIntelPT::PluginProperties::GetInfiniteDecodingLoopVerificationThreshold() {
+ const uint32_t idx = ePropertyInfiniteDecodingLoopVerificationThreshold;
+ return GetPropertyAtIndexAs<uint64_t>(
+ idx, g_traceintelpt_properties[idx].default_uint_value);
+}
+
+uint64_t TraceIntelPT::PluginProperties::GetExtremelyLargeDecodingThreshold() {
+ const uint32_t idx = ePropertyExtremelyLargeDecodingThreshold;
+ return GetPropertyAtIndexAs<uint64_t>(
+ idx, g_traceintelpt_properties[idx].default_uint_value);
+}
+
+TraceIntelPT::PluginProperties &TraceIntelPT::GetGlobalProperties() {
+ static TraceIntelPT::PluginProperties g_settings;
+ return g_settings;
+}
+
+void TraceIntelPT::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), "Intel Processor Trace",
+ CreateInstanceForTraceBundle, CreateInstanceForLiveProcess,
+ TraceIntelPTBundleLoader::GetSchema(), DebuggerInitialize);
+}
+
+void TraceIntelPT::DebuggerInitialize(Debugger &debugger) {
+ if (!PluginManager::GetSettingForProcessPlugin(
+ debugger, PluginProperties::GetSettingName())) {
+ const bool is_global_setting = true;
+ PluginManager::CreateSettingForTracePlugin(
+ debugger, GetGlobalProperties().GetValueProperties(),
+ "Properties for the intel-pt trace plug-in.", is_global_setting);
+ }
+}
+
+void TraceIntelPT::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstanceForTraceBundle);
+}
+
+StringRef TraceIntelPT::GetSchema() {
+ return TraceIntelPTBundleLoader::GetSchema();
+}
+
+void TraceIntelPT::Dump(Stream *s) const {}
+
+Expected<FileSpec> TraceIntelPT::SaveToDisk(FileSpec directory, bool compact) {
+ RefreshLiveProcessState();
+ return TraceIntelPTBundleSaver().SaveToDisk(*this, directory, compact);
+}
+
+Expected<TraceSP> TraceIntelPT::CreateInstanceForTraceBundle(
+ const json::Value &bundle_description, StringRef bundle_dir,
+ Debugger &debugger) {
+ return TraceIntelPTBundleLoader(debugger, bundle_description, bundle_dir)
+ .Load();
+}
+
+Expected<TraceSP> TraceIntelPT::CreateInstanceForLiveProcess(Process &process) {
+ TraceSP instance(new TraceIntelPT(process));
+ process.GetTarget().SetTrace(instance);
+ return instance;
+}
+
+TraceIntelPTSP TraceIntelPT::GetSharedPtr() {
+ return std::static_pointer_cast<TraceIntelPT>(shared_from_this());
+}
+
+TraceIntelPT::TraceMode TraceIntelPT::GetTraceMode() { return trace_mode; }
+
+TraceIntelPTSP TraceIntelPT::CreateInstanceForPostmortemTrace(
+ JSONTraceBundleDescription &bundle_description,
+ ArrayRef<ProcessSP> traced_processes,
+ ArrayRef<ThreadPostMortemTraceSP> traced_threads, TraceMode trace_mode) {
+ TraceIntelPTSP trace_sp(
+ new TraceIntelPT(bundle_description, traced_processes, trace_mode));
+ trace_sp->m_storage.tsc_conversion =
+ bundle_description.tsc_perf_zero_conversion;
+
+ if (bundle_description.cpus) {
+ std::vector<cpu_id_t> cpus;
+
+ for (const JSONCpu &cpu : *bundle_description.cpus) {
+ trace_sp->SetPostMortemCpuDataFile(cpu.id, IntelPTDataKinds::kIptTrace,
+ FileSpec(cpu.ipt_trace));
+
+ trace_sp->SetPostMortemCpuDataFile(
+ cpu.id, IntelPTDataKinds::kPerfContextSwitchTrace,
+ FileSpec(cpu.context_switch_trace));
+ cpus.push_back(cpu.id);
+ }
+
+ if (trace_mode == TraceMode::UserMode) {
+ trace_sp->m_storage.multicpu_decoder.emplace(trace_sp);
+ }
+ }
+
+ if (!bundle_description.cpus || trace_mode == TraceMode::KernelMode) {
+ for (const ThreadPostMortemTraceSP &thread : traced_threads) {
+ trace_sp->m_storage.thread_decoders.try_emplace(
+ thread->GetID(), std::make_unique<ThreadDecoder>(thread, *trace_sp));
+ if (const std::optional<FileSpec> &trace_file = thread->GetTraceFile()) {
+ trace_sp->SetPostMortemThreadDataFile(
+ thread->GetID(), IntelPTDataKinds::kIptTrace, *trace_file);
+ }
+ }
+ }
+
+ for (const ProcessSP &process_sp : traced_processes)
+ process_sp->GetTarget().SetTrace(trace_sp);
+ return trace_sp;
+}
+
+TraceIntelPT::TraceIntelPT(JSONTraceBundleDescription &bundle_description,
+ ArrayRef<ProcessSP> traced_processes,
+ TraceMode trace_mode)
+ : Trace(traced_processes, bundle_description.GetCpuIds()),
+ m_cpu_info(bundle_description.cpu_info), trace_mode(trace_mode) {}
+
+Expected<DecodedThreadSP> TraceIntelPT::Decode(Thread &thread) {
+ if (const char *error = RefreshLiveProcessState())
+ return createStringError(inconvertibleErrorCode(), error);
+
+ Storage &storage = GetUpdatedStorage();
+ if (storage.multicpu_decoder)
+ return storage.multicpu_decoder->Decode(thread);
+
+ auto it = storage.thread_decoders.find(thread.GetID());
+ if (it == storage.thread_decoders.end())
+ return createStringError(inconvertibleErrorCode(), "thread not traced");
+ return it->second->Decode();
+}
+
+Expected<std::optional<uint64_t>> TraceIntelPT::FindBeginningOfTimeNanos() {
+ Storage &storage = GetUpdatedStorage();
+ if (storage.beginning_of_time_nanos_calculated)
+ return storage.beginning_of_time_nanos;
+ storage.beginning_of_time_nanos_calculated = true;
+
+ if (!storage.tsc_conversion)
+ return std::nullopt;
+
+ std::optional<uint64_t> lowest_tsc;
+
+ if (storage.multicpu_decoder) {
+ if (Expected<std::optional<uint64_t>> tsc =
+ storage.multicpu_decoder->FindLowestTSC()) {
+ lowest_tsc = *tsc;
+ } else {
+ return tsc.takeError();
+ }
+ }
+
+ for (auto &decoder : storage.thread_decoders) {
+ Expected<std::optional<uint64_t>> tsc = decoder.second->FindLowestTSC();
+ if (!tsc)
+ return tsc.takeError();
+
+ if (*tsc && (!lowest_tsc || *lowest_tsc > **tsc))
+ lowest_tsc = **tsc;
+ }
+
+ if (lowest_tsc) {
+ storage.beginning_of_time_nanos =
+ storage.tsc_conversion->ToNanos(*lowest_tsc);
+ }
+ return storage.beginning_of_time_nanos;
+}
+
+llvm::Expected<lldb::TraceCursorSP>
+TraceIntelPT::CreateNewCursor(Thread &thread) {
+ if (Expected<DecodedThreadSP> decoded_thread = Decode(thread)) {
+ if (Expected<std::optional<uint64_t>> beginning_of_time =
+ FindBeginningOfTimeNanos())
+ return std::make_shared<TraceCursorIntelPT>(
+ thread.shared_from_this(), *decoded_thread, m_storage.tsc_conversion,
+ *beginning_of_time);
+ else
+ return beginning_of_time.takeError();
+ } else
+ return decoded_thread.takeError();
+}
+
+void TraceIntelPT::DumpTraceInfo(Thread &thread, Stream &s, bool verbose,
+ bool json) {
+ Storage &storage = GetUpdatedStorage();
+
+ lldb::tid_t tid = thread.GetID();
+ if (json) {
+ DumpTraceInfoAsJson(thread, s, verbose);
+ return;
+ }
+
+ s.Format("\nthread #{0}: tid = {1}", thread.GetIndexID(), thread.GetID());
+ if (!IsTraced(tid)) {
+ s << ", not traced\n";
+ return;
+ }
+ s << "\n";
+
+ Expected<DecodedThreadSP> decoded_thread_sp_or_err = Decode(thread);
+ if (!decoded_thread_sp_or_err) {
+ s << toString(decoded_thread_sp_or_err.takeError()) << "\n";
+ return;
+ }
+
+ DecodedThreadSP &decoded_thread_sp = *decoded_thread_sp_or_err;
+
+ Expected<std::optional<uint64_t>> raw_size_or_error = GetRawTraceSize(thread);
+ if (!raw_size_or_error) {
+ s.Format(" {0}\n", toString(raw_size_or_error.takeError()));
+ return;
+ }
+ std::optional<uint64_t> raw_size = *raw_size_or_error;
+
+ s.Format("\n Trace technology: {0}\n", GetPluginName());
+
+ /// Instruction stats
+ {
+ uint64_t items_count = decoded_thread_sp->GetItemsCount();
+ uint64_t mem_used = decoded_thread_sp->CalculateApproximateMemoryUsage();
+
+ s.Format("\n Total number of trace items: {0}\n", items_count);
+
+ s << "\n Memory usage:\n";
+ if (raw_size)
+ s.Format(" Raw trace size: {0} KiB\n", *raw_size / 1024);
+
+ s.Format(
+ " Total approximate memory usage (excluding raw trace): {0:2} KiB\n",
+ (double)mem_used / 1024);
+ if (items_count != 0)
+ s.Format(" Average memory usage per item (excluding raw trace): "
+ "{0:2} bytes\n",
+ (double)mem_used / items_count);
+ }
+
+ // Timing
+ {
+ s << "\n Timing for this thread:\n";
+ auto print_duration = [&](const std::string &name,
+ std::chrono::milliseconds duration) {
+ s.Format(" {0}: {1:2}s\n", name, duration.count() / 1000.0);
+ };
+ GetThreadTimer(tid).ForEachTimedTask(print_duration);
+
+ s << "\n Timing for global tasks:\n";
+ GetGlobalTimer().ForEachTimedTask(print_duration);
+ }
+
+ // Instruction events stats
+ {
+ const DecodedThread::EventsStats &events_stats =
+ decoded_thread_sp->GetEventsStats();
+ s << "\n Events:\n";
+ s.Format(" Number of individual events: {0}\n",
+ events_stats.total_count);
+ for (const auto &event_to_count : events_stats.events_counts) {
+ s.Format(" {0}: {1}\n",
+ TraceCursor::EventKindToString(event_to_count.first),
+ event_to_count.second);
+ }
+ }
+ // Trace error stats
+ {
+ const DecodedThread::ErrorStats &error_stats =
+ decoded_thread_sp->GetErrorStats();
+ s << "\n Errors:\n";
+ s.Format(" Number of individual errors: {0}\n",
+ error_stats.GetTotalCount());
+ s.Format(" Number of fatal errors: {0}\n", error_stats.fatal_errors);
+ for (const auto &[kind, count] : error_stats.libipt_errors) {
+ s.Format(" Number of libipt errors of kind [{0}]: {1}\n", kind,
+ count);
+ }
+ s.Format(" Number of other errors: {0}\n", error_stats.other_errors);
+ }
+
+ if (storage.multicpu_decoder) {
+ s << "\n Multi-cpu decoding:\n";
+ s.Format(" Total number of continuous executions found: {0}\n",
+ storage.multicpu_decoder->GetTotalContinuousExecutionsCount());
+ s.Format(
+ " Number of continuous executions for this thread: {0}\n",
+ storage.multicpu_decoder->GetNumContinuousExecutionsForThread(tid));
+ s.Format(" Total number of PSB blocks found: {0}\n",
+ storage.multicpu_decoder->GetTotalPSBBlocksCount());
+ s.Format(" Number of PSB blocks for this thread: {0}\n",
+ storage.multicpu_decoder->GePSBBlocksCountForThread(tid));
+ s.Format(" Total number of unattributed PSB blocks found: {0}\n",
+ storage.multicpu_decoder->GetUnattributedPSBBlocksCount());
+ }
+}
+
+void TraceIntelPT::DumpTraceInfoAsJson(Thread &thread, Stream &s,
+ bool verbose) {
+ Storage &storage = GetUpdatedStorage();
+
+ lldb::tid_t tid = thread.GetID();
+ json::OStream json_str(s.AsRawOstream(), 2);
+ if (!IsTraced(tid)) {
+ s << "error: thread not traced\n";
+ return;
+ }
+
+ Expected<std::optional<uint64_t>> raw_size_or_error = GetRawTraceSize(thread);
+ if (!raw_size_or_error) {
+ s << "error: " << toString(raw_size_or_error.takeError()) << "\n";
+ return;
+ }
+
+ Expected<DecodedThreadSP> decoded_thread_sp_or_err = Decode(thread);
+ if (!decoded_thread_sp_or_err) {
+ s << "error: " << toString(decoded_thread_sp_or_err.takeError()) << "\n";
+ return;
+ }
+ DecodedThreadSP &decoded_thread_sp = *decoded_thread_sp_or_err;
+
+ json_str.object([&] {
+ json_str.attribute("traceTechnology", "intel-pt");
+ json_str.attributeObject("threadStats", [&] {
+ json_str.attribute("tid", tid);
+
+ uint64_t insn_len = decoded_thread_sp->GetItemsCount();
+ json_str.attribute("traceItemsCount", insn_len);
+
+ // Instruction stats
+ uint64_t mem_used = decoded_thread_sp->CalculateApproximateMemoryUsage();
+ json_str.attributeObject("memoryUsage", [&] {
+ json_str.attribute("totalInBytes", std::to_string(mem_used));
+ std::optional<double> avg;
+ if (insn_len != 0)
+ avg = double(mem_used) / insn_len;
+ json_str.attribute("avgPerItemInBytes", avg);
+ });
+
+ // Timing
+ json_str.attributeObject("timingInSeconds", [&] {
+ GetTimer().ForThread(tid).ForEachTimedTask(
+ [&](const std::string &name, std::chrono::milliseconds duration) {
+ json_str.attribute(name, duration.count() / 1000.0);
+ });
+ });
+
+ // Instruction events stats
+ const DecodedThread::EventsStats &events_stats =
+ decoded_thread_sp->GetEventsStats();
+ json_str.attributeObject("events", [&] {
+ json_str.attribute("totalCount", events_stats.total_count);
+ json_str.attributeObject("individualCounts", [&] {
+ for (const auto &event_to_count : events_stats.events_counts) {
+ json_str.attribute(
+ TraceCursor::EventKindToString(event_to_count.first),
+ event_to_count.second);
+ }
+ });
+ });
+ // Trace error stats
+ const DecodedThread::ErrorStats &error_stats =
+ decoded_thread_sp->GetErrorStats();
+ json_str.attributeObject("errors", [&] {
+ json_str.attribute("totalCount", error_stats.GetTotalCount());
+ json_str.attributeObject("libiptErrors", [&] {
+ for (const auto &[kind, count] : error_stats.libipt_errors) {
+ json_str.attribute(kind, count);
+ }
+ });
+ json_str.attribute("fatalErrors", error_stats.fatal_errors);
+ json_str.attribute("otherErrors", error_stats.other_errors);
+ });
+
+ if (storage.multicpu_decoder) {
+ json_str.attribute(
+ "continuousExecutions",
+ storage.multicpu_decoder->GetNumContinuousExecutionsForThread(tid));
+ json_str.attribute(
+ "PSBBlocks",
+ storage.multicpu_decoder->GePSBBlocksCountForThread(tid));
+ }
+ });
+
+ json_str.attributeObject("globalStats", [&] {
+ json_str.attributeObject("timingInSeconds", [&] {
+ GetTimer().ForGlobal().ForEachTimedTask(
+ [&](const std::string &name, std::chrono::milliseconds duration) {
+ json_str.attribute(name, duration.count() / 1000.0);
+ });
+ });
+ if (storage.multicpu_decoder) {
+ json_str.attribute(
+ "totalUnattributedPSBBlocks",
+ storage.multicpu_decoder->GetUnattributedPSBBlocksCount());
+ json_str.attribute(
+ "totalCountinuosExecutions",
+ storage.multicpu_decoder->GetTotalContinuousExecutionsCount());
+ json_str.attribute("totalPSBBlocks",
+ storage.multicpu_decoder->GetTotalPSBBlocksCount());
+ json_str.attribute(
+ "totalContinuousExecutions",
+ storage.multicpu_decoder->GetTotalContinuousExecutionsCount());
+ }
+ });
+ });
+}
+
+llvm::Expected<std::optional<uint64_t>>
+TraceIntelPT::GetRawTraceSize(Thread &thread) {
+ if (GetUpdatedStorage().multicpu_decoder)
+ return std::nullopt; // TODO: calculate the amount of intel pt raw trace associated
+ // with the given thread.
+ if (GetLiveProcess())
+ return GetLiveThreadBinaryDataSize(thread.GetID(),
+ IntelPTDataKinds::kIptTrace);
+ uint64_t size;
+ auto callback = [&](llvm::ArrayRef<uint8_t> data) {
+ size = data.size();
+ return Error::success();
+ };
+ if (Error err = OnThreadBufferRead(thread.GetID(), callback))
+ return std::move(err);
+
+ return size;
+}
+
+Expected<pt_cpu> TraceIntelPT::GetCPUInfoForLiveProcess() {
+ Expected<std::vector<uint8_t>> cpu_info =
+ GetLiveProcessBinaryData(IntelPTDataKinds::kProcFsCpuInfo);
+ if (!cpu_info)
+ return cpu_info.takeError();
+
+ int64_t cpu_family = -1;
+ int64_t model = -1;
+ int64_t stepping = -1;
+ std::string vendor_id;
+
+ StringRef rest(reinterpret_cast<const char *>(cpu_info->data()),
+ cpu_info->size());
+ while (!rest.empty()) {
+ StringRef line;
+ std::tie(line, rest) = rest.split('\n');
+
+ SmallVector<StringRef, 2> columns;
+ line.split(columns, StringRef(":"), -1, false);
+
+ if (columns.size() < 2)
+ continue; // continue searching
+
+ columns[1] = columns[1].trim(" ");
+ if (columns[0].contains("cpu family") &&
+ columns[1].getAsInteger(10, cpu_family))
+ continue;
+
+ else if (columns[0].contains("model") && columns[1].getAsInteger(10, model))
+ continue;
+
+ else if (columns[0].contains("stepping") &&
+ columns[1].getAsInteger(10, stepping))
+ continue;
+
+ else if (columns[0].contains("vendor_id")) {
+ vendor_id = columns[1].str();
+ if (!vendor_id.empty())
+ continue;
+ }
+
+ if ((cpu_family != -1) && (model != -1) && (stepping != -1) &&
+ (!vendor_id.empty())) {
+ return pt_cpu{vendor_id == "GenuineIntel" ? pcv_intel : pcv_unknown,
+ static_cast<uint16_t>(cpu_family),
+ static_cast<uint8_t>(model),
+ static_cast<uint8_t>(stepping)};
+ }
+ }
+ return createStringError(inconvertibleErrorCode(),
+ "Failed parsing the target's /proc/cpuinfo file");
+}
+
+Expected<pt_cpu> TraceIntelPT::GetCPUInfo() {
+ if (!m_cpu_info) {
+ if (llvm::Expected<pt_cpu> cpu_info = GetCPUInfoForLiveProcess())
+ m_cpu_info = *cpu_info;
+ else
+ return cpu_info.takeError();
+ }
+ return *m_cpu_info;
+}
+
+std::optional<LinuxPerfZeroTscConversion>
+TraceIntelPT::GetPerfZeroTscConversion() {
+ return GetUpdatedStorage().tsc_conversion;
+}
+
+TraceIntelPT::Storage &TraceIntelPT::GetUpdatedStorage() {
+ RefreshLiveProcessState();
+ return m_storage;
+}
+
+Error TraceIntelPT::DoRefreshLiveProcessState(TraceGetStateResponse state,
+ StringRef json_response) {
+ m_storage = Storage();
+
+ Expected<TraceIntelPTGetStateResponse> intelpt_state =
+ json::parse<TraceIntelPTGetStateResponse>(json_response,
+ "TraceIntelPTGetStateResponse");
+ if (!intelpt_state)
+ return intelpt_state.takeError();
+
+ m_storage.tsc_conversion = intelpt_state->tsc_perf_zero_conversion;
+
+ if (!intelpt_state->cpus) {
+ for (const TraceThreadState &thread_state : state.traced_threads) {
+ ThreadSP thread_sp =
+ GetLiveProcess()->GetThreadList().FindThreadByID(thread_state.tid);
+ m_storage.thread_decoders.try_emplace(
+ thread_state.tid, std::make_unique<ThreadDecoder>(thread_sp, *this));
+ }
+ } else {
+ std::vector<cpu_id_t> cpus;
+ for (const TraceCpuState &cpu : *intelpt_state->cpus)
+ cpus.push_back(cpu.id);
+
+ std::vector<tid_t> tids;
+ for (const TraceThreadState &thread : intelpt_state->traced_threads)
+ tids.push_back(thread.tid);
+
+ if (!intelpt_state->tsc_perf_zero_conversion)
+ return createStringError(inconvertibleErrorCode(),
+ "Missing perf time_zero conversion values");
+ m_storage.multicpu_decoder.emplace(GetSharedPtr());
+ }
+
+ if (m_storage.tsc_conversion) {
+ Log *log = GetLog(LLDBLog::Target);
+ LLDB_LOG(log, "TraceIntelPT found TSC conversion information");
+ }
+ return Error::success();
+}
+
+bool TraceIntelPT::IsTraced(lldb::tid_t tid) {
+ Storage &storage = GetUpdatedStorage();
+ if (storage.multicpu_decoder)
+ return storage.multicpu_decoder->TracesThread(tid);
+ return storage.thread_decoders.count(tid);
+}
+
+// The information here should match the description of the intel-pt section
+// of the jLLDBTraceStart packet in the lldb/docs/lldb-gdb-remote.txt
+// documentation file. Similarly, it should match the CLI help messages of the
+// TraceIntelPTOptions.td file.
+const char *TraceIntelPT::GetStartConfigurationHelp() {
+ static std::optional<std::string> message;
+ if (!message) {
+ message.emplace(formatv(R"(Parameters:
+
+ See the jLLDBTraceStart section in lldb/docs/lldb-gdb-remote.txt for a
+ description of each parameter below.
+
+ - int iptTraceSize (defaults to {0} bytes):
+ [process and thread tracing]
+
+ - boolean enableTsc (default to {1}):
+ [process and thread tracing]
+
+ - int psbPeriod (defaults to {2}):
+ [process and thread tracing]
+
+ - boolean perCpuTracing (default to {3}):
+ [process tracing only]
+
+ - int processBufferSizeLimit (defaults to {4} MiB):
+ [process tracing only]
+
+ - boolean disableCgroupFiltering (default to {5}):
+ [process tracing only])",
+ kDefaultIptTraceSize, kDefaultEnableTscValue,
+ kDefaultPsbPeriod, kDefaultPerCpuTracing,
+ kDefaultProcessBufferSizeLimit / 1024 / 1024,
+ kDefaultDisableCgroupFiltering));
+ }
+ return message->c_str();
+}
+
+Error TraceIntelPT::Start(uint64_t ipt_trace_size,
+ uint64_t total_buffer_size_limit, bool enable_tsc,
+ std::optional<uint64_t> psb_period,
+ bool per_cpu_tracing, bool disable_cgroup_filtering) {
+ TraceIntelPTStartRequest request;
+ request.ipt_trace_size = ipt_trace_size;
+ request.process_buffer_size_limit = total_buffer_size_limit;
+ request.enable_tsc = enable_tsc;
+ request.psb_period = psb_period;
+ request.type = GetPluginName().str();
+ request.per_cpu_tracing = per_cpu_tracing;
+ request.disable_cgroup_filtering = disable_cgroup_filtering;
+ return Trace::Start(toJSON(request));
+}
+
+Error TraceIntelPT::Start(StructuredData::ObjectSP configuration) {
+ uint64_t ipt_trace_size = kDefaultIptTraceSize;
+ uint64_t process_buffer_size_limit = kDefaultProcessBufferSizeLimit;
+ bool enable_tsc = kDefaultEnableTscValue;
+ std::optional<uint64_t> psb_period = kDefaultPsbPeriod;
+ bool per_cpu_tracing = kDefaultPerCpuTracing;
+ bool disable_cgroup_filtering = kDefaultDisableCgroupFiltering;
+
+ if (configuration) {
+ if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) {
+ dict->GetValueForKeyAsInteger("iptTraceSize", ipt_trace_size);
+ dict->GetValueForKeyAsInteger("processBufferSizeLimit",
+ process_buffer_size_limit);
+ dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc);
+ dict->GetValueForKeyAsInteger("psbPeriod", psb_period);
+ dict->GetValueForKeyAsBoolean("perCpuTracing", per_cpu_tracing);
+ dict->GetValueForKeyAsBoolean("disableCgroupFiltering",
+ disable_cgroup_filtering);
+ } else {
+ return createStringError(inconvertibleErrorCode(),
+ "configuration object is not a dictionary");
+ }
+ }
+
+ return Start(ipt_trace_size, process_buffer_size_limit, enable_tsc,
+ psb_period, per_cpu_tracing, disable_cgroup_filtering);
+}
+
+llvm::Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids,
+ uint64_t ipt_trace_size, bool enable_tsc,
+ std::optional<uint64_t> psb_period) {
+ TraceIntelPTStartRequest request;
+ request.ipt_trace_size = ipt_trace_size;
+ request.enable_tsc = enable_tsc;
+ request.psb_period = psb_period;
+ request.type = GetPluginName().str();
+ request.tids.emplace();
+ for (lldb::tid_t tid : tids)
+ request.tids->push_back(tid);
+ return Trace::Start(toJSON(request));
+}
+
+Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids,
+ StructuredData::ObjectSP configuration) {
+ uint64_t ipt_trace_size = kDefaultIptTraceSize;
+ bool enable_tsc = kDefaultEnableTscValue;
+ std::optional<uint64_t> psb_period = kDefaultPsbPeriod;
+
+ if (configuration) {
+ if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) {
+ llvm::StringRef ipt_trace_size_not_parsed;
+ if (dict->GetValueForKeyAsString("iptTraceSize",
+ ipt_trace_size_not_parsed)) {
+ if (std::optional<uint64_t> bytes =
+ ParsingUtils::ParseUserFriendlySizeExpression(
+ ipt_trace_size_not_parsed))
+ ipt_trace_size = *bytes;
+ else
+ return createStringError(inconvertibleErrorCode(),
+ "iptTraceSize is wrong bytes expression");
+ } else {
+ dict->GetValueForKeyAsInteger("iptTraceSize", ipt_trace_size);
+ }
+
+ dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc);
+ dict->GetValueForKeyAsInteger("psbPeriod", psb_period);
+ } else {
+ return createStringError(inconvertibleErrorCode(),
+ "configuration object is not a dictionary");
+ }
+ }
+
+ return Start(tids, ipt_trace_size, enable_tsc, psb_period);
+}
+
+Error TraceIntelPT::OnThreadBufferRead(lldb::tid_t tid,
+ OnBinaryDataReadCallback callback) {
+ return OnThreadBinaryDataRead(tid, IntelPTDataKinds::kIptTrace, callback);
+}
+
+TaskTimer &TraceIntelPT::GetTimer() { return GetUpdatedStorage().task_timer; }
+
+ScopedTaskTimer &TraceIntelPT::GetThreadTimer(lldb::tid_t tid) {
+ return GetTimer().ForThread(tid);
+}
+
+ScopedTaskTimer &TraceIntelPT::GetGlobalTimer() {
+ return GetTimer().ForGlobal();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
new file mode 100644
index 000000000000..da9cefe9ed95
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
@@ -0,0 +1,293 @@
+//===-- TraceIntelPT.h ------------------------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPT_H
+#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPT_H
+
+#include "TaskTimer.h"
+#include "ThreadDecoder.h"
+#include "TraceIntelPTBundleLoader.h"
+#include "TraceIntelPTMultiCpuDecoder.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/lldb-types.h"
+#include "llvm/Support/raw_ostream.h"
+#include <optional>
+
+namespace lldb_private {
+namespace trace_intel_pt {
+
+class TraceIntelPT : public Trace {
+public:
+ /// Properties to be used with the `settings` command.
+ class PluginProperties : public Properties {
+ public:
+ static llvm::StringRef GetSettingName();
+
+ PluginProperties();
+
+ ~PluginProperties() override = default;
+
+ uint64_t GetInfiniteDecodingLoopVerificationThreshold();
+
+ uint64_t GetExtremelyLargeDecodingThreshold();
+ };
+
+ /// Return the global properties for this trace plug-in.
+ static PluginProperties &GetGlobalProperties();
+
+ void Dump(Stream *s) const override;
+
+ llvm::Expected<FileSpec> SaveToDisk(FileSpec directory,
+ bool compact) override;
+
+ ~TraceIntelPT() override = default;
+
+ /// PluginInterface protocol
+ /// \{
+ llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
+
+ static void Initialize();
+
+ static void Terminate();
+
+ /// Create an instance of this class from a trace bundle.
+ ///
+ /// \param[in] trace_bundle_description
+ /// The description of the trace bundle. See \a Trace::FindPlugin.
+ ///
+ /// \param[in] bundle_dir
+ /// The path to the directory that contains the trace bundle.
+ ///
+ /// \param[in] debugger
+ /// The debugger instance where new Targets will be created as part of the
+ /// JSON data parsing.
+ ///
+ /// \return
+ /// A trace instance or an error in case of failures.
+ static llvm::Expected<lldb::TraceSP> CreateInstanceForTraceBundle(
+ const llvm::json::Value &trace_bundle_description,
+ llvm::StringRef bundle_dir, Debugger &debugger);
+
+ static llvm::Expected<lldb::TraceSP>
+ CreateInstanceForLiveProcess(Process &process);
+
+ static llvm::StringRef GetPluginNameStatic() { return "intel-pt"; }
+
+ static void DebuggerInitialize(Debugger &debugger);
+ /// \}
+
+ lldb::CommandObjectSP
+ GetProcessTraceStartCommand(CommandInterpreter &interpreter) override;
+
+ lldb::CommandObjectSP
+ GetThreadTraceStartCommand(CommandInterpreter &interpreter) override;
+
+ llvm::StringRef GetSchema() override;
+
+ llvm::Expected<lldb::TraceCursorSP> CreateNewCursor(Thread &thread) override;
+
+ void DumpTraceInfo(Thread &thread, Stream &s, bool verbose,
+ bool json) override;
+
+ llvm::Expected<std::optional<uint64_t>> GetRawTraceSize(Thread &thread);
+
+ llvm::Error DoRefreshLiveProcessState(TraceGetStateResponse state,
+ llvm::StringRef json_response) override;
+
+ bool IsTraced(lldb::tid_t tid) override;
+
+ const char *GetStartConfigurationHelp() override;
+
+ /// Start tracing a live process.
+ ///
+ /// More information on the parameters below can be found in the
+ /// jLLDBTraceStart section in lldb/docs/lldb-gdb-remote.txt.
+ ///
+ /// \param[in] ipt_trace_size
+ /// Trace size per thread in bytes.
+ ///
+ /// \param[in] total_buffer_size_limit
+ /// Maximum total trace size per process in bytes.
+ ///
+ /// \param[in] enable_tsc
+ /// Whether to use enable TSC timestamps or not.
+ ///
+ /// \param[in] psb_period
+ /// This value defines the period in which PSB packets will be generated.
+ ///
+ /// \param[in] per_cpu_tracing
+ /// This value defines whether to have an intel pt trace buffer per thread
+ /// or per cpu core.
+ ///
+ /// \param[in] disable_cgroup_filtering
+ /// Disable the cgroup filtering that is automatically applied when doing
+ /// per cpu tracing.
+ ///
+ /// \return
+ /// \a llvm::Error::success if the operation was successful, or
+ /// \a llvm::Error otherwise.
+ llvm::Error Start(uint64_t ipt_trace_size, uint64_t total_buffer_size_limit,
+ bool enable_tsc, std::optional<uint64_t> psb_period,
+ bool m_per_cpu_tracing, bool disable_cgroup_filtering);
+
+ /// \copydoc Trace::Start
+ llvm::Error Start(StructuredData::ObjectSP configuration =
+ StructuredData::ObjectSP()) override;
+
+ /// Start tracing live threads.
+ ///
+ /// More information on the parameters below can be found in the
+ /// jLLDBTraceStart section in lldb/docs/lldb-gdb-remote.txt.
+ ///
+ /// \param[in] tids
+ /// Threads to trace.
+ ///
+ /// \param[in] ipt_trace_size
+ /// Trace size per thread or per cpu core in bytes.
+ ///
+ /// \param[in] enable_tsc
+ /// Whether to use enable TSC timestamps or not.
+ ///
+ /// \param[in] psb_period
+ /// This value defines the period in which PSB packets will be generated.
+ ///
+ /// \return
+ /// \a llvm::Error::success if the operation was successful, or
+ /// \a llvm::Error otherwise.
+ llvm::Error Start(llvm::ArrayRef<lldb::tid_t> tids, uint64_t ipt_trace_size,
+ bool enable_tsc, std::optional<uint64_t> psb_period);
+
+ /// \copydoc Trace::Start
+ llvm::Error Start(llvm::ArrayRef<lldb::tid_t> tids,
+ StructuredData::ObjectSP configuration =
+ StructuredData::ObjectSP()) override;
+
+ /// See \a Trace::OnThreadBinaryDataRead().
+ llvm::Error OnThreadBufferRead(lldb::tid_t tid,
+ OnBinaryDataReadCallback callback);
+
+ /// Get or fetch the cpu information from, for example, /proc/cpuinfo.
+ llvm::Expected<pt_cpu> GetCPUInfo();
+
+ /// Get or fetch the values used to convert to and from TSCs and nanos.
+ std::optional<LinuxPerfZeroTscConversion> GetPerfZeroTscConversion();
+
+ /// \return
+ /// The timer object for this trace.
+ TaskTimer &GetTimer();
+
+ /// \return
+ /// The ScopedTaskTimer object for the given thread in this trace.
+ ScopedTaskTimer &GetThreadTimer(lldb::tid_t tid);
+
+ /// \return
+ /// The global copedTaskTimer object for this trace.
+ ScopedTaskTimer &GetGlobalTimer();
+
+ TraceIntelPTSP GetSharedPtr();
+
+ enum class TraceMode { UserMode, KernelMode };
+
+ TraceMode GetTraceMode();
+
+private:
+ friend class TraceIntelPTBundleLoader;
+
+ llvm::Expected<pt_cpu> GetCPUInfoForLiveProcess();
+
+ /// Postmortem trace constructor
+ ///
+ /// \param[in] bundle_description
+ /// The definition file for the postmortem bundle.
+ ///
+ /// \param[in] traced_processes
+ /// The processes traced in the postmortem session.
+ ///
+ /// \param[in] trace_threads
+ /// The threads traced in the postmortem session. They must belong to the
+ /// processes mentioned above.
+ ///
+ /// \param[in] trace_mode
+ /// The tracing mode of the postmortem session.
+ ///
+ /// \return
+ /// A TraceIntelPT shared pointer instance.
+ /// \{
+ static TraceIntelPTSP CreateInstanceForPostmortemTrace(
+ JSONTraceBundleDescription &bundle_description,
+ llvm::ArrayRef<lldb::ProcessSP> traced_processes,
+ llvm::ArrayRef<lldb::ThreadPostMortemTraceSP> traced_threads,
+ TraceMode trace_mode);
+
+ /// This constructor is used by CreateInstanceForPostmortemTrace to get the
+ /// instance ready before using shared pointers, which is a limitation of C++.
+ TraceIntelPT(JSONTraceBundleDescription &bundle_description,
+ llvm::ArrayRef<lldb::ProcessSP> traced_processes,
+ TraceMode trace_mode);
+ /// \}
+
+ /// Constructor for live processes
+ TraceIntelPT(Process &live_process)
+ : Trace(live_process), trace_mode(TraceMode::UserMode){};
+
+ /// Decode the trace of the given thread that, i.e. recontruct the traced
+ /// instructions.
+ ///
+ /// \param[in] thread
+ /// If \a thread is a \a ThreadTrace, then its internal trace file will be
+ /// decoded. Live threads are not currently supported.
+ ///
+ /// \return
+ /// A \a DecodedThread shared pointer with the decoded instructions. Any
+ /// errors are embedded in the instruction list. An \a llvm::Error is
+ /// returned if the decoder couldn't be properly set up.
+ llvm::Expected<DecodedThreadSP> Decode(Thread &thread);
+
+ /// \return
+ /// The lowest timestamp in nanoseconds in all traces if available, \a
+ /// std::nullopt if all the traces were empty or no trace contained no
+ /// timing information, or an \a llvm::Error if it was not possible to set
+ /// up the decoder for some trace.
+ llvm::Expected<std::optional<uint64_t>> FindBeginningOfTimeNanos();
+
+ // Dump out trace info in JSON format
+ void DumpTraceInfoAsJson(Thread &thread, Stream &s, bool verbose);
+
+ /// We package all the data that can change upon process stops to make sure
+ /// this contract is very visible.
+ /// This variable should only be accessed directly by constructores or live
+ /// process data refreshers.
+ struct Storage {
+ std::optional<TraceIntelPTMultiCpuDecoder> multicpu_decoder;
+ /// These decoders are used for the non-per-cpu case
+ llvm::DenseMap<lldb::tid_t, std::unique_ptr<ThreadDecoder>> thread_decoders;
+ /// Helper variable used to track long running operations for telemetry.
+ TaskTimer task_timer;
+ /// It is provided by either a trace bundle or a live process to convert TSC
+ /// counters to and from nanos. It might not be available on all hosts.
+ std::optional<LinuxPerfZeroTscConversion> tsc_conversion;
+ std::optional<uint64_t> beginning_of_time_nanos;
+ bool beginning_of_time_nanos_calculated = false;
+ } m_storage;
+
+ /// It is provided by either a trace bundle or a live process' "cpuInfo"
+ /// binary data. We don't put it in the Storage because this variable doesn't
+ /// change.
+ std::optional<pt_cpu> m_cpu_info;
+
+ /// Get the storage after refreshing the data in the case of a live process.
+ Storage &GetUpdatedStorage();
+
+ /// The tracing mode of post mortem trace.
+ TraceMode trace_mode;
+};
+
+} // namespace trace_intel_pt
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPT_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.cpp b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.cpp
new file mode 100644
index 000000000000..1a9f6fe30509
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.cpp
@@ -0,0 +1,437 @@
+//===-- TraceIntelPTBundleLoader.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 "TraceIntelPTBundleLoader.h"
+
+#include "../common/ThreadPostMortemTrace.h"
+#include "TraceIntelPT.h"
+#include "TraceIntelPTConstants.h"
+#include "TraceIntelPTJSONStructs.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/ProcessTrace.h"
+#include "lldb/Target/Target.h"
+#include <optional>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::trace_intel_pt;
+using namespace llvm;
+
+FileSpec TraceIntelPTBundleLoader::NormalizePath(const std::string &path) {
+ FileSpec file_spec(path);
+ if (file_spec.IsRelative())
+ file_spec.PrependPathComponent(m_bundle_dir);
+ return file_spec;
+}
+
+Error TraceIntelPTBundleLoader::ParseModule(Target &target,
+ const JSONModule &module) {
+ auto do_parse = [&]() -> Error {
+ FileSpec system_file_spec(module.system_path);
+
+ FileSpec local_file_spec(module.file.has_value() ? *module.file
+ : module.system_path);
+
+ ModuleSpec module_spec;
+ module_spec.GetFileSpec() = local_file_spec;
+ module_spec.GetPlatformFileSpec() = system_file_spec;
+
+ if (module.uuid.has_value())
+ module_spec.GetUUID().SetFromStringRef(*module.uuid);
+
+ Status error;
+ ModuleSP module_sp =
+ target.GetOrCreateModule(module_spec, /*notify*/ false, &error);
+
+ if (error.Fail())
+ return error.ToError();
+
+ bool load_addr_changed = false;
+ module_sp->SetLoadAddress(target, module.load_address.value, false,
+ load_addr_changed);
+ return Error::success();
+ };
+ if (Error err = do_parse())
+ return createStringError(
+ inconvertibleErrorCode(), "Error when parsing module %s. %s",
+ module.system_path.c_str(), toString(std::move(err)).c_str());
+ return Error::success();
+}
+
+Error TraceIntelPTBundleLoader::CreateJSONError(json::Path::Root &root,
+ const json::Value &value) {
+ std::string err;
+ raw_string_ostream os(err);
+ root.printErrorContext(value, os);
+ return createStringError(
+ std::errc::invalid_argument, "%s\n\nContext:\n%s\n\nSchema:\n%s",
+ toString(root.getError()).c_str(), os.str().c_str(), GetSchema().data());
+}
+
+ThreadPostMortemTraceSP
+TraceIntelPTBundleLoader::ParseThread(Process &process,
+ const JSONThread &thread) {
+ lldb::tid_t tid = static_cast<lldb::tid_t>(thread.tid);
+
+ std::optional<FileSpec> trace_file;
+ if (thread.ipt_trace)
+ trace_file = FileSpec(*thread.ipt_trace);
+
+ ThreadPostMortemTraceSP thread_sp =
+ std::make_shared<ThreadPostMortemTrace>(process, tid, trace_file);
+ process.GetThreadList().AddThread(thread_sp);
+ return thread_sp;
+}
+
+Expected<TraceIntelPTBundleLoader::ParsedProcess>
+TraceIntelPTBundleLoader::CreateEmptyProcess(lldb::pid_t pid,
+ llvm::StringRef triple) {
+ TargetSP target_sp;
+ Status error = m_debugger.GetTargetList().CreateTarget(
+ m_debugger, /*user_exe_path*/ StringRef(), triple, eLoadDependentsNo,
+ /*platform_options*/ nullptr, target_sp);
+
+ if (!target_sp)
+ return error.ToError();
+
+ ParsedProcess parsed_process;
+ parsed_process.target_sp = target_sp;
+
+ ProcessTrace::Initialize();
+ ProcessSP process_sp = target_sp->CreateProcess(
+ /*listener*/ nullptr, "trace",
+ /*crash_file*/ nullptr,
+ /*can_connect*/ false);
+
+ process_sp->SetID(static_cast<lldb::pid_t>(pid));
+
+ return parsed_process;
+}
+
+Expected<TraceIntelPTBundleLoader::ParsedProcess>
+TraceIntelPTBundleLoader::ParseProcess(const JSONProcess &process) {
+ Expected<ParsedProcess> parsed_process =
+ CreateEmptyProcess(process.pid, process.triple.value_or(""));
+
+ if (!parsed_process)
+ return parsed_process.takeError();
+
+ ProcessSP process_sp = parsed_process->target_sp->GetProcessSP();
+
+ for (const JSONThread &thread : process.threads)
+ parsed_process->threads.push_back(ParseThread(*process_sp, thread));
+
+ for (const JSONModule &module : process.modules)
+ if (Error err = ParseModule(*parsed_process->target_sp, module))
+ return std::move(err);
+
+ if (!process.threads.empty())
+ process_sp->GetThreadList().SetSelectedThreadByIndexID(0);
+
+ // We invoke DidAttach to create a correct stopped state for the process and
+ // its threads.
+ ArchSpec process_arch;
+ process_sp->DidAttach(process_arch);
+
+ return parsed_process;
+}
+
+Expected<TraceIntelPTBundleLoader::ParsedProcess>
+TraceIntelPTBundleLoader::ParseKernel(
+ const JSONTraceBundleDescription &bundle_description) {
+ Expected<ParsedProcess> parsed_process =
+ CreateEmptyProcess(kDefaultKernelProcessID, "");
+
+ if (!parsed_process)
+ return parsed_process.takeError();
+
+ ProcessSP process_sp = parsed_process->target_sp->GetProcessSP();
+
+ // Add cpus as fake threads
+ for (const JSONCpu &cpu : *bundle_description.cpus) {
+ ThreadPostMortemTraceSP thread_sp = std::make_shared<ThreadPostMortemTrace>(
+ *process_sp, static_cast<lldb::tid_t>(cpu.id), FileSpec(cpu.ipt_trace));
+ thread_sp->SetName(formatv("kernel_cpu_{0}", cpu.id).str().c_str());
+ process_sp->GetThreadList().AddThread(thread_sp);
+ parsed_process->threads.push_back(thread_sp);
+ }
+
+ // Add kernel image
+ FileSpec file_spec(bundle_description.kernel->file);
+ ModuleSpec module_spec;
+ module_spec.GetFileSpec() = file_spec;
+
+ Status error;
+ ModuleSP module_sp =
+ parsed_process->target_sp->GetOrCreateModule(module_spec, false, &error);
+
+ if (error.Fail())
+ return error.ToError();
+
+ lldb::addr_t load_address =
+ bundle_description.kernel->load_address
+ ? bundle_description.kernel->load_address->value
+ : kDefaultKernelLoadAddress;
+
+ bool load_addr_changed = false;
+ module_sp->SetLoadAddress(*parsed_process->target_sp, load_address, false,
+ load_addr_changed);
+
+ process_sp->GetThreadList().SetSelectedThreadByIndexID(0);
+
+ // We invoke DidAttach to create a correct stopped state for the process and
+ // its threads.
+ ArchSpec process_arch;
+ process_sp->DidAttach(process_arch);
+
+ return parsed_process;
+}
+
+Expected<std::vector<TraceIntelPTBundleLoader::ParsedProcess>>
+TraceIntelPTBundleLoader::LoadBundle(
+ const JSONTraceBundleDescription &bundle_description) {
+ std::vector<ParsedProcess> parsed_processes;
+
+ auto HandleError = [&](Error &&err) {
+ // Delete all targets that were created so far in case of failures
+ for (ParsedProcess &parsed_process : parsed_processes)
+ m_debugger.GetTargetList().DeleteTarget(parsed_process.target_sp);
+ return std::move(err);
+ };
+
+ if (bundle_description.processes) {
+ for (const JSONProcess &process : *bundle_description.processes) {
+ if (Expected<ParsedProcess> parsed_process = ParseProcess(process))
+ parsed_processes.push_back(std::move(*parsed_process));
+ else
+ return HandleError(parsed_process.takeError());
+ }
+ }
+
+ if (bundle_description.kernel) {
+ if (Expected<ParsedProcess> kernel_process =
+ ParseKernel(bundle_description))
+ parsed_processes.push_back(std::move(*kernel_process));
+ else
+ return HandleError(kernel_process.takeError());
+ }
+
+ return parsed_processes;
+}
+
+StringRef TraceIntelPTBundleLoader::GetSchema() {
+ static std::string schema;
+ if (schema.empty()) {
+ schema = R"({
+ "type": "intel-pt",
+ "cpuInfo": {
+ // CPU information gotten from, for example, /proc/cpuinfo.
+
+ "vendor": "GenuineIntel" | "unknown",
+ "family": integer,
+ "model": integer,
+ "stepping": integer
+ },
+ "processes?": [
+ {
+ "pid": integer,
+ "triple"?: string,
+ // Optional clang/llvm target triple.
+ // This must be provided if the trace will be created not using the
+ // CLI or on a machine other than where the target was traced.
+ "threads": [
+ // A list of known threads for the given process. When context switch
+ // data is provided, LLDB will automatically create threads for the
+ // this process whenever it finds new threads when traversing the
+ // context switches, so passing values to this list in this case is
+ // optional.
+ {
+ "tid": integer,
+ "iptTrace"?: string
+ // Path to the raw Intel PT buffer file for this thread.
+ }
+ ],
+ "modules": [
+ {
+ "systemPath": string,
+ // Original path of the module at runtime.
+ "file"?: string,
+ // Path to a copy of the file if not available at "systemPath".
+ "loadAddress": integer | string decimal | hex string,
+ // Lowest address of the sections of the module loaded on memory.
+ "uuid"?: string,
+ // Build UUID for the file for sanity checks.
+ }
+ ]
+ }
+ ],
+ "cpus"?: [
+ {
+ "id": integer,
+ // Id of this CPU core.
+ "iptTrace": string,
+ // Path to the raw Intel PT buffer for this cpu core.
+ "contextSwitchTrace": string,
+ // Path to the raw perf_event_open context switch trace file for this cpu core.
+ // The perf_event must have been configured with PERF_SAMPLE_TID and
+ // PERF_SAMPLE_TIME, as well as sample_id_all = 1.
+ }
+ ],
+ "tscPerfZeroConversion"?: {
+ // Values used to convert between TSCs and nanoseconds. See the time_zero
+ // section in https://man7.org/linux/man-pages/man2/perf_event_open.2.html
+ // for information.
+
+ "timeMult": integer,
+ "timeShift": integer,
+ "timeZero": integer | string decimal | hex string,
+ },
+ "kernel"?: {
+ "loadAddress"?: integer | string decimal | hex string,
+ // Kernel's image load address. Defaults to 0xffffffff81000000, which
+ // is a load address of x86 architecture if KASLR is not enabled.
+ "file": string,
+ // Path to the kernel image.
+ }
+}
+
+Notes:
+
+- All paths are either absolute or relative to folder containing the bundle
+ description file.
+- "cpus" is provided if and only if processes[].threads[].iptTrace is not provided.
+- "tscPerfZeroConversion" must be provided if "cpus" is provided.
+- If "kernel" is provided, then the "processes" section must be empty or not
+ passed at all, and the "cpus" section must be provided. This configuration
+ indicates that the kernel was traced and user processes weren't. Besides
+ that, the kernel is treated as a single process with one thread per CPU
+ core. This doesn't handle actual kernel threads, but instead treats
+ all the instructions executed by the kernel on each core as an
+ individual thread.})";
+ }
+ return schema;
+}
+
+Error TraceIntelPTBundleLoader::AugmentThreadsFromContextSwitches(
+ JSONTraceBundleDescription &bundle_description) {
+ if (!bundle_description.cpus || !bundle_description.processes)
+ return Error::success();
+
+ if (!bundle_description.tsc_perf_zero_conversion)
+ return createStringError(inconvertibleErrorCode(),
+ "TSC to nanos conversion values are needed when "
+ "context switch information is provided.");
+
+ DenseMap<lldb::pid_t, JSONProcess *> indexed_processes;
+ DenseMap<JSONProcess *, DenseSet<tid_t>> indexed_threads;
+
+ for (JSONProcess &process : *bundle_description.processes) {
+ indexed_processes[process.pid] = &process;
+ for (JSONThread &thread : process.threads)
+ indexed_threads[&process].insert(thread.tid);
+ }
+
+ auto on_thread_seen = [&](lldb::pid_t pid, tid_t tid) {
+ auto proc = indexed_processes.find(pid);
+ if (proc == indexed_processes.end())
+ return;
+ if (indexed_threads[proc->second].count(tid))
+ return;
+ indexed_threads[proc->second].insert(tid);
+ proc->second->threads.push_back({tid, /*ipt_trace=*/std::nullopt});
+ };
+
+ for (const JSONCpu &cpu : *bundle_description.cpus) {
+ Error err = Trace::OnDataFileRead(
+ FileSpec(cpu.context_switch_trace),
+ [&](ArrayRef<uint8_t> data) -> Error {
+ Expected<std::vector<ThreadContinuousExecution>> executions =
+ DecodePerfContextSwitchTrace(
+ data, cpu.id, *bundle_description.tsc_perf_zero_conversion);
+ if (!executions)
+ return executions.takeError();
+ for (const ThreadContinuousExecution &execution : *executions)
+ on_thread_seen(execution.pid, execution.tid);
+ return Error::success();
+ });
+ if (err)
+ return err;
+ }
+ return Error::success();
+}
+
+Expected<TraceSP> TraceIntelPTBundleLoader::CreateTraceIntelPTInstance(
+ JSONTraceBundleDescription &bundle_description,
+ std::vector<ParsedProcess> &parsed_processes) {
+ std::vector<ThreadPostMortemTraceSP> threads;
+ std::vector<ProcessSP> processes;
+ for (const ParsedProcess &parsed_process : parsed_processes) {
+ processes.push_back(parsed_process.target_sp->GetProcessSP());
+ threads.insert(threads.end(), parsed_process.threads.begin(),
+ parsed_process.threads.end());
+ }
+
+ TraceIntelPT::TraceMode trace_mode = bundle_description.kernel
+ ? TraceIntelPT::TraceMode::KernelMode
+ : TraceIntelPT::TraceMode::UserMode;
+
+ TraceSP trace_instance = TraceIntelPT::CreateInstanceForPostmortemTrace(
+ bundle_description, processes, threads, trace_mode);
+ for (const ParsedProcess &parsed_process : parsed_processes)
+ parsed_process.target_sp->SetTrace(trace_instance);
+
+ return trace_instance;
+}
+
+void TraceIntelPTBundleLoader::NormalizeAllPaths(
+ JSONTraceBundleDescription &bundle_description) {
+ if (bundle_description.processes) {
+ for (JSONProcess &process : *bundle_description.processes) {
+ for (JSONModule &module : process.modules) {
+ module.system_path = NormalizePath(module.system_path).GetPath();
+ if (module.file)
+ module.file = NormalizePath(*module.file).GetPath();
+ }
+ for (JSONThread &thread : process.threads) {
+ if (thread.ipt_trace)
+ thread.ipt_trace = NormalizePath(*thread.ipt_trace).GetPath();
+ }
+ }
+ }
+ if (bundle_description.cpus) {
+ for (JSONCpu &cpu : *bundle_description.cpus) {
+ cpu.context_switch_trace =
+ NormalizePath(cpu.context_switch_trace).GetPath();
+ cpu.ipt_trace = NormalizePath(cpu.ipt_trace).GetPath();
+ }
+ }
+ if (bundle_description.kernel) {
+ bundle_description.kernel->file =
+ NormalizePath(bundle_description.kernel->file).GetPath();
+ }
+}
+
+Expected<TraceSP> TraceIntelPTBundleLoader::Load() {
+ json::Path::Root root("traceBundle");
+ JSONTraceBundleDescription bundle_description;
+ if (!fromJSON(m_bundle_description, bundle_description, root))
+ return CreateJSONError(root, m_bundle_description);
+
+ NormalizeAllPaths(bundle_description);
+
+ if (Error err = AugmentThreadsFromContextSwitches(bundle_description))
+ return std::move(err);
+
+ if (Expected<std::vector<ParsedProcess>> parsed_processes =
+ LoadBundle(bundle_description))
+ return CreateTraceIntelPTInstance(bundle_description, *parsed_processes);
+ else
+ return parsed_processes.takeError();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.h b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.h
new file mode 100644
index 000000000000..691a3f4fa08c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.h
@@ -0,0 +1,130 @@
+//===-- TraceIntelPTBundleLoader.h ----------------------------*- C++ //-*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTBUNDLELOADER_H
+#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTBUNDLELOADER_H
+
+#include "../common/ThreadPostMortemTrace.h"
+#include "TraceIntelPTJSONStructs.h"
+
+namespace lldb_private {
+namespace trace_intel_pt {
+
+class TraceIntelPT;
+
+class TraceIntelPTBundleLoader {
+public:
+ /// Helper struct holding the objects created when parsing a process
+ struct ParsedProcess {
+ lldb::TargetSP target_sp;
+ std::vector<lldb::ThreadPostMortemTraceSP> threads;
+ };
+
+ /// \param[in] debugger
+ /// The debugger that will own the targets to create.
+ ///
+ /// \param[in] bundle_description
+ /// The JSON description of a trace bundle that follows the schema of the
+ /// intel pt trace plug-in.
+ ///
+ /// \param[in] bundle_dir
+ /// The folder where the trace bundle is located.
+ TraceIntelPTBundleLoader(Debugger &debugger,
+ const llvm::json::Value &bundle_description,
+ llvm::StringRef bundle_dir)
+ : m_debugger(debugger), m_bundle_description(bundle_description),
+ m_bundle_dir(bundle_dir) {}
+
+ /// \return
+ /// The JSON schema for the bundle description.
+ static llvm::StringRef GetSchema();
+
+ /// Parse the trace bundle description and create the corresponding \a
+ /// Target objects. In case of an error, no targets are created.
+ ///
+ /// \return
+ /// A \a lldb::TraceSP instance created according to the trace bundle
+ /// information. In case of errors, return a null pointer.
+ llvm::Expected<lldb::TraceSP> Load();
+
+private:
+ /// Resolve non-absolute paths relative to the bundle folder.
+ FileSpec NormalizePath(const std::string &path);
+
+ /// Create a post-mortem thread associated with the given \p process
+ /// using the definition from \p thread.
+ lldb::ThreadPostMortemTraceSP ParseThread(Process &process,
+ const JSONThread &thread);
+
+ /// Given a bundle description and a list of fully parsed processes,
+ /// create an actual Trace instance that "traces" these processes.
+ llvm::Expected<lldb::TraceSP>
+ CreateTraceIntelPTInstance(JSONTraceBundleDescription &bundle_description,
+ std::vector<ParsedProcess> &parsed_processes);
+
+ /// Create an empty Process object with given pid and target.
+ llvm::Expected<ParsedProcess> CreateEmptyProcess(lldb::pid_t pid,
+ llvm::StringRef triple);
+
+ /// Create the corresponding Threads and Process objects given the JSON
+ /// process definition.
+ ///
+ /// \param[in] process
+ /// The JSON process definition
+ llvm::Expected<ParsedProcess> ParseProcess(const JSONProcess &process);
+
+ /// Create a module associated with the given \p target using the definition
+ /// from \p module.
+ llvm::Error ParseModule(Target &target, const JSONModule &module);
+
+ /// Create a kernel process and cpu threads given the JSON kernel definition.
+ llvm::Expected<ParsedProcess>
+ ParseKernel(const JSONTraceBundleDescription &bundle_description);
+
+ /// Create a user-friendly error message upon a JSON-parsing failure using the
+ /// \a json::ObjectMapper functionality.
+ ///
+ /// \param[in] root
+ /// The \a llvm::json::Path::Root used to parse the JSON \a value.
+ ///
+ /// \param[in] value
+ /// The json value that failed to parse.
+ ///
+ /// \return
+ /// An \a llvm::Error containing the user-friendly error message.
+ llvm::Error CreateJSONError(llvm::json::Path::Root &root,
+ const llvm::json::Value &value);
+
+ /// Create the corresponding Process, Thread and Module objects given this
+ /// bundle description.
+ llvm::Expected<std::vector<ParsedProcess>>
+ LoadBundle(const JSONTraceBundleDescription &bundle_description);
+
+ /// When applicable, augment the list of threads in the trace bundle by
+ /// inspecting the context switch trace. This only applies for threads of
+ /// processes already specified in this bundle description.
+ ///
+ /// \return
+ /// An \a llvm::Error in case if failures, or \a llvm::Error::success
+ /// otherwise.
+ llvm::Error AugmentThreadsFromContextSwitches(
+ JSONTraceBundleDescription &bundle_description);
+
+ /// Modifiy the bundle description by normalizing all the paths relative to
+ /// the session file directory.
+ void NormalizeAllPaths(JSONTraceBundleDescription &bundle_description);
+
+ Debugger &m_debugger;
+ const llvm::json::Value &m_bundle_description;
+ const std::string m_bundle_dir;
+};
+
+} // namespace trace_intel_pt
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTBUNDLELOADER_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.cpp b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.cpp
new file mode 100644
index 000000000000..a09bb372bb01
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.cpp
@@ -0,0 +1,402 @@
+//===-- TraceIntelPTBundleSaver.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 "TraceIntelPTBundleSaver.h"
+#include "PerfContextSwitchDecoder.h"
+#include "TraceIntelPT.h"
+#include "TraceIntelPTConstants.h"
+#include "TraceIntelPTJSONStructs.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadList.h"
+#include "lldb/lldb-types.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/JSON.h"
+#include <fstream>
+#include <iostream>
+#include <optional>
+#include <sstream>
+#include <string>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::trace_intel_pt;
+using namespace llvm;
+
+/// Strip the \p directory component from the given \p path. It assumes that \p
+/// directory is a prefix of \p path.
+static std::string GetRelativePath(const FileSpec &directory,
+ const FileSpec &path) {
+ return path.GetPath().substr(directory.GetPath().size() + 1);
+}
+
+/// Write a stream of bytes from \p data to the given output file.
+/// It creates or overwrites the output file, but not append.
+static llvm::Error WriteBytesToDisk(FileSpec &output_file,
+ ArrayRef<uint8_t> data) {
+ std::basic_fstream<char> out_fs = std::fstream(
+ output_file.GetPath().c_str(), std::ios::out | std::ios::binary);
+ if (!data.empty())
+ out_fs.write(reinterpret_cast<const char *>(&data[0]), data.size());
+
+ out_fs.close();
+ if (!out_fs)
+ return createStringError(inconvertibleErrorCode(),
+ formatv("couldn't write to the file {0}",
+ output_file.GetPath().c_str()));
+ return Error::success();
+}
+
+/// Save the trace bundle description JSON object inside the given directory
+/// as a file named \a trace.json.
+///
+/// \param[in] trace_bundle_description
+/// The trace bundle description as JSON Object.
+///
+/// \param[in] directory
+/// The directory where the JSON file will be saved.
+///
+/// \return
+/// A \a FileSpec pointing to the bundle description file, or an \a
+/// llvm::Error otherwise.
+static Expected<FileSpec>
+SaveTraceBundleDescription(const llvm::json::Value &trace_bundle_description,
+ const FileSpec &directory) {
+ FileSpec trace_path = directory;
+ trace_path.AppendPathComponent("trace.json");
+ std::ofstream os(trace_path.GetPath());
+ os << formatv("{0:2}", trace_bundle_description).str();
+ os.close();
+ if (!os)
+ return createStringError(inconvertibleErrorCode(),
+ formatv("couldn't write to the file {0}",
+ trace_path.GetPath().c_str()));
+ return trace_path;
+}
+
+/// Build the threads sub-section of the trace bundle description file.
+/// Any associated binary files are created inside the given directory.
+///
+/// \param[in] process
+/// The process being traced.
+///
+/// \param[in] directory
+/// The directory where files will be saved when building the threads
+/// section.
+///
+/// \return
+/// The threads section or \a llvm::Error in case of failures.
+static llvm::Expected<std::vector<JSONThread>>
+BuildThreadsSection(Process &process, FileSpec directory) {
+ std::vector<JSONThread> json_threads;
+ TraceSP trace_sp = process.GetTarget().GetTrace();
+
+ FileSpec threads_dir = directory;
+ threads_dir.AppendPathComponent("threads");
+ sys::fs::create_directories(threads_dir.GetPath().c_str());
+
+ for (ThreadSP thread_sp : process.Threads()) {
+ lldb::tid_t tid = thread_sp->GetID();
+ if (!trace_sp->IsTraced(tid))
+ continue;
+
+ JSONThread json_thread;
+ json_thread.tid = tid;
+
+ if (trace_sp->GetTracedCpus().empty()) {
+ FileSpec output_file = threads_dir;
+ output_file.AppendPathComponent(std::to_string(tid) + ".intelpt_trace");
+ json_thread.ipt_trace = GetRelativePath(directory, output_file);
+
+ llvm::Error err = process.GetTarget().GetTrace()->OnThreadBinaryDataRead(
+ tid, IntelPTDataKinds::kIptTrace,
+ [&](llvm::ArrayRef<uint8_t> data) -> llvm::Error {
+ return WriteBytesToDisk(output_file, data);
+ });
+ if (err)
+ return std::move(err);
+ }
+
+ json_threads.push_back(std::move(json_thread));
+ }
+ return json_threads;
+}
+
+/// \return
+/// an \a llvm::Error in case of failures, \a std::nullopt if the trace is not
+/// written to disk because the trace is empty and the \p compact flag is
+/// present, or the FileSpec of the trace file on disk.
+static Expected<std::optional<FileSpec>>
+WriteContextSwitchTrace(TraceIntelPT &trace_ipt, lldb::cpu_id_t cpu_id,
+ const FileSpec &cpus_dir, bool compact) {
+ FileSpec output_context_switch_trace = cpus_dir;
+ output_context_switch_trace.AppendPathComponent(std::to_string(cpu_id) +
+ ".perf_context_switch_trace");
+
+ bool should_skip = false;
+
+ Error err = trace_ipt.OnCpuBinaryDataRead(
+ cpu_id, IntelPTDataKinds::kPerfContextSwitchTrace,
+ [&](llvm::ArrayRef<uint8_t> data) -> llvm::Error {
+ if (!compact)
+ return WriteBytesToDisk(output_context_switch_trace, data);
+
+ std::set<lldb::pid_t> pids;
+ for (Process *process : trace_ipt.GetAllProcesses())
+ pids.insert(process->GetID());
+
+ Expected<std::vector<uint8_t>> compact_context_switch_trace =
+ FilterProcessesFromContextSwitchTrace(data, pids);
+ if (!compact_context_switch_trace)
+ return compact_context_switch_trace.takeError();
+
+ if (compact_context_switch_trace->empty()) {
+ should_skip = true;
+ return Error::success();
+ }
+
+ return WriteBytesToDisk(output_context_switch_trace,
+ *compact_context_switch_trace);
+ });
+ if (err)
+ return std::move(err);
+
+ if (should_skip)
+ return std::nullopt;
+ return output_context_switch_trace;
+}
+
+static Expected<FileSpec> WriteIntelPTTrace(TraceIntelPT &trace_ipt,
+ lldb::cpu_id_t cpu_id,
+ const FileSpec &cpus_dir) {
+ FileSpec output_trace = cpus_dir;
+ output_trace.AppendPathComponent(std::to_string(cpu_id) + ".intelpt_trace");
+
+ Error err = trace_ipt.OnCpuBinaryDataRead(
+ cpu_id, IntelPTDataKinds::kIptTrace,
+ [&](llvm::ArrayRef<uint8_t> data) -> llvm::Error {
+ return WriteBytesToDisk(output_trace, data);
+ });
+ if (err)
+ return std::move(err);
+ return output_trace;
+}
+
+static llvm::Expected<std::optional<std::vector<JSONCpu>>>
+BuildCpusSection(TraceIntelPT &trace_ipt, FileSpec directory, bool compact) {
+ if (trace_ipt.GetTracedCpus().empty())
+ return std::nullopt;
+
+ std::vector<JSONCpu> json_cpus;
+ FileSpec cpus_dir = directory;
+ cpus_dir.AppendPathComponent("cpus");
+ sys::fs::create_directories(cpus_dir.GetPath().c_str());
+
+ for (lldb::cpu_id_t cpu_id : trace_ipt.GetTracedCpus()) {
+ JSONCpu json_cpu;
+ json_cpu.id = cpu_id;
+ Expected<std::optional<FileSpec>> context_switch_trace_path =
+ WriteContextSwitchTrace(trace_ipt, cpu_id, cpus_dir, compact);
+ if (!context_switch_trace_path)
+ return context_switch_trace_path.takeError();
+ if (!*context_switch_trace_path)
+ continue;
+ json_cpu.context_switch_trace =
+ GetRelativePath(directory, **context_switch_trace_path);
+
+ if (Expected<FileSpec> ipt_trace_path =
+ WriteIntelPTTrace(trace_ipt, cpu_id, cpus_dir))
+ json_cpu.ipt_trace = GetRelativePath(directory, *ipt_trace_path);
+ else
+ return ipt_trace_path.takeError();
+
+ json_cpus.push_back(std::move(json_cpu));
+ }
+ return json_cpus;
+}
+
+/// Build modules sub-section of the trace bundle. The original modules
+/// will be copied over to the \a <directory/modules> folder. Invalid modules
+/// are skipped.
+/// Copying the modules has the benefit of making these
+/// directories self-contained, as the raw traces and modules are part of the
+/// output directory and can be sent to another machine, where lldb can load
+/// them and replicate exactly the same trace session.
+///
+/// \param[in] process
+/// The process being traced.
+///
+/// \param[in] directory
+/// The directory where the modules files will be saved when building
+/// the modules section.
+/// Example: If a module \a libbar.so exists in the path
+/// \a /usr/lib/foo/libbar.so, then it will be copied to
+/// \a <directory>/modules/usr/lib/foo/libbar.so.
+///
+/// \return
+/// The modules section or \a llvm::Error in case of failures.
+static llvm::Expected<std::vector<JSONModule>>
+BuildModulesSection(Process &process, FileSpec directory) {
+ std::vector<JSONModule> json_modules;
+ ModuleList module_list = process.GetTarget().GetImages();
+ for (size_t i = 0; i < module_list.GetSize(); ++i) {
+ ModuleSP module_sp(module_list.GetModuleAtIndex(i));
+ if (!module_sp)
+ continue;
+ std::string system_path = module_sp->GetPlatformFileSpec().GetPath();
+ // TODO: support memory-only libraries like [vdso]
+ if (!module_sp->GetFileSpec().IsAbsolute())
+ continue;
+
+ std::string file = module_sp->GetFileSpec().GetPath();
+ ObjectFile *objfile = module_sp->GetObjectFile();
+ if (objfile == nullptr)
+ continue;
+
+ lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;
+ Address base_addr(objfile->GetBaseAddress());
+ if (base_addr.IsValid() &&
+ !process.GetTarget().GetSectionLoadList().IsEmpty())
+ load_addr = base_addr.GetLoadAddress(&process.GetTarget());
+
+ if (load_addr == LLDB_INVALID_ADDRESS)
+ continue;
+
+ FileSpec path_to_copy_module = directory;
+ path_to_copy_module.AppendPathComponent("modules");
+ path_to_copy_module.AppendPathComponent(system_path);
+ sys::fs::create_directories(path_to_copy_module.GetDirectory().AsCString());
+
+ if (std::error_code ec =
+ llvm::sys::fs::copy_file(file, path_to_copy_module.GetPath()))
+ return createStringError(
+ inconvertibleErrorCode(),
+ formatv("couldn't write to the file. {0}", ec.message()));
+
+ json_modules.push_back(
+ JSONModule{system_path, GetRelativePath(directory, path_to_copy_module),
+ JSONUINT64{load_addr}, module_sp->GetUUID().GetAsString()});
+ }
+ return json_modules;
+}
+
+/// Build the processes section of the trace bundle description object. Besides
+/// returning the processes information, this method saves to disk all modules
+/// and raw traces corresponding to the traced threads of the given process.
+///
+/// \param[in] process
+/// The process being traced.
+///
+/// \param[in] directory
+/// The directory where files will be saved when building the processes
+/// section.
+///
+/// \return
+/// The processes section or \a llvm::Error in case of failures.
+static llvm::Expected<JSONProcess>
+BuildProcessSection(Process &process, const FileSpec &directory) {
+ Expected<std::vector<JSONThread>> json_threads =
+ BuildThreadsSection(process, directory);
+ if (!json_threads)
+ return json_threads.takeError();
+
+ Expected<std::vector<JSONModule>> json_modules =
+ BuildModulesSection(process, directory);
+ if (!json_modules)
+ return json_modules.takeError();
+
+ return JSONProcess{
+ process.GetID(),
+ process.GetTarget().GetArchitecture().GetTriple().getTriple(),
+ json_threads.get(), json_modules.get()};
+}
+
+/// See BuildProcessSection()
+static llvm::Expected<std::vector<JSONProcess>>
+BuildProcessesSection(TraceIntelPT &trace_ipt, const FileSpec &directory) {
+ std::vector<JSONProcess> processes;
+ for (Process *process : trace_ipt.GetAllProcesses()) {
+ if (llvm::Expected<JSONProcess> json_process =
+ BuildProcessSection(*process, directory))
+ processes.push_back(std::move(*json_process));
+ else
+ return json_process.takeError();
+ }
+ return processes;
+}
+
+static llvm::Expected<JSONKernel>
+BuildKernelSection(TraceIntelPT &trace_ipt, const FileSpec &directory) {
+ JSONKernel json_kernel;
+ std::vector<Process *> processes = trace_ipt.GetAllProcesses();
+ Process *kernel_process = processes[0];
+
+ assert(processes.size() == 1 && "User processeses exist in kernel mode");
+ assert(kernel_process->GetID() == kDefaultKernelProcessID &&
+ "Kernel process not exist");
+
+ Expected<std::vector<JSONModule>> json_modules =
+ BuildModulesSection(*kernel_process, directory);
+ if (!json_modules)
+ return json_modules.takeError();
+
+ JSONModule kernel_image = json_modules.get()[0];
+ return JSONKernel{kernel_image.load_address, kernel_image.system_path};
+}
+
+Expected<FileSpec> TraceIntelPTBundleSaver::SaveToDisk(TraceIntelPT &trace_ipt,
+ FileSpec directory,
+ bool compact) {
+ if (std::error_code ec =
+ sys::fs::create_directories(directory.GetPath().c_str()))
+ return llvm::errorCodeToError(ec);
+
+ Expected<pt_cpu> cpu_info = trace_ipt.GetCPUInfo();
+ if (!cpu_info)
+ return cpu_info.takeError();
+
+ FileSystem::Instance().Resolve(directory);
+
+ Expected<std::optional<std::vector<JSONCpu>>> json_cpus =
+ BuildCpusSection(trace_ipt, directory, compact);
+ if (!json_cpus)
+ return json_cpus.takeError();
+
+ std::optional<std::vector<JSONProcess>> json_processes;
+ std::optional<JSONKernel> json_kernel;
+
+ if (trace_ipt.GetTraceMode() == TraceIntelPT::TraceMode::KernelMode) {
+ Expected<std::optional<JSONKernel>> exp_json_kernel =
+ BuildKernelSection(trace_ipt, directory);
+ if (!exp_json_kernel)
+ return exp_json_kernel.takeError();
+ else
+ json_kernel = *exp_json_kernel;
+ } else {
+ Expected<std::optional<std::vector<JSONProcess>>> exp_json_processes =
+ BuildProcessesSection(trace_ipt, directory);
+ if (!exp_json_processes)
+ return exp_json_processes.takeError();
+ else
+ json_processes = *exp_json_processes;
+ }
+
+ JSONTraceBundleDescription json_intel_pt_bundle_desc{
+ "intel-pt",
+ *cpu_info,
+ json_processes,
+ *json_cpus,
+ trace_ipt.GetPerfZeroTscConversion(),
+ json_kernel};
+
+ return SaveTraceBundleDescription(toJSON(json_intel_pt_bundle_desc),
+ directory);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.h b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.h
new file mode 100644
index 000000000000..f5a0301217c7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.h
@@ -0,0 +1,48 @@
+//===-- TraceIntelPTBundleSaver.h ----------------------------*- C++ //-*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTBUNDLESAVER_H
+#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTBUNDLESAVER_H
+
+#include "TraceIntelPT.h"
+#include "TraceIntelPTJSONStructs.h"
+
+namespace lldb_private {
+namespace trace_intel_pt {
+
+class TraceIntelPTBundleSaver {
+public:
+ /// Save the Intel PT trace of a live process to the specified directory,
+ /// which will be created if needed. This will also create a file
+ /// \a <directory>/trace.json with the description of the trace
+ /// bundle, along with others files which contain the actual trace data.
+ /// The trace.json file can be used later as input for the "trace load"
+ /// command to load the trace in LLDB.
+ ///
+ /// \param[in] trace_ipt
+ /// The Intel PT trace to be saved to disk.
+ ///
+ /// \param[in] directory
+ /// The directory where the trace bundle will be created.
+ ///
+ /// \param[in] compact
+ /// Filter out information irrelevant to the traced processes in the
+ /// context switch and intel pt traces when using per-cpu mode. This
+ /// effectively reduces the size of those traces.
+ ///
+ /// \return
+ /// A \a FileSpec pointing to the bundle description file, or an \a
+ /// llvm::Error otherwise.
+ llvm::Expected<FileSpec> SaveToDisk(TraceIntelPT &trace_ipt,
+ FileSpec directory, bool compact);
+};
+
+} // namespace trace_intel_pt
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTBUNDLESAVER_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTConstants.h b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTConstants.h
new file mode 100644
index 000000000000..e80f512457cf
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTConstants.h
@@ -0,0 +1,36 @@
+//===-- TraceIntelPTConstants.h ---------------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_CONSTANTS_H
+#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_CONSTANTS_H
+
+#include "lldb/lldb-types.h"
+#include <cstddef>
+#include <optional>
+
+namespace lldb_private {
+namespace trace_intel_pt {
+
+const size_t kDefaultIptTraceSize = 4 * 1024; // 4KB
+const size_t kDefaultProcessBufferSizeLimit = 5 * 1024 * 1024; // 500MB
+const bool kDefaultEnableTscValue = false;
+const std::optional<size_t> kDefaultPsbPeriod;
+const bool kDefaultPerCpuTracing = false;
+const bool kDefaultDisableCgroupFiltering = false;
+
+// Physical address where the kernel is loaded in x86 architecture. Refer to
+// https://github.com/torvalds/linux/blob/master/Documentation/x86/x86_64/mm.rst
+// for the start address of kernel text section.
+// The kernel entry point is 0x1000000 by default when KASLR is disabled.
+const lldb::addr_t kDefaultKernelLoadAddress = 0xffffffff81000000;
+const lldb::pid_t kDefaultKernelProcessID = 1;
+
+} // namespace trace_intel_pt
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_CONSTANTS_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp
new file mode 100644
index 000000000000..52ca69420587
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp
@@ -0,0 +1,189 @@
+//===-- TraceIntelPTJSONStructs.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 "TraceIntelPTJSONStructs.h"
+#include "llvm/Support/JSON.h"
+#include <optional>
+#include <string>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::trace_intel_pt;
+using namespace llvm;
+using namespace llvm::json;
+
+namespace lldb_private {
+namespace trace_intel_pt {
+
+std::optional<std::vector<lldb::cpu_id_t>>
+JSONTraceBundleDescription::GetCpuIds() {
+ if (!cpus)
+ return std::nullopt;
+ std::vector<lldb::cpu_id_t> cpu_ids;
+ for (const JSONCpu &cpu : *cpus)
+ cpu_ids.push_back(cpu.id);
+ return cpu_ids;
+}
+
+json::Value toJSON(const JSONModule &module) {
+ json::Object json_module;
+ json_module["systemPath"] = module.system_path;
+ if (module.file)
+ json_module["file"] = *module.file;
+ json_module["loadAddress"] = toJSON(module.load_address, true);
+ if (module.uuid)
+ json_module["uuid"] = *module.uuid;
+ return std::move(json_module);
+}
+
+bool fromJSON(const json::Value &value, JSONModule &module, Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("systemPath", module.system_path) &&
+ o.map("file", module.file) &&
+ o.map("loadAddress", module.load_address) &&
+ o.map("uuid", module.uuid);
+}
+
+json::Value toJSON(const JSONThread &thread) {
+ json::Object obj{{"tid", thread.tid}};
+ if (thread.ipt_trace)
+ obj["iptTrace"] = *thread.ipt_trace;
+ return obj;
+}
+
+bool fromJSON(const json::Value &value, JSONThread &thread, Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("tid", thread.tid) && o.map("iptTrace", thread.ipt_trace);
+}
+
+json::Value toJSON(const JSONProcess &process) {
+ return Object{
+ {"pid", process.pid},
+ {"triple", process.triple},
+ {"threads", process.threads},
+ {"modules", process.modules},
+ };
+}
+
+bool fromJSON(const json::Value &value, JSONProcess &process, Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("pid", process.pid) && o.map("triple", process.triple) &&
+ o.map("threads", process.threads) && o.map("modules", process.modules);
+}
+
+json::Value toJSON(const JSONCpu &cpu) {
+ return Object{
+ {"id", cpu.id},
+ {"iptTrace", cpu.ipt_trace},
+ {"contextSwitchTrace", cpu.context_switch_trace},
+ };
+}
+
+bool fromJSON(const json::Value &value, JSONCpu &cpu, Path path) {
+ ObjectMapper o(value, path);
+ uint64_t cpu_id;
+ if (!(o && o.map("id", cpu_id) && o.map("iptTrace", cpu.ipt_trace) &&
+ o.map("contextSwitchTrace", cpu.context_switch_trace)))
+ return false;
+ cpu.id = cpu_id;
+ return true;
+}
+
+json::Value toJSON(const pt_cpu &cpu_info) {
+ return Object{
+ {"vendor", cpu_info.vendor == pcv_intel ? "GenuineIntel" : "Unknown"},
+ {"family", cpu_info.family},
+ {"model", cpu_info.model},
+ {"stepping", cpu_info.stepping},
+ };
+}
+
+bool fromJSON(const json::Value &value, pt_cpu &cpu_info, Path path) {
+ ObjectMapper o(value, path);
+ std::string vendor;
+ uint64_t family, model, stepping;
+ if (!(o && o.map("vendor", vendor) && o.map("family", family) &&
+ o.map("model", model) && o.map("stepping", stepping)))
+ return false;
+ cpu_info.vendor = vendor == "GenuineIntel" ? pcv_intel : pcv_unknown;
+ cpu_info.family = family;
+ cpu_info.model = model;
+ cpu_info.stepping = stepping;
+ return true;
+}
+
+json::Value toJSON(const JSONKernel &kernel) {
+ json::Object json_module;
+ if (kernel.load_address)
+ json_module["loadAddress"] = toJSON(*kernel.load_address, true);
+ json_module["file"] = kernel.file;
+ return std::move(json_module);
+}
+
+bool fromJSON(const json::Value &value, JSONKernel &kernel, Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("loadAddress", kernel.load_address) &&
+ o.map("file", kernel.file);
+}
+
+json::Value toJSON(const JSONTraceBundleDescription &bundle_description) {
+ return Object{
+ {"type", bundle_description.type},
+ {"processes", bundle_description.processes},
+ // We have to do this because the compiler fails at doing it
+ // automatically because pt_cpu is not in a namespace
+ {"cpuInfo", toJSON(bundle_description.cpu_info)},
+ {"cpus", bundle_description.cpus},
+ {"tscPerfZeroConversion", bundle_description.tsc_perf_zero_conversion},
+ {"kernel", bundle_description.kernel}};
+}
+
+bool fromJSON(const json::Value &value,
+ JSONTraceBundleDescription &bundle_description, Path path) {
+ ObjectMapper o(value, path);
+ if (!(o && o.map("processes", bundle_description.processes) &&
+ o.map("type", bundle_description.type) &&
+ o.map("cpus", bundle_description.cpus) &&
+ o.map("tscPerfZeroConversion",
+ bundle_description.tsc_perf_zero_conversion) &&
+ o.map("kernel", bundle_description.kernel)))
+ return false;
+ if (bundle_description.cpus && !bundle_description.tsc_perf_zero_conversion) {
+ path.report(
+ "\"tscPerfZeroConversion\" is required when \"cpus\" is provided");
+ return false;
+ }
+ // We have to do this because the compiler fails at doing it automatically
+ // because pt_cpu is not in a namespace
+ if (!fromJSON(*value.getAsObject()->get("cpuInfo"),
+ bundle_description.cpu_info, path.field("cpuInfo")))
+ return false;
+
+ // When kernel section is present, this is kernel-only tracing. Thus, throw an
+ // error if the "processes" section is non-empty or the "cpus" section is not
+ // present.
+ if (bundle_description.kernel) {
+ if (bundle_description.processes &&
+ !bundle_description.processes->empty()) {
+ path.report("\"processes\" must be empty when \"kernel\" is provided");
+ return false;
+ }
+ if (!bundle_description.cpus) {
+ path.report("\"cpus\" is required when \"kernel\" is provided");
+ return false;
+ }
+ } else if (!bundle_description.processes) {
+ // Usermode tracing requires processes section.
+ path.report("\"processes\" is required when \"kernel\" is not provided");
+ return false;
+ }
+ return true;
+}
+
+} // namespace trace_intel_pt
+} // namespace lldb_private
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h
new file mode 100644
index 000000000000..93156aa4aa9e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h
@@ -0,0 +1,101 @@
+//===-- TraceIntelPTJSONStructs.h -----------------------------*- C++ //-*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTJSONSTRUCTS_H
+#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTJSONSTRUCTS_H
+
+#include "lldb/Utility/TraceIntelPTGDBRemotePackets.h"
+#include "lldb/lldb-types.h"
+#include "llvm/Support/JSON.h"
+#include <intel-pt.h>
+#include <optional>
+#include <vector>
+
+namespace lldb_private {
+namespace trace_intel_pt {
+
+struct JSONModule {
+ std::string system_path;
+ std::optional<std::string> file;
+ JSONUINT64 load_address;
+ std::optional<std::string> uuid;
+};
+
+struct JSONThread {
+ uint64_t tid;
+ std::optional<std::string> ipt_trace;
+};
+
+struct JSONProcess {
+ uint64_t pid;
+ std::optional<std::string> triple;
+ std::vector<JSONThread> threads;
+ std::vector<JSONModule> modules;
+};
+
+struct JSONCpu {
+ lldb::cpu_id_t id;
+ std::string ipt_trace;
+ std::string context_switch_trace;
+};
+
+struct JSONKernel {
+ std::optional<JSONUINT64> load_address;
+ std::string file;
+};
+
+struct JSONTraceBundleDescription {
+ std::string type;
+ pt_cpu cpu_info;
+ std::optional<std::vector<JSONProcess>> processes;
+ std::optional<std::vector<JSONCpu>> cpus;
+ std::optional<LinuxPerfZeroTscConversion> tsc_perf_zero_conversion;
+ std::optional<JSONKernel> kernel;
+
+ std::optional<std::vector<lldb::cpu_id_t>> GetCpuIds();
+};
+
+llvm::json::Value toJSON(const JSONModule &module);
+
+llvm::json::Value toJSON(const JSONThread &thread);
+
+llvm::json::Value toJSON(const JSONProcess &process);
+
+llvm::json::Value toJSON(const JSONCpu &cpu);
+
+llvm::json::Value toJSON(const pt_cpu &cpu_info);
+
+llvm::json::Value toJSON(const JSONKernel &kernel);
+
+llvm::json::Value toJSON(const JSONTraceBundleDescription &bundle_description);
+
+bool fromJSON(const llvm::json::Value &value, JSONModule &module,
+ llvm::json::Path path);
+
+bool fromJSON(const llvm::json::Value &value, JSONThread &thread,
+ llvm::json::Path path);
+
+bool fromJSON(const llvm::json::Value &value, JSONProcess &process,
+ llvm::json::Path path);
+
+bool fromJSON(const llvm::json::Value &value, JSONCpu &cpu,
+ llvm::json::Path path);
+
+bool fromJSON(const llvm::json::Value &value, pt_cpu &cpu_info,
+ llvm::json::Path path);
+
+bool fromJSON(const llvm::json::Value &value, JSONModule &kernel,
+ llvm::json::Path path);
+
+bool fromJSON(const llvm::json::Value &value,
+ JSONTraceBundleDescription &bundle_description,
+ llvm::json::Path path);
+} // namespace trace_intel_pt
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTJSONSTRUCTS_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCpuDecoder.cpp b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCpuDecoder.cpp
new file mode 100644
index 000000000000..e2ca8b4d1db0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCpuDecoder.cpp
@@ -0,0 +1,238 @@
+//===-- TraceIntelPTMultiCpuDecoder.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 "TraceIntelPTMultiCpuDecoder.h"
+#include "TraceIntelPT.h"
+#include "llvm/Support/Error.h"
+#include <optional>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::trace_intel_pt;
+using namespace llvm;
+
+TraceIntelPTMultiCpuDecoder::TraceIntelPTMultiCpuDecoder(
+ TraceIntelPTSP trace_sp)
+ : m_trace_wp(trace_sp) {
+ for (Process *proc : trace_sp->GetAllProcesses()) {
+ for (ThreadSP thread_sp : proc->GetThreadList().Threads()) {
+ m_tids.insert(thread_sp->GetID());
+ }
+ }
+}
+
+TraceIntelPTSP TraceIntelPTMultiCpuDecoder::GetTrace() {
+ return m_trace_wp.lock();
+}
+
+bool TraceIntelPTMultiCpuDecoder::TracesThread(lldb::tid_t tid) const {
+ return m_tids.count(tid);
+}
+
+Expected<std::optional<uint64_t>> TraceIntelPTMultiCpuDecoder::FindLowestTSC() {
+ std::optional<uint64_t> lowest_tsc;
+ TraceIntelPTSP trace_sp = GetTrace();
+
+ Error err = GetTrace()->OnAllCpusBinaryDataRead(
+ IntelPTDataKinds::kIptTrace,
+ [&](const DenseMap<cpu_id_t, ArrayRef<uint8_t>> &buffers) -> Error {
+ for (auto &cpu_id_to_buffer : buffers) {
+ Expected<std::optional<uint64_t>> tsc =
+ FindLowestTSCInTrace(*trace_sp, cpu_id_to_buffer.second);
+ if (!tsc)
+ return tsc.takeError();
+ if (*tsc && (!lowest_tsc || *lowest_tsc > **tsc))
+ lowest_tsc = **tsc;
+ }
+ return Error::success();
+ });
+ if (err)
+ return std::move(err);
+ return lowest_tsc;
+}
+
+Expected<DecodedThreadSP> TraceIntelPTMultiCpuDecoder::Decode(Thread &thread) {
+ if (Error err = CorrelateContextSwitchesAndIntelPtTraces())
+ return std::move(err);
+
+ TraceIntelPTSP trace_sp = GetTrace();
+
+ return trace_sp->GetThreadTimer(thread.GetID())
+ .TimeTask("Decoding instructions", [&]() -> Expected<DecodedThreadSP> {
+ auto it = m_decoded_threads.find(thread.GetID());
+ if (it != m_decoded_threads.end())
+ return it->second;
+
+ DecodedThreadSP decoded_thread_sp = std::make_shared<DecodedThread>(
+ thread.shared_from_this(), trace_sp->GetPerfZeroTscConversion());
+
+ Error err = trace_sp->OnAllCpusBinaryDataRead(
+ IntelPTDataKinds::kIptTrace,
+ [&](const DenseMap<cpu_id_t, ArrayRef<uint8_t>> &buffers) -> Error {
+ auto it =
+ m_continuous_executions_per_thread->find(thread.GetID());
+ if (it != m_continuous_executions_per_thread->end())
+ return DecodeSystemWideTraceForThread(
+ *decoded_thread_sp, *trace_sp, buffers, it->second);
+
+ return Error::success();
+ });
+ if (err)
+ return std::move(err);
+
+ m_decoded_threads.try_emplace(thread.GetID(), decoded_thread_sp);
+ return decoded_thread_sp;
+ });
+}
+
+static Expected<std::vector<PSBBlock>> GetPSBBlocksForCPU(TraceIntelPT &trace,
+ cpu_id_t cpu_id) {
+ std::vector<PSBBlock> psb_blocks;
+ Error err = trace.OnCpuBinaryDataRead(
+ cpu_id, IntelPTDataKinds::kIptTrace,
+ [&](ArrayRef<uint8_t> data) -> Error {
+ Expected<std::vector<PSBBlock>> split_trace =
+ SplitTraceIntoPSBBlock(trace, data, /*expect_tscs=*/true);
+ if (!split_trace)
+ return split_trace.takeError();
+
+ psb_blocks = std::move(*split_trace);
+ return Error::success();
+ });
+ if (err)
+ return std::move(err);
+ return psb_blocks;
+}
+
+Expected<DenseMap<lldb::tid_t, std::vector<IntelPTThreadContinousExecution>>>
+TraceIntelPTMultiCpuDecoder::DoCorrelateContextSwitchesAndIntelPtTraces() {
+ DenseMap<lldb::tid_t, std::vector<IntelPTThreadContinousExecution>>
+ continuous_executions_per_thread;
+ TraceIntelPTSP trace_sp = GetTrace();
+
+ std::optional<LinuxPerfZeroTscConversion> conv_opt =
+ trace_sp->GetPerfZeroTscConversion();
+ if (!conv_opt)
+ return createStringError(
+ inconvertibleErrorCode(),
+ "TSC to nanoseconds conversion values were not found");
+
+ LinuxPerfZeroTscConversion tsc_conversion = *conv_opt;
+
+ for (cpu_id_t cpu_id : trace_sp->GetTracedCpus()) {
+ Expected<std::vector<PSBBlock>> psb_blocks =
+ GetPSBBlocksForCPU(*trace_sp, cpu_id);
+ if (!psb_blocks)
+ return psb_blocks.takeError();
+
+ m_total_psb_blocks += psb_blocks->size();
+ // We'll be iterating through the thread continuous executions and the intel
+ // pt subtraces sorted by time.
+ auto it = psb_blocks->begin();
+ auto on_new_thread_execution =
+ [&](const ThreadContinuousExecution &thread_execution) {
+ IntelPTThreadContinousExecution execution(thread_execution);
+
+ for (; it != psb_blocks->end() &&
+ *it->tsc < thread_execution.GetEndTSC();
+ it++) {
+ if (*it->tsc > thread_execution.GetStartTSC()) {
+ execution.psb_blocks.push_back(*it);
+ } else {
+ m_unattributed_psb_blocks++;
+ }
+ }
+ continuous_executions_per_thread[thread_execution.tid].push_back(
+ execution);
+ };
+ Error err = trace_sp->OnCpuBinaryDataRead(
+ cpu_id, IntelPTDataKinds::kPerfContextSwitchTrace,
+ [&](ArrayRef<uint8_t> data) -> Error {
+ Expected<std::vector<ThreadContinuousExecution>> executions =
+ DecodePerfContextSwitchTrace(data, cpu_id, tsc_conversion);
+ if (!executions)
+ return executions.takeError();
+ for (const ThreadContinuousExecution &exec : *executions)
+ on_new_thread_execution(exec);
+ return Error::success();
+ });
+ if (err)
+ return std::move(err);
+
+ m_unattributed_psb_blocks += psb_blocks->end() - it;
+ }
+ // We now sort the executions of each thread to have them ready for
+ // instruction decoding
+ for (auto &tid_executions : continuous_executions_per_thread)
+ std::sort(tid_executions.second.begin(), tid_executions.second.end());
+
+ return continuous_executions_per_thread;
+}
+
+Error TraceIntelPTMultiCpuDecoder::CorrelateContextSwitchesAndIntelPtTraces() {
+ if (m_setup_error)
+ return createStringError(inconvertibleErrorCode(), m_setup_error->c_str());
+
+ if (m_continuous_executions_per_thread)
+ return Error::success();
+
+ Error err = GetTrace()->GetGlobalTimer().TimeTask(
+ "Context switch and Intel PT traces correlation", [&]() -> Error {
+ if (auto correlation = DoCorrelateContextSwitchesAndIntelPtTraces()) {
+ m_continuous_executions_per_thread.emplace(std::move(*correlation));
+ return Error::success();
+ } else {
+ return correlation.takeError();
+ }
+ });
+ if (err) {
+ m_setup_error = toString(std::move(err));
+ return createStringError(inconvertibleErrorCode(), m_setup_error->c_str());
+ }
+ return Error::success();
+}
+
+size_t TraceIntelPTMultiCpuDecoder::GetNumContinuousExecutionsForThread(
+ lldb::tid_t tid) const {
+ if (!m_continuous_executions_per_thread)
+ return 0;
+ auto it = m_continuous_executions_per_thread->find(tid);
+ if (it == m_continuous_executions_per_thread->end())
+ return 0;
+ return it->second.size();
+}
+
+size_t TraceIntelPTMultiCpuDecoder::GetTotalContinuousExecutionsCount() const {
+ if (!m_continuous_executions_per_thread)
+ return 0;
+ size_t count = 0;
+ for (const auto &kv : *m_continuous_executions_per_thread)
+ count += kv.second.size();
+ return count;
+}
+
+size_t
+TraceIntelPTMultiCpuDecoder::GePSBBlocksCountForThread(lldb::tid_t tid) const {
+ if (!m_continuous_executions_per_thread)
+ return 0;
+ size_t count = 0;
+ auto it = m_continuous_executions_per_thread->find(tid);
+ if (it == m_continuous_executions_per_thread->end())
+ return 0;
+ for (const IntelPTThreadContinousExecution &execution : it->second)
+ count += execution.psb_blocks.size();
+ return count;
+}
+
+size_t TraceIntelPTMultiCpuDecoder::GetUnattributedPSBBlocksCount() const {
+ return m_unattributed_psb_blocks;
+}
+
+size_t TraceIntelPTMultiCpuDecoder::GetTotalPSBBlocksCount() const {
+ return m_total_psb_blocks;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCpuDecoder.h b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCpuDecoder.h
new file mode 100644
index 000000000000..1633cd1e8088
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCpuDecoder.h
@@ -0,0 +1,110 @@
+//===-- TraceIntelPTMultiCpuDecoder.h ---------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTMULTICPUDECODER_H
+#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTMULTICPUDECODER_H
+
+#include "LibiptDecoder.h"
+#include "PerfContextSwitchDecoder.h"
+#include "ThreadDecoder.h"
+#include "forward-declarations.h"
+#include <optional>
+
+namespace lldb_private {
+namespace trace_intel_pt {
+
+/// Class used to decode a multi-cpu Intel PT trace. It assumes that each
+/// thread could have potentially been executed on different cpu cores. It uses
+/// a context switch trace per CPU with timestamps to identify which thread owns
+/// each Intel PT decoded instruction and in which order. It also assumes that
+/// the Intel PT data and context switches might have gaps in their traces due
+/// to contention or race conditions. Finally, it assumes that a tid is not
+/// repeated twice for two different threads because of the shortness of the
+/// intel pt trace.
+///
+/// This object should be recreated after every stop in the case of live
+/// processes.
+class TraceIntelPTMultiCpuDecoder {
+public:
+ /// \param[in] TraceIntelPT
+ /// The trace object to be decoded
+ TraceIntelPTMultiCpuDecoder(TraceIntelPTSP trace_sp);
+
+ /// \return
+ /// A \a DecodedThread for the \p thread by decoding its instructions on all
+ /// CPUs, sorted by TSCs. An \a llvm::Error is returned if the decoder
+ /// couldn't be properly set up.
+ llvm::Expected<DecodedThreadSP> Decode(Thread &thread);
+
+ /// \return
+ /// \b true if the given \p tid is managed by this decoder, regardless of
+ /// whether there's tracing data associated to it or not.
+ bool TracesThread(lldb::tid_t tid) const;
+
+ /// \return
+ /// The number of continuous executions found for the given \p tid.
+ size_t GetNumContinuousExecutionsForThread(lldb::tid_t tid) const;
+
+ /// \return
+ /// The number of PSB blocks for a given thread in all cores.
+ size_t GePSBBlocksCountForThread(lldb::tid_t tid) const;
+
+ /// \return
+ /// The total number of continuous executions found across CPUs.
+ size_t GetTotalContinuousExecutionsCount() const;
+
+ /// \return
+ /// The number of psb blocks in all cores that couldn't be matched with a
+ /// thread execution coming from context switch traces.
+ size_t GetUnattributedPSBBlocksCount() const;
+
+ /// \return
+ /// The total number of PSB blocks in all cores.
+ size_t GetTotalPSBBlocksCount() const;
+
+ /// \return
+ /// The lowest TSC value in this trace if available, \a std::nullopt if
+ /// the trace is empty or the trace contains no timing information, or an
+ /// \a llvm::Error if it was not possible to set up the decoder.
+ llvm::Expected<std::optional<uint64_t>> FindLowestTSC();
+
+private:
+ /// Traverse the context switch traces and the basic intel pt continuous
+ /// subtraces and produce a list of continuous executions for each process and
+ /// thread.
+ ///
+ /// See \a DoCorrelateContextSwitchesAndIntelPtTraces.
+ ///
+ /// Any errors are stored in \a m_setup_error.
+ llvm::Error CorrelateContextSwitchesAndIntelPtTraces();
+
+ /// Produce a mapping from thread ids to the list of continuos executions with
+ /// their associated intel pt subtraces.
+ llvm::Expected<
+ llvm::DenseMap<lldb::tid_t, std::vector<IntelPTThreadContinousExecution>>>
+ DoCorrelateContextSwitchesAndIntelPtTraces();
+
+ TraceIntelPTSP GetTrace();
+
+ std::weak_ptr<TraceIntelPT> m_trace_wp;
+ std::set<lldb::tid_t> m_tids;
+ std::optional<
+ llvm::DenseMap<lldb::tid_t, std::vector<IntelPTThreadContinousExecution>>>
+ m_continuous_executions_per_thread;
+ llvm::DenseMap<lldb::tid_t, DecodedThreadSP> m_decoded_threads;
+ /// This variable will not be std::nullopt if a severe error happened during
+ /// the setup of the decoder and we don't want decoding to be reattempted.
+ std::optional<std::string> m_setup_error;
+ uint64_t m_unattributed_psb_blocks = 0;
+ uint64_t m_total_psb_blocks = 0;
+};
+
+} // namespace trace_intel_pt
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTMULTICPUDECODER_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td
new file mode 100644
index 000000000000..4fb79448a3a0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td
@@ -0,0 +1,132 @@
+include "../../../../source/Commands/OptionsBase.td"
+
+// The information of the start commands here should match the description of
+// the intel-pt section of the jLLDBTraceStart packet in the
+// lldb/docs/lldb-gdb-remote.txt documentation file. Similarly, it should match
+// the API help message of TraceIntelPT::GetStartConfigurationHelp().
+
+let Command = "thread trace start intel pt" in {
+ def thread_trace_start_intel_pt_size
+ : Option<"size", "s">,
+ Group<1>,
+ Arg<"Value">,
+ Desc<
+ "Trace size in bytes per thread. It must be a power of 2 greater "
+ "than or equal to 4096 (2^12). The trace is circular keeping "
+ "the most recent data. Defaults to 4096 bytes. It's possible to "
+ "specify size using multiples of unit bytes, e.g., 4KB, 1MB, 1MiB, "
+ "where 1K is 1024 bytes and 1M is 1048576 bytes.">;
+ def thread_trace_start_intel_pt_tsc
+ : Option<"tsc", "t">,
+ Group<1>,
+ Desc<"Enable the use of TSC timestamps. This is supported on all "
+ "devices "
+ "that support intel-pt.">;
+ def thread_trace_start_intel_pt_psb_period
+ : Option<"psb-period", "p">,
+ Group<1>,
+ Arg<"Value">,
+ Desc<"This value defines the period in which PSB packets will be "
+ "generated. A PSB packet is a synchronization packet that "
+ "contains a "
+ "TSC timestamp and the current absolute instruction pointer. "
+ "This parameter can only be used if "
+ "/sys/bus/event_source/devices/intel_pt/caps/psb_cyc is 1. "
+ "Otherwise, "
+ "the PSB period will be defined by the processor. If supported, "
+ "valid "
+ "values for this period can be found in "
+ "/sys/bus/event_source/devices/intel_pt/caps/psb_periods which "
+ "contains a hexadecimal number, whose bits represent valid values "
+ "e.g. if bit 2 is set, then value 2 is valid. The psb_period "
+ "value is "
+ "converted to the approximate number of raw trace bytes between "
+ "PSB "
+ "packets as: 2 ^ (value + 11), e.g. value 3 means 16KiB between "
+ "PSB "
+ "packets. Defaults to 0 if supported.">;
+}
+
+let Command = "process trace start intel pt" in {
+ def process_trace_start_intel_pt_buffer_size
+ : Option<"buffer-size", "s">,
+ Group<1>,
+ Arg<"Value">,
+ Desc<
+ "Size in bytes used by each individual per-thread or per-cpu trace "
+ "buffer. It must be a power of 2 greater than or equal to 4096 "
+ "(2^12) "
+ "bytes. It's possible to specify a unit for these bytes, like 4KB, "
+ "16KiB or 1MB. Lower case units are allowed for convenience.">;
+ def process_trace_start_intel_pt_per_cpu_tracing
+ : Option<"per-cpu-tracing", "c">,
+ Group<1>,
+ Desc<
+ "Instead of having an individual trace buffer per thread, which "
+ "uses "
+ "a number trace buffers proportional to the number of running "
+ "threads, this option triggers the collection on a per cpu core "
+ "basis. This effectively traces the entire activity on all cpus "
+ "using a limited amount of trace buffers regardless of the number "
+ "of "
+ "threads. This might cause data loss for less frequent threads. "
+ "This "
+ "option forces the capture of TSC timestamps (see --tsc). Also, "
+ "this "
+ "option can't be used simulatenously with any other trace sessions "
+ "because of its system-wide nature.">;
+ def process_trace_start_intel_pt_process_size_limit
+ : Option<"total-size-limit", "l">,
+ Group<1>,
+ Arg<"Value">,
+ Desc<
+ "Maximum total trace size per process in bytes. This limit applies "
+ "to "
+ "the sum of the sizes of all thread and cpu traces of this "
+ "process, "
+ "excluding the ones created with the \"thread trace start\" "
+ "command. "
+ "Whenever a thread is attempted to be traced due to this command "
+ "and "
+ "the limit would be reached, the process is stopped with a "
+ "\"processor trace\" reason, so that the user can retrace the "
+ "process "
+ "if needed. Defaults to 500MB. It's possible to specify a unit for "
+ "these bytes, like 4KB, 16KiB or 1MB. Lower case units are allowed "
+ "for convenience.">;
+ def process_trace_start_intel_pt_tsc
+ : Option<"tsc", "t">,
+ Group<1>,
+ Desc<"Enable the use of TSC timestamps. This is supported on all "
+ "devices "
+ "that support intel-pt.">;
+ def process_trace_start_intel_pt_psb_period
+ : Option<"psb-period", "p">,
+ Group<1>,
+ Arg<"Value">,
+ Desc<"This value defines the period in which PSB packets will be "
+ "generated. A PSB packet is a synchronization packet that "
+ "contains a "
+ "TSC timestamp and the current absolute instruction pointer. "
+ "This parameter can only be used if "
+ "/sys/bus/event_source/devices/intel_pt/caps/psb_cyc is 1. "
+ "Otherwise, "
+ "the PSB period will be defined by the processor. If supported, "
+ "valid "
+ "values for this period can be found in "
+ "/sys/bus/event_source/devices/intel_pt/caps/psb_periods which "
+ "contains a hexadecimal number, whose bits represent valid values "
+ "e.g. if bit 2 is set, then value 2 is valid. The psb_period "
+ "value is "
+ "converted to the approximate number of raw trace bytes between "
+ "PSB "
+ "packets as: 2 ^ (value + 11), e.g. value 3 means 16KiB between "
+ "PSB "
+ "packets. Defaults to 0 if supported.">;
+ def process_trace_start_intel_pt_disable_cgroup_filtering
+ : Option<"disable-cgroup-filtering", "d">,
+ Desc<"Disable the automatic cgroup filtering that is applied if "
+ "--per-cpu "
+ "is provided. Cgroup filtering allows collecting intel pt data "
+ "exclusively of processes of the same cgroup as the target.">;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTProperties.td b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTProperties.td
new file mode 100644
index 000000000000..d338df1df5cb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTProperties.td
@@ -0,0 +1,24 @@
+include "../../../../include/lldb/Core/PropertiesBase.td"
+
+let Definition = "traceintelpt" in {
+ def InfiniteDecodingLoopVerificationThreshold:
+ Property<"infinite-decoding-loop-verification-threshold", "UInt64">,
+ Global,
+ DefaultUnsignedValue<10000>,
+ Desc<"Specify how many instructions following an individual Intel PT "
+ "packet must have been decoded before triggering the verification of "
+ "infinite decoding loops. If no decoding loop has been found after this "
+ "threshold T, another attempt will be done after 2T instructions, then "
+ "4T, 8T and so on, which guarantees a total linear time spent checking "
+ "this anomaly. If a loop is found, then decoding of the corresponding "
+ "PSB block is stopped. An error is hence emitted in the trace and "
+ "decoding is resumed in the next PSB block.">;
+ def ExtremelyLargeDecodingThreshold:
+ Property<"extremely-large-decoding-threshold", "UInt64">,
+ Global,
+ DefaultUnsignedValue<500000>,
+ Desc<"Specify how many instructions following an individual Intel PT "
+ "packet must have been decoded before stopping the decoding of the "
+ "corresponding PSB block. An error is hence emitted in the trace and "
+ "decoding is resumed in the next PSB block.">;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/forward-declarations.h b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/forward-declarations.h
new file mode 100644
index 000000000000..a05bf224bf07
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/forward-declarations.h
@@ -0,0 +1,24 @@
+//===-- forward-declarations.h ----------------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_FORWARD_DECLARATIONS_H
+#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_FORWARD_DECLARATIONS_H
+
+#include <memory>
+
+namespace lldb_private {
+namespace trace_intel_pt {
+
+class TraceIntelPT;
+class ThreadDecoder;
+
+using TraceIntelPTSP = std::shared_ptr<TraceIntelPT>;
+
+} // namespace trace_intel_pt
+} // namespace lldb_private
+#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_FORWARD_DECLARATIONS_H