[Lldb-commits] [lldb] [lldb][trace] Add an Arm ETM plugin (PR #185691)

via lldb-commits lldb-commits at lists.llvm.org
Tue Mar 10 10:12:50 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lldb

Author: Tom Hewitt (tom-hewitt)

<details>
<summary>Changes</summary>

This is the first in a series of patches to add Arm Coresight ETM support to the LLDB Trace plugin to allow process tracing on Arm.

This patch is mostly concerned with setting up the infrastructure required for the actual implementation. It adds a new "TraceArmETM" plugin mirroring the existing "TraceIntelPT" plugin. Rather than libipt, the plugin links the OpenCSD library (https://github.com/Linaro/OpenCSD) developed by Linaro, TI & Arm, and used by linux perf.

The plugin defines an "arm-etm" variation of the JSON trace bundle format that will be expanded upon in future patches to include CPU-specific information required to decode the trace. Some functionality not specific to any particular trace format has been factored out to share it between the plugins, such as module parsing and debugger target/process/module creation.

With this patch it is currently possible to print the JSON schema using "trace schema arm-etm", and load a trace using "trace load" (although this doesn't do anything with the actual trace yet, it justs parses the bundle and creates the target, process, threads and modules). Tests for these commands have been added.

Various methods are left unimplemented for now and will be implemented in the following patches.

---

Patch is 52.85 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/185691.diff


29 Files Affected:

- (modified) lldb/packages/Python/lldbsuite/test/dotest.py (+4) 
- (added) lldb/packages/Python/lldbsuite/test/tools/armetm/armetm_testcase.py (+58) 
- (modified) lldb/source/Plugins/Trace/CMakeLists.txt (+5) 
- (added) lldb/source/Plugins/Trace/arm-etm/CMakeLists.txt (+27) 
- (added) lldb/source/Plugins/Trace/arm-etm/TraceArmETM.cpp (+110) 
- (added) lldb/source/Plugins/Trace/arm-etm/TraceArmETM.h (+128) 
- (added) lldb/source/Plugins/Trace/arm-etm/TraceArmETMBundleLoader.cpp (+190) 
- (added) lldb/source/Plugins/Trace/arm-etm/TraceArmETMBundleLoader.h (+98) 
- (added) lldb/source/Plugins/Trace/arm-etm/TraceArmETMJSONStructs.cpp (+64) 
- (added) lldb/source/Plugins/Trace/arm-etm/TraceArmETMJSONStructs.h (+56) 
- (added) lldb/source/Plugins/Trace/arm-etm/forward-declarations.h (+23) 
- (modified) lldb/source/Plugins/Trace/common/CMakeLists.txt (+2) 
- (added) lldb/source/Plugins/Trace/common/TraceBundleLoader.cpp (+86) 
- (added) lldb/source/Plugins/Trace/common/TraceBundleLoader.h (+52) 
- (added) lldb/source/Plugins/Trace/common/TraceJSONStructs.cpp (+38) 
- (added) lldb/source/Plugins/Trace/common/TraceJSONStructs.h (+33) 
- (modified) lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.cpp (-66) 
- (modified) lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.h (+4-22) 
- (modified) lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp (-19) 
- (modified) lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h (+1-13) 
- (added) lldb/test/API/commands/trace/arm-etm/TestArmETMTraceLoad.py (+26) 
- (added) lldb/test/API/commands/trace/arm-etm/TestArmETMTraceSchema.py (+26) 
- (added) lldb/test/API/commands/trace/arm-etm/trace/0.trace () 
- (added) lldb/test/API/commands/trace/arm-etm/trace/picow_wifi_scan.elf () 
- (added) lldb/test/API/commands/trace/arm-etm/trace/trace.json (+22) 
- (modified) lldb/test/API/lit.site.cfg.py.in (+3) 
- (modified) lldb/test/CMakeLists.txt (+1) 
- (modified) lldb/utils/lldb-dotest/CMakeLists.txt (+1) 
- (modified) lldb/utils/lldb-dotest/lldb-dotest.in (+3) 


``````````diff
diff --git a/lldb/packages/Python/lldbsuite/test/dotest.py b/lldb/packages/Python/lldbsuite/test/dotest.py
index 533be0a065e3a..1ac63c5acd3e0 100644
--- a/lldb/packages/Python/lldbsuite/test/dotest.py
+++ b/lldb/packages/Python/lldbsuite/test/dotest.py
@@ -519,6 +519,7 @@ def setupSysPath():
     toolsLLDBDAP = os.path.join(scriptPath, "tools", "lldb-dap")
     toolsLLDBServerPath = os.path.join(scriptPath, "tools", "lldb-server")
     intelpt = os.path.join(scriptPath, "tools", "intelpt")
+    armetm = os.path.join(scriptPath, "tools", "armetm")
 
     # Insert script dir, plugin dir and lldb-server dir to the sys.path.
     sys.path.insert(0, pluginPath)
@@ -531,6 +532,9 @@ def setupSysPath():
     # Adding test/tools/intelpt to the path makes it easy
     # to "import intelpt_testcase" from the lldb-server tests
     sys.path.insert(0, intelpt)
+    # Adding test/tools/armetm to the path makes it easy
+    # to "import armetm_testcase" from the lldb-server tests
+    sys.path.insert(0, armetm)
 
     # This is the root of the lldb git/svn checkout
     # When this changes over to a package instead of a standalone script, this
diff --git a/lldb/packages/Python/lldbsuite/test/tools/armetm/armetm_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/armetm/armetm_testcase.py
new file mode 100644
index 0000000000000..f186eb79ec25f
--- /dev/null
+++ b/lldb/packages/Python/lldbsuite/test/tools/armetm/armetm_testcase.py
@@ -0,0 +1,58 @@
+from lldbsuite.test.lldbtest import *
+import os
+import time
+import json
+
+ADDRESS_REGEX = "0x[0-9a-fA-F]*"
+
+
+# Decorator that runs a test with both modes of USE_SB_API.
+# It assumes that no tests can be executed in parallel.
+def testSBAPIAndCommands(func):
+    def wrapper(*args, **kwargs):
+        TraceArmETMTestCaseBase.USE_SB_API = True
+        func(*args, **kwargs)
+        TraceArmETMTestCaseBase.USE_SB_API = False
+        func(*args, **kwargs)
+
+    return wrapper
+
+
+# Class that should be used by all python Arm ETM tests.
+#
+# It has a handy check that skips the test if the arm-etm plugin is not enabled.
+#
+# It also contains many functions that can test both the SB API or the command line version
+# of the most important tracing actions.
+class TraceArmETMTestCaseBase(TestBase):
+    NO_DEBUG_INFO_TESTCASE = True
+
+    # If True, the trace test methods will use the SB API, otherwise they'll use raw commands.
+    USE_SB_API = False
+
+    def setUp(self):
+        TestBase.setUp(self)
+        if "arm-etm" not in configuration.enabled_plugins:
+            self.skipTest("The arm-etm test plugin is not enabled")
+
+    def getTraceOrCreate(self):
+        if not self.target().GetTrace().IsValid():
+            error = lldb.SBError()
+            self.target().CreateTrace(error)
+        return self.target().GetTrace()
+
+    def assertSBError(self, sberror, error=False):
+        if error:
+            self.assertTrue(sberror.Fail())
+        else:
+            self.assertSuccess(sberror)
+
+    def traceLoad(self, traceDescriptionFilePath, error=False, substrs=None):
+        if self.USE_SB_API:
+            traceDescriptionFile = lldb.SBFileSpec(traceDescriptionFilePath, True)
+            loadTraceError = lldb.SBError()
+            self.dbg.LoadTraceFromFile(loadTraceError, traceDescriptionFile)
+            self.assertSBError(loadTraceError, error)
+        else:
+            command = f"trace load -v {traceDescriptionFilePath}"
+            self.expect(command, error=error, substrs=substrs)
diff --git a/lldb/source/Plugins/Trace/CMakeLists.txt b/lldb/source/Plugins/Trace/CMakeLists.txt
index 331b48f95f1a4..7d3847f57b2e7 100644
--- a/lldb/source/Plugins/Trace/CMakeLists.txt
+++ b/lldb/source/Plugins/Trace/CMakeLists.txt
@@ -1,9 +1,14 @@
 set_property(DIRECTORY PROPERTY LLDB_PLUGIN_KIND Trace)
 
 option(LLDB_BUILD_INTEL_PT "Enable Building of Intel(R) Processor Trace Tool" OFF)
+option(LLDB_BUILD_ARM_ETM "Enable Building of ARM Embedded Trace Macrocell Tool" OFF)
 
 add_subdirectory(common)
 
 if (LLDB_BUILD_INTEL_PT)
   add_subdirectory(intel-pt)
 endif()
+
+if (LLDB_BUILD_ARM_ETM)
+  add_subdirectory(arm-etm)
+endif()
diff --git a/lldb/source/Plugins/Trace/arm-etm/CMakeLists.txt b/lldb/source/Plugins/Trace/arm-etm/CMakeLists.txt
new file mode 100644
index 0000000000000..7b47d42dfa6c2
--- /dev/null
+++ b/lldb/source/Plugins/Trace/arm-etm/CMakeLists.txt
@@ -0,0 +1,27 @@
+if (NOT OPENCSD_INCLUDE_PATH)
+  message (FATAL_ERROR "OpenCSD include path not provided")
+endif()
+
+if (NOT EXISTS "${OPENCSD_INCLUDE_PATH}")
+  message (FATAL_ERROR "invalid OpenCSD include path provided")
+endif()
+include_directories(${OPENCSD_INCLUDE_PATH})
+
+message(STATUS "Using OpenCSD include path: ${OPENCSD_INCLUDE_PATH}")
+
+find_library(OPENCSD_LIBRARY opencsd PATHS ${OPENCSD_LIBRARY_PATH} REQUIRED)
+
+add_lldb_library(lldbPluginTraceArmETM PLUGIN
+  TraceArmETM.cpp
+  TraceArmETMJSONStructs.cpp
+  TraceArmETMBundleLoader.cpp
+
+  LINK_COMPONENTS
+    Support
+  LINK_LIBS
+    lldbCore
+    lldbSymbol
+    lldbTarget
+    lldbPluginTraceCommon
+    ${OPENCSD_LIBRARY}
+  )
diff --git a/lldb/source/Plugins/Trace/arm-etm/TraceArmETM.cpp b/lldb/source/Plugins/Trace/arm-etm/TraceArmETM.cpp
new file mode 100644
index 0000000000000..8de553020d4c7
--- /dev/null
+++ b/lldb/source/Plugins/Trace/arm-etm/TraceArmETM.cpp
@@ -0,0 +1,110 @@
+//===-- TraceArmETM.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 "TraceArmETM.h"
+
+#include "TraceArmETMBundleLoader.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Target/Process.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::trace_arm_etm;
+using namespace llvm;
+
+LLDB_PLUGIN_DEFINE(TraceArmETM)
+
+lldb::CommandObjectSP
+TraceArmETM::GetProcessTraceStartCommand(CommandInterpreter &interpreter) {
+  llvm_unreachable("Unimplemented");
+}
+
+lldb::CommandObjectSP
+TraceArmETM::GetThreadTraceStartCommand(CommandInterpreter &interpreter) {
+  llvm_unreachable("Unimplemented");
+}
+
+void TraceArmETM::Initialize() {
+  PluginManager::RegisterPlugin(
+      GetPluginNameStatic(), "Arm ETM", CreateInstanceForTraceBundle,
+      CreateInstanceForLiveProcess, TraceArmETMBundleLoader::GetSchema(),
+      DebuggerInitialize);
+}
+
+void TraceArmETM::DebuggerInitialize(Debugger &debugger) {}
+
+void TraceArmETM::Terminate() {
+  PluginManager::UnregisterPlugin(CreateInstanceForTraceBundle);
+}
+
+StringRef TraceArmETM::GetSchema() {
+  return TraceArmETMBundleLoader::GetSchema();
+}
+
+void TraceArmETM::Dump(Stream *s) const {}
+
+Expected<FileSpec> TraceArmETM::SaveToDisk(FileSpec directory, bool compact) {
+  llvm_unreachable("Unimplemented");
+}
+
+Expected<TraceSP>
+TraceArmETM::CreateInstanceForTraceBundle(const json::Value &bundle_description,
+                                          StringRef bundle_dir,
+                                          Debugger &debugger) {
+  return TraceArmETMBundleLoader(debugger, bundle_description, bundle_dir)
+      .Load();
+}
+
+Expected<TraceSP> TraceArmETM::CreateInstanceForLiveProcess(Process &process) {
+  TraceSP instance(new TraceArmETM(process));
+  process.GetTarget().SetTrace(instance);
+  return instance;
+}
+
+TraceArmETMSP TraceArmETM::CreateInstanceForPostmortemTrace(
+    JSONTraceBundleDescription &bundle_description,
+    ArrayRef<ProcessSP> traced_processes,
+    ArrayRef<ThreadPostMortemTraceSP> traced_threads) {
+  TraceArmETMSP trace_sp(new TraceArmETM(bundle_description, traced_processes));
+
+  for (const ProcessSP &process_sp : traced_processes)
+    process_sp->GetTarget().SetTrace(trace_sp);
+  return trace_sp;
+}
+
+TraceArmETM::TraceArmETM(JSONTraceBundleDescription &bundle_description,
+                         llvm::ArrayRef<lldb::ProcessSP> traced_processes)
+    : Trace(traced_processes, std::nullopt) {}
+
+llvm::Expected<lldb::TraceCursorSP>
+TraceArmETM::CreateNewCursor(Thread &thread) {
+  llvm_unreachable("Unimplemented");
+}
+
+void TraceArmETM::DumpTraceInfo(Thread &thread, Stream &s, bool verbose,
+                                bool json) {}
+
+Error TraceArmETM::DoRefreshLiveProcessState(TraceGetStateResponse state,
+                                             StringRef json_response) {
+  llvm_unreachable("Unimplemented");
+}
+
+bool TraceArmETM::IsTraced(lldb::tid_t tid) { return false; }
+
+const char *TraceArmETM::GetStartConfigurationHelp() {
+  llvm_unreachable("Unimplemented");
+}
+
+Error TraceArmETM::Start(StructuredData::ObjectSP configuration) {
+  llvm_unreachable("Unimplemented");
+}
+
+Error TraceArmETM::Start(llvm::ArrayRef<lldb::tid_t> tids,
+                         StructuredData::ObjectSP configuration) {
+  llvm_unreachable("Unimplemented");
+}
diff --git a/lldb/source/Plugins/Trace/arm-etm/TraceArmETM.h b/lldb/source/Plugins/Trace/arm-etm/TraceArmETM.h
new file mode 100644
index 0000000000000..a6d2749615046
--- /dev/null
+++ b/lldb/source/Plugins/Trace/arm-etm/TraceArmETM.h
@@ -0,0 +1,128 @@
+//===-- TraceArmETM.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_ARM_ETM_TRACEARMETM_H
+#define LLDB_SOURCE_PLUGINS_TRACE_ARM_ETM_TRACEARMETM_H
+
+#include "TraceArmETMBundleLoader.h"
+#include "forward-declarations.h"
+#include "lldb/Target/Trace.h"
+
+namespace lldb_private {
+namespace trace_arm_etm {
+
+class TraceArmETM : public Trace {
+public:
+  void Dump(lldb_private::Stream *s) const override;
+
+  llvm::Expected<FileSpec> SaveToDisk(FileSpec directory,
+                                      bool compact) override;
+
+  /// 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 "arm-etm"; }
+
+  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::Error DoRefreshLiveProcessState(TraceGetStateResponse state,
+                                        llvm::StringRef json_response) override;
+
+  bool IsTraced(lldb::tid_t tid) override;
+
+  const char *GetStartConfigurationHelp() override;
+
+  /// \copydoc Trace::Start
+  llvm::Error Start(StructuredData::ObjectSP configuration =
+                        StructuredData::ObjectSP()) override;
+
+  /// \copydoc Trace::Start
+  llvm::Error Start(llvm::ArrayRef<lldb::tid_t> tids,
+                    StructuredData::ObjectSP configuration =
+                        StructuredData::ObjectSP()) override;
+
+private:
+  friend class TraceArmETMBundleLoader;
+
+  /// 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 TraceArmETM shared pointer instance.
+  /// \{
+  static TraceArmETMSP CreateInstanceForPostmortemTrace(
+      JSONTraceBundleDescription &bundle_description,
+      llvm::ArrayRef<lldb::ProcessSP> traced_processes,
+      llvm::ArrayRef<lldb::ThreadPostMortemTraceSP> traced_threads);
+
+  /// This constructor is used by CreateInstanceForPostmortemTrace to get the
+  /// instance ready before using shared pointers, which is a limitation of C++.
+  TraceArmETM(JSONTraceBundleDescription &bundle_description,
+              llvm::ArrayRef<lldb::ProcessSP> traced_processes);
+  /// \}
+
+  /// Constructor for live processes
+  TraceArmETM(Process &live_process) : Trace(live_process) {};
+};
+
+} // namespace trace_arm_etm
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_TRACE_ARM_ETM_TRACEARMETM_H
diff --git a/lldb/source/Plugins/Trace/arm-etm/TraceArmETMBundleLoader.cpp b/lldb/source/Plugins/Trace/arm-etm/TraceArmETMBundleLoader.cpp
new file mode 100644
index 0000000000000..72b8e77f71aaf
--- /dev/null
+++ b/lldb/source/Plugins/Trace/arm-etm/TraceArmETMBundleLoader.cpp
@@ -0,0 +1,190 @@
+//===-- TraceArmETMBundleLoader.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 "TraceArmETMBundleLoader.h"
+
+#include "TraceArmETM.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Target/ProcessTrace.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::trace_arm_etm;
+using namespace llvm;
+
+Error TraceArmETMBundleLoader::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(), err.c_str(), GetSchema().data());
+}
+
+ThreadPostMortemTraceSP
+TraceArmETMBundleLoader::ParseThread(Process &process,
+                                     const JSONThread &thread) {
+  lldb::tid_t tid = static_cast<lldb::tid_t>(thread.tid);
+
+  std::optional<FileSpec> trace_file;
+  if (thread.etm_trace)
+    trace_file = FileSpec(*thread.etm_trace);
+
+  ThreadPostMortemTraceSP thread_sp =
+      std::make_shared<ThreadPostMortemTrace>(process, tid, trace_file);
+  process.GetThreadList().AddThread(thread_sp);
+  return thread_sp;
+}
+
+Expected<TraceArmETMBundleLoader::ParsedProcess>
+TraceArmETMBundleLoader::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<std::vector<TraceArmETMBundleLoader::ParsedProcess>>
+TraceArmETMBundleLoader::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());
+    }
+  }
+
+  return parsed_processes;
+}
+
+StringRef TraceArmETMBundleLoader::GetSchema() {
+  static std::string schema;
+  if (schema.empty()) {
+    schema = R"({
+  "type": "arm-etm",
+  "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.
+        {
+          "tid": integer,
+          "etmTrace"?: string
+              // Path to the raw ARM ETM 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.
+        }
+      ]
+    }
+  ]
+}
+
+Notes:
+
+- All paths are either absolute or relative to folder containing the bundle
+  description file.})";
+  }
+  return schema;
+}
+
+Expected<TraceSP> TraceArmETMBundleLoader::CreateTraceArmETMInstance(
+    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());
+  }
+
+  TraceSP trace_instance = TraceArmETM::CreateInstanceForPostmortemTrace(
+      bundle_description, processes, threads);
+  for (const ParsedProcess &parsed_process : parsed_processes)
+    parsed_process.target_sp->SetTrace(trace_instance);
+
+  return trace_instance;
+}
+
+void TraceArmETMBundleLoader::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.etm_trace)
+          thread.etm_trace = NormalizePath(*thread.etm_trace).GetPath();
+      }
+    }
+  }
+}
+
+Expected<TraceSP> TraceArmETMBundleLoader::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 (Expecte...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/185691


More information about the lldb-commits mailing list