hanbingwang created this revision.
hanbingwang added reviewers: wallace, clayborg.
Herald added subscribers: dang, mgorny.
hanbingwang requested review of this revision.
Herald added a project: LLDB.
Herald added a subscriber: lldb-commits.
added new command "process trace save -d <directory>".
-it only support Intel-pt right now.
-it saves a JSON file as <directory>/trace.json, with the main properties of
the trace session.
-it saves binary Intel-PT trace as <directory>/thread_id.trace; each file saves
each thread.
Example:
b main
run
process trace start
n
process trace save -d /tmp/mytrace
A file named trace.json and xxx.trace should be generated in /tmp/mytrace. To
load the trace that was just saved:
trace load /tmp/mytrace
thread trace dump instructions
You should see the instructions of the trace got printed.
To run a test:
./bin/lldb-dotest -p TestTraceSave
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D107669
Files:
lldb/include/lldb/Target/Trace.h
lldb/source/Commands/CommandObjectProcess.cpp
lldb/source/Commands/Options.td
lldb/source/Plugins/Trace/common/CMakeLists.txt
lldb/source/Plugins/Trace/common/TraceJSONStructs.cpp
lldb/source/Plugins/Trace/common/TraceJSONStructs.h
lldb/source/Plugins/Trace/common/TraceSessionFileParser.cpp
lldb/source/Plugins/Trace/common/TraceSessionFileParser.h
lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt
lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h
lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp
lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h
lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td
lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileSaver.cpp
lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileSaver.h
lldb/test/API/commands/trace/TestTraceSave.py
Index: lldb/test/API/commands/trace/TestTraceSave.py
===================================================================
--- /dev/null
+++ lldb/test/API/commands/trace/TestTraceSave.py
@@ -0,0 +1,70 @@
+import lldb
+from intelpt_testcase import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+from lldbsuite.test.decorators import *
+
+class TestTraceSave(TraceIntelPTTestCaseBase):
+ mydir = TestBase.compute_mydir(__file__)
+
+ def testErrorMessages(self):
+ # We first check the output when there are no targets
+ self.expect("process trace save",
+ substrs=["error: invalid target, create a target using the 'target create' command"],
+ error=True)
+
+ # We now check the output when there's a non-running target
+ self.expect("target create " +
+ os.path.join(self.getSourceDir(), "intelpt-trace", "a.out"))
+
+ self.expect("process trace save",
+ substrs=["error: invalid process"],
+ error=True)
+
+ # Now we check the output when there's a running target without a trace
+ self.expect("b main")
+ self.expect("run")
+
+ self.expect("process trace save",
+ substrs=["error: Process is not being traced"],
+ error=True)
+
+ def testSaveTrace(self):
+ # Load a trace
+ self.expect("trace load -v " +
+ os.path.join(self.getSourceDir(), "intelpt-trace", "trace.json"),
+ substrs=["intel-pt"])
+ # Save the trace to <trace_copy_dir>
+ self.expect("process trace save -d " +
+ os.path.join(self.getSourceDir(), "intelpt-trace", "trace_copy_dir"))
+ # Load the trace just saved
+ self.expect("trace load -v " +
+ os.path.join(self.getSourceDir(), "intelpt-trace", "trace_copy_dir", "trace.json"),
+ substrs=["intel-pt"])
+ #dump instructions and check
+ self.expect("thread trace dump instructions --raw --count 21 --forwards",
+ substrs=['''thread #1: tid = 3842849
+ [ 0] 0x0000000000400511
+ [ 1] 0x0000000000400518
+ [ 2] 0x000000000040051f
+ [ 3] 0x0000000000400529
+ [ 4] 0x000000000040052d
+ [ 5] 0x0000000000400521
+ [ 6] 0x0000000000400525
+ [ 7] 0x0000000000400529
+ [ 8] 0x000000000040052d
+ [ 9] 0x0000000000400521
+ [10] 0x0000000000400525
+ [11] 0x0000000000400529
+ [12] 0x000000000040052d
+ [13] 0x0000000000400521
+ [14] 0x0000000000400525
+ [15] 0x0000000000400529
+ [16] 0x000000000040052d
+ [17] 0x0000000000400521
+ [18] 0x0000000000400525
+ [19] 0x0000000000400529
+ [20] 0x000000000040052d'''])
+
+ #remove <trace_copy_dir>
+ shutil.rmtree(os.path.join(self.getSourceDir(), "intelpt-trace", "trace_copy_dir"))
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileSaver.h
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileSaver.h
@@ -0,0 +1,124 @@
+//===-- TraceIntelPTSessionFileSaver.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_TRACEINTELPTSESSIONFILESAVER_H
+#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTSESSIONFILESAVER_H
+
+#include "TraceIntelPT.h"
+
+#include "../common/TraceSessionFileParser.h"
+
+namespace lldb_private {
+namespace trace_intel_pt {
+
+class TraceIntelPT;
+
+class TraceIntelPTSessionFileSaver {
+
+public:
+ /// Save the trace of the given process to the specified directory,
+ /// which will be created if needed. This will also create a file
+ /// <directory>/trace.json with the main properties of the trace
+ /// session, 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] process_sp
+ /// The process whose trace will be saved to disk.
+ ///
+ /// \param[in] trace_ipt
+ /// The Intel PT trace to be saved to disk.
+ ///
+ /// \param[in] directory
+ /// The directory where the trace files will be saved.
+ ///
+ /// \return
+ /// \a llvm::success if the operation was successful, or an \a llvm::Error
+ /// otherwise.
+ ///
+ llvm::Error SaveToDisk(lldb::ProcessSP &process_sp, TraceIntelPT &trace_ipt,
+ FileSpec directory);
+
+private:
+ /// Save the intel-pt trace's schema with main properties of the trace
+ /// session to the specified directory as a JSON file. The filename is
+ /// \a <directory>/trace.json
+ ///
+ /// \param[in] json_trace_intel_pt_schema
+ /// schema that describes trace.
+ ///
+ /// \param[in] directory
+ /// The directory where the JSON file will be saved.
+ ///
+ void WriteIntelPTSchemaToFile(
+ const JSONTraceIntelPTSchema &json_trace_intel_pt_schema,
+ FileSpec directory);
+
+ /// Build trace section of the intel-pt trace's schema.
+ ///
+ /// \param[in] trace_ipt
+ /// The Intel PT trace.
+ ///
+ /// \return The trace section to be built.
+ ///
+ llvm::Expected<JSONTraceIntelPTTrace>
+ BuildTraceSection(TraceIntelPT &trace_ipt);
+
+ /// Build processes section of the intel-pt trace's schema.
+ ///
+ /// \param[in] process_sp
+ /// The process being traced.
+ ///
+ /// \param[in] trace_ipt
+ /// The Intel PT trace.
+ ///
+ /// \param[in] directory
+ /// The directory where the thread files will be saved when building
+ /// processes section.
+ ///
+ /// \return The processes section to be built.
+ ///
+ llvm::Expected<JSONTraceSessionBase>
+ BuildProcessesSection(lldb::ProcessSP &process_sp, TraceIntelPT &trace_ipt,
+ FileSpec directory);
+
+ /// Build threads sub-section inside processes section of the intel-pt trace's
+ /// schema. The raw trace for a thread is written to a file with filename
+ /// \a <directory>/thread_id.trace.
+ ///
+ /// \param[in] process_sp
+ /// The process being traced.
+ ///
+ /// \param[in] trace_ipt
+ /// The Intel PT trace.
+ ///
+ /// \param[in] directory
+ /// The directory where the thread files will be saved when building
+ /// the threads section.
+ ///
+ /// \return The threads section to be built.
+ ///
+ llvm::Expected<std::vector<JSONThread>>
+ BuildThreadsSection(lldb::ProcessSP &process_sp, TraceIntelPT &trace_ipt,
+ FileSpec directory);
+
+ /// Build modules sub-section inside processes section of the intel-pt trace's
+ /// schema. Invalid modules are skipped.
+ ///
+ /// \param[in] process_sp
+ /// The process being traced.
+ ///
+ /// \return The modules section to be built.
+ //
+ std::vector<JSONModule> BuildModulesSection(lldb::ProcessSP &process_sp);
+};
+
+} // namespace trace_intel_pt
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTSESSIONFILESAVER_H
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileSaver.cpp
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileSaver.cpp
@@ -0,0 +1,170 @@
+//===-- TraceIntelPTSessionFileSaver.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 "TraceIntelPTSessionFileSaver.h"
+#include "TraceIntelPT.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/Analysis/ModuleSummaryAnalysis.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/JSON.h"
+#include <fstream>
+#include <sstream>
+#include <string>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::trace_intel_pt;
+using namespace llvm;
+
+llvm::Error TraceIntelPTSessionFileSaver::SaveToDisk(
+ lldb::ProcessSP &process_sp, TraceIntelPT &trace_ipt, FileSpec directory) {
+ if (std::error_code ec =
+ sys::fs::create_directories(directory.GetPath().c_str())) {
+ return llvm::errorCodeToError(ec);
+ }
+
+ llvm::Expected<JSONTraceIntelPTTrace> json_trace_intel_pt_trace =
+ BuildTraceSection(trace_ipt);
+ if (!json_trace_intel_pt_trace) {
+ return json_trace_intel_pt_trace.takeError();
+ }
+
+ llvm::Expected<JSONTraceSessionBase> json_trace_session_base =
+ BuildProcessesSection(process_sp, trace_ipt, directory);
+ if (!json_trace_session_base) {
+ return json_trace_session_base.takeError();
+ }
+
+ struct JSONTraceIntelPTSchema json_trace_intel_pt_schema(
+ json_trace_intel_pt_trace.get(), json_trace_session_base.get());
+ WriteIntelPTSchemaToFile(json_trace_intel_pt_schema, directory);
+ return llvm::Error::success();
+}
+
+void TraceIntelPTSessionFileSaver::WriteIntelPTSchemaToFile(
+ const JSONTraceIntelPTSchema &json_trace_intel_pt_schema,
+ FileSpec directory) {
+ llvm::json::Value ipt_schema_jo =
+ llvm::json::toJSON(json_trace_intel_pt_schema);
+ std::string out = formatv("{0:2}", ipt_schema_jo);
+ FileSpec trace_path = directory;
+ trace_path.AppendPathComponent("trace.json");
+ std::ofstream os(trace_path.GetPath());
+ os << out;
+ return;
+}
+
+llvm::Expected<JSONTraceIntelPTTrace>
+TraceIntelPTSessionFileSaver::BuildTraceSection(TraceIntelPT &trace_ipt) {
+ llvm::Expected<pt_cpu> cpu_info = trace_ipt.GetCPUInfo();
+ if (!cpu_info) {
+ return cpu_info.takeError();
+ }
+
+ struct JSONTraceIntelPTCPUInfo json_trace_intel_pt_cpu_info(
+ static_cast<int64_t>(cpu_info->family),
+ static_cast<int64_t>(cpu_info->model),
+ static_cast<int64_t>(cpu_info->stepping),
+ cpu_info->vendor == pcv_intel ? "intel" : "Unknown");
+
+ struct JSONTraceIntelPTTrace result("intel-pt", json_trace_intel_pt_cpu_info);
+ return result;
+}
+
+llvm::Expected<JSONTraceSessionBase>
+TraceIntelPTSessionFileSaver::BuildProcessesSection(lldb::ProcessSP &process_sp,
+ TraceIntelPT &trace_ipt,
+ FileSpec directory) {
+ JSONTraceSessionBase result;
+ Expected<std::vector<JSONThread>> json_threads =
+ BuildThreadsSection(process_sp, trace_ipt, directory);
+ if (!json_threads) {
+ return json_threads.takeError();
+ }
+
+ std::vector<JSONModule> json_modules = BuildModulesSection(process_sp);
+
+ struct JSONProcess json_process(
+ process_sp->GetID(),
+ process_sp->GetTarget().GetArchitecture().GetTriple().getTriple(),
+ json_threads.get(), json_modules);
+ result.processes.push_back(json_process);
+
+ return result;
+}
+
+llvm::Expected<std::vector<JSONThread>>
+TraceIntelPTSessionFileSaver::BuildThreadsSection(lldb::ProcessSP &process_sp,
+ TraceIntelPT &trace_ipt,
+ FileSpec directory) {
+ std::vector<JSONThread> result;
+ for (ThreadSP thread_sp : process_sp->Threads()) {
+ JSONThread json_thread;
+ json_thread.tid = thread_sp->GetID();
+ // resolve the directory just in case
+ FileSystem::Instance().Resolve(directory);
+ FileSpec json_path = directory;
+ json_path.AppendPathComponent(std::to_string(thread_sp->GetID()) +
+ ".trace");
+ json_thread.trace_file = json_path.GetPath().c_str();
+ result.push_back(json_thread);
+
+ Expected<std::vector<uint8_t>> raw_trace =
+ trace_ipt.GetThreadBuffer(*thread_sp);
+ if (!raw_trace) {
+ return raw_trace.takeError();
+ }
+ std::basic_fstream<char> raw_trace_file =
+ std::fstream(json_thread.trace_file, std::ios::out | std::ios::binary);
+ raw_trace_file.write(reinterpret_cast<const char *>(&raw_trace->at(0)),
+ raw_trace->size() * sizeof(uint8_t));
+ }
+ return result;
+}
+
+std::vector<JSONModule>
+TraceIntelPTSessionFileSaver::BuildModulesSection(lldb::ProcessSP &process_sp) {
+ std::vector<JSONModule> result;
+ ModuleList module_list = process_sp->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_sp->GetTarget().GetSectionLoadList().IsEmpty()) {
+ load_addr = base_addr.GetLoadAddress(&process_sp->GetTarget());
+ }
+ if (load_addr == LLDB_INVALID_ADDRESS) {
+ continue;
+ }
+ JSONAddress load_address(load_addr);
+ JSONModule json_module(system_path, file, load_address,
+ module_sp->GetUUID().GetAsString());
+ result.push_back(json_module);
+ }
+ return result;
+}
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
@@ -9,9 +9,9 @@
#ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTSESSIONFILEPARSER_H
#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTSESSIONFILEPARSER_H
-#include "TraceIntelPT.h"
-
#include "../common/TraceSessionFileParser.h"
+#include "TraceIntelPT.h"
+#include "TraceIntelPTJSONStructs.h"
namespace lldb_private {
namespace trace_intel_pt {
@@ -20,17 +20,6 @@
class TraceIntelPTSessionFileParser : public TraceSessionFileParser {
public:
- struct JSONTraceIntelPTCPUInfo {
- int64_t family;
- int64_t model;
- int64_t stepping;
- std::string vendor;
- };
-
- struct JSONTraceIntelPTSettings
- : TraceSessionFileParser::JSONTracePluginSettings {
- JSONTraceIntelPTCPUInfo cpuInfo;
- };
/// See \a TraceSessionFileParser::TraceSessionFileParser for the description
/// of these fields.
@@ -65,24 +54,5 @@
} // namespace trace_intel_pt
} // namespace lldb_private
-namespace llvm {
-namespace json {
-
-bool fromJSON(const Value &value,
- lldb_private::trace_intel_pt::TraceIntelPTSessionFileParser::
- JSONTraceIntelPTSettings &plugin_settings,
- Path path);
-
-bool fromJSON(const llvm::json::Value &value,
- lldb_private::trace_intel_pt::TraceIntelPTSessionFileParser::
- JSONTraceIntelPTCPUInfo &packet,
- llvm::json::Path path);
-
-llvm::json::Value
-toJSON(const lldb_private::trace_intel_pt::TraceIntelPTSessionFileParser::
- JSONTraceIntelPTCPUInfo &packet);
-
-} // namespace json
-} // namespace llvm
#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTSESSIONFILEPARSER_H
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
@@ -9,10 +9,7 @@
#include "TraceIntelPTSessionFileParser.h"
#include "../common/ThreadPostMortemTrace.h"
-#include "lldb/Core/Debugger.h"
-#include "lldb/Target/Process.h"
-#include "lldb/Target/Target.h"
-#include "lldb/Target/ThreadList.h"
+#include "TraceIntelPT.h"
using namespace lldb;
using namespace lldb_private;
@@ -59,7 +56,7 @@
Expected<TraceSP> TraceIntelPTSessionFileParser::Parse() {
json::Path::Root root("traceSession");
- TraceSessionFileParser::JSONTraceSession<JSONTraceIntelPTSettings> session;
+ JSONTraceSession<JSONTraceIntelPTSettings> session;
if (!json::fromJSON(m_trace_session_file, session, root))
return CreateJSONError(root, m_trace_session_file);
@@ -70,38 +67,3 @@
else
return parsed_processes.takeError();
}
-
-namespace llvm {
-namespace json {
-
-bool fromJSON(
- const Value &value,
- TraceIntelPTSessionFileParser::JSONTraceIntelPTSettings &plugin_settings,
- Path path) {
- ObjectMapper o(value, path);
- return o && o.map("cpuInfo", plugin_settings.cpuInfo) &&
- fromJSON(
- value,
- (TraceSessionFileParser::JSONTracePluginSettings &)plugin_settings,
- path);
-}
-
-bool fromJSON(const json::Value &value,
- TraceIntelPTSessionFileParser::JSONTraceIntelPTCPUInfo &cpu_info,
- Path path) {
- ObjectMapper o(value, path);
- return o && o.map("vendor", cpu_info.vendor) &&
- o.map("family", cpu_info.family) && o.map("model", cpu_info.model) &&
- o.map("stepping", cpu_info.stepping);
-}
-
-Value toJSON(
- const TraceIntelPTSessionFileParser::JSONTraceIntelPTCPUInfo &cpu_info) {
- return Value(Object{{"family", cpu_info.family},
- {"model", cpu_info.model},
- {"stepping", cpu_info.stepping},
- {"vendor", cpu_info.vendor}});
-}
-
-} // namespace json
-} // namespace llvm
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td
@@ -72,3 +72,13 @@
"packets as: 2 ^ (value + 11), e.g. value 3 means 16KiB between PSB "
"packets. Defaults to 0 if supported.">;
}
+
+let Command = "process trace save intel pt" in {
+ def process_trace_save_intel_directory: Option<"directory", "d">,
+ Group<1>,
+ Arg<"Value">, Required,
+ Desc<"This value defines the directory where the trace will be saved."
+ "It will be created if it does not exist. It will also create a "
+ "trace files with the trace data and a trace.json with the main "
+ "properties of the trace session.">;
+}
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h
@@ -0,0 +1,82 @@
+//===-- 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 "../common/TraceJSONStructs.h"
+
+namespace lldb_private {
+namespace trace_intel_pt {
+
+struct JSONTraceIntelPTCPUInfo {
+ JSONTraceIntelPTCPUInfo() = default;
+
+ JSONTraceIntelPTCPUInfo(int64_t family, int64_t model, int64_t stepping,
+ std::string vendor)
+ : family(family), model(model), stepping(stepping), vendor(vendor) {}
+
+ int64_t family;
+ int64_t model;
+ int64_t stepping;
+ std::string vendor;
+};
+
+struct JSONTraceIntelPTTrace {
+ JSONTraceIntelPTTrace() = default;
+
+ JSONTraceIntelPTTrace(std::string type, JSONTraceIntelPTCPUInfo cpuInfo)
+ : type(type), cpuInfo(cpuInfo) {}
+
+ std::string type;
+ JSONTraceIntelPTCPUInfo cpuInfo;
+};
+
+struct JSONTraceIntelPTSchema {
+ JSONTraceIntelPTSchema() = default;
+
+ JSONTraceIntelPTSchema(JSONTraceIntelPTTrace ipt_trace,
+ JSONTraceSessionBase session_base)
+ : ipt_trace(ipt_trace), session_base(session_base) {}
+
+ JSONTraceIntelPTTrace ipt_trace;
+ JSONTraceSessionBase session_base;
+};
+
+struct JSONTraceIntelPTSettings : JSONTracePluginSettings {
+ JSONTraceIntelPTCPUInfo cpuInfo;
+};
+
+} // namespace trace_intel_pt
+} // namespace lldb_private
+
+namespace llvm {
+namespace json {
+
+bool fromJSON(
+ const Value &value,
+ lldb_private::trace_intel_pt::JSONTraceIntelPTSettings &plugin_settings,
+ Path path);
+
+bool fromJSON(const llvm::json::Value &value,
+ lldb_private::trace_intel_pt::JSONTraceIntelPTCPUInfo &packet,
+ llvm::json::Path path);
+
+llvm::json::Value
+toJSON(const lldb_private::trace_intel_pt::JSONTraceIntelPTCPUInfo &cpu_info);
+
+llvm::json::Value
+toJSON(const lldb_private::trace_intel_pt::JSONTraceIntelPTTrace &trace);
+
+llvm::json::Value
+toJSON(const lldb_private::trace_intel_pt::JSONTraceIntelPTSchema &schema);
+
+} // namespace json
+} // namespace llvm
+
+#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTJSONSTRUCTS_H
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp
@@ -0,0 +1,59 @@
+//===-- 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 <string>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::trace_intel_pt;
+using namespace llvm;
+
+namespace llvm {
+namespace json {
+
+bool fromJSON(const Value &value, JSONTraceIntelPTSettings &plugin_settings,
+ Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("cpuInfo", plugin_settings.cpuInfo) &&
+ fromJSON(value, (JSONTracePluginSettings &)plugin_settings, path);
+}
+
+bool fromJSON(const json::Value &value, JSONTraceIntelPTCPUInfo &cpu_info,
+ Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("vendor", cpu_info.vendor) &&
+ o.map("family", cpu_info.family) && o.map("model", cpu_info.model) &&
+ o.map("stepping", cpu_info.stepping);
+}
+
+Value toJSON(const JSONTraceIntelPTCPUInfo &cpu_info) {
+ return Value(Object{{"family", cpu_info.family},
+ {"model", cpu_info.model},
+ {"stepping", cpu_info.stepping},
+ {"vendor", cpu_info.vendor}});
+}
+
+llvm::json::Value toJSON(const JSONTraceIntelPTTrace &trace) {
+ llvm::json::Object result;
+ result["type"] = trace.type;
+ result["cpuInfo"] = toJSON(trace.cpuInfo);
+ return std::move(result);
+}
+
+llvm::json::Value toJSON(const JSONTraceIntelPTSchema &schema) {
+ llvm::json::Object result;
+ result["trace"] = toJSON(schema.ipt_trace);
+ result["processes"] = toJSON(schema.session_base);
+ return std::move(result);
+}
+
+} // namespace json
+} // namespace llvm
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
@@ -11,6 +11,8 @@
#include "IntelPTDecoder.h"
#include "TraceIntelPTSessionFileParser.h"
+#include "lldb/Utility/FileSpec.h"
+#include "llvm/Support/raw_ostream.h"
namespace lldb_private {
namespace trace_intel_pt {
@@ -19,6 +21,11 @@
public:
void Dump(Stream *s) const override;
+ llvm::Error SaveToDisk(FileSpec directory,
+ lldb::ProcessSP &process_sp) override;
+
+ llvm::Expected<std::vector<uint8_t>> GetThreadBuffer(Thread &thread);
+
~TraceIntelPT() override = default;
/// PluginInterface protocol
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
@@ -13,9 +13,11 @@
#include "DecodedThread.h"
#include "TraceIntelPTConstants.h"
#include "TraceIntelPTSessionFileParser.h"
+#include "TraceIntelPTSessionFileSaver.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/Target.h"
+#include "llvm/ADT/None.h"
using namespace lldb;
using namespace lldb_private;
@@ -66,6 +68,26 @@
void TraceIntelPT::Dump(Stream *s) const {}
+llvm::Error TraceIntelPT::SaveToDisk(FileSpec directory,
+ lldb::ProcessSP &process_sp) {
+ RefreshLiveProcessState();
+ return TraceIntelPTSessionFileSaver().SaveToDisk(process_sp, *this,
+ directory);
+}
+
+llvm::Expected<std::vector<uint8_t>>
+TraceIntelPT::GetThreadBuffer(Thread &thread) {
+ auto it = m_thread_decoders.find(&thread);
+ if (it == m_thread_decoders.end())
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "thread not traced");
+
+ if (!it->second->GetRawTrace())
+ return it->second->GetRawTrace().takeError();
+
+ return it->second->GetRawTrace();
+}
+
Expected<TraceSP> TraceIntelPT::CreateInstanceForSessionFile(
const json::Value &trace_session_file, StringRef session_file_dir,
Debugger &debugger) {
Index: lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h
+++ lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h
@@ -33,6 +33,12 @@
/// A \a DecodedThread instance.
DecodedThreadSP Decode();
+ /// Get thr raw trace of the thread.
+ ///
+ /// \return
+ /// raw trace from the thread buffer as bytes.
+ virtual llvm::Expected<std::vector<uint8_t>> GetRawTrace() = 0;
+
ThreadDecoder(const ThreadDecoder &other) = delete;
ThreadDecoder &operator=(const ThreadDecoder &other) = delete;
@@ -61,6 +67,8 @@
private:
DecodedThreadSP DoDecode() override;
+ llvm::Expected<std::vector<uint8_t>> GetRawTrace() override;
+
lldb::ThreadPostMortemTraceSP m_trace_thread;
TraceIntelPT &m_trace;
};
@@ -77,6 +85,8 @@
private:
DecodedThreadSP DoDecode() override;
+ llvm::Expected<std::vector<uint8_t>> GetRawTrace() override;
+
lldb::ThreadSP m_thread_sp;
TraceIntelPT &m_trace;
};
Index: lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
+++ lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
@@ -7,6 +7,7 @@
#include "IntelPTDecoder.h"
+#include "llvm/ADT/Twine.h"
#include "llvm/Support/MemoryBuffer.h"
#include "../common/ThreadPostMortemTrace.h"
@@ -263,6 +264,21 @@
return std::make_shared<DecodedThread>(m_trace_thread->shared_from_this(),
instructions.takeError());
}
+llvm::Expected<std::vector<uint8_t>> PostMortemThreadDecoder::GetRawTrace() {
+ FileSpec trace_file = m_trace_thread->GetTraceFile();
+ ErrorOr<std::unique_ptr<MemoryBuffer>> trace_or_error =
+ MemoryBuffer::getFile(trace_file.GetPath());
+ if (std::error_code err = trace_or_error.getError())
+ return errorCodeToError(err);
+ MemoryBuffer &trace = **trace_or_error;
+ MutableArrayRef<uint8_t> trace_data(
+ reinterpret_cast<uint8_t *>(const_cast<char *>(trace.getBufferStart())),
+ trace.getBufferSize());
+ std::vector<uint8_t> result;
+ for (size_t i = 0; i < trace_data.size(); i++)
+ result.push_back(trace_data[i]);
+ return result;
+}
LiveThreadDecoder::LiveThreadDecoder(Thread &thread, TraceIntelPT &trace)
: m_thread_sp(thread.shared_from_this()), m_trace(trace) {}
@@ -277,3 +293,7 @@
return std::make_shared<DecodedThread>(m_thread_sp,
instructions.takeError());
}
+
+llvm::Expected<std::vector<uint8_t>> LiveThreadDecoder::GetRawTrace() {
+ return m_trace.GetLiveThreadBuffer(m_thread_sp->GetID());
+}
Index: lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt
+++ lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt
@@ -19,7 +19,9 @@
IntelPTDecoder.cpp
TraceCursorIntelPT.cpp
TraceIntelPT.cpp
+ TraceIntelPTJSONStructs.cpp
TraceIntelPTSessionFileParser.cpp
+ TraceIntelPTSessionFileSaver.cpp
LINK_LIBS
lldbCore
Index: lldb/source/Plugins/Trace/common/TraceSessionFileParser.h
===================================================================
--- lldb/source/Plugins/Trace/common/TraceSessionFileParser.h
+++ lldb/source/Plugins/Trace/common/TraceSessionFileParser.h
@@ -9,9 +9,8 @@
#ifndef LLDB_TARGET_TRACESESSIONPARSER_H
#define LLDB_TARGET_TRACESESSIONPARSER_H
-#include "llvm/Support/JSON.h"
-
#include "ThreadPostMortemTrace.h"
+#include "TraceJSONStructs.h"
namespace lldb_private {
@@ -24,46 +23,6 @@
/// See \a Trace::FindPlugin for more information regarding these JSON files.
class TraceSessionFileParser {
public:
- /// C++ structs representing the JSON trace session.
- /// \{
- struct JSONAddress {
- lldb::addr_t value;
- };
-
- struct JSONModule {
- std::string system_path;
- llvm::Optional<std::string> file;
- JSONAddress load_address;
- llvm::Optional<std::string> uuid;
- };
-
- struct JSONThread {
- int64_t tid;
- std::string trace_file;
- };
-
- struct JSONProcess {
- int64_t pid;
- std::string triple;
- std::vector<JSONThread> threads;
- std::vector<JSONModule> modules;
- };
-
- struct JSONTracePluginSettings {
- std::string type;
- };
-
- struct JSONTraceSessionBase {
- std::vector<JSONProcess> processes;
- };
-
- /// The trace plug-in implementation should provide its own TPluginSettings,
- /// which corresponds to the "trace" section of the schema.
- template <class TPluginSettings>
- struct JSONTraceSession : JSONTraceSessionBase {
- TPluginSettings trace;
- };
- /// \}
/// Helper struct holding the objects created when parsing a process
struct ParsedProcess {
@@ -130,50 +89,5 @@
};
} // namespace lldb_private
-namespace llvm {
-namespace json {
-
-bool fromJSON(const Value &value,
- lldb_private::TraceSessionFileParser::JSONAddress &address,
- Path path);
-
-bool fromJSON(const Value &value,
- lldb_private::TraceSessionFileParser::JSONModule &module,
- Path path);
-
-bool fromJSON(const Value &value,
- lldb_private::TraceSessionFileParser::JSONThread &thread,
- Path path);
-
-bool fromJSON(const Value &value,
- lldb_private::TraceSessionFileParser::JSONProcess &process,
- Path path);
-
-bool fromJSON(const Value &value,
- lldb_private::TraceSessionFileParser::JSONTracePluginSettings
- &plugin_settings,
- Path path);
-
-bool fromJSON(
- const Value &value,
- lldb_private::TraceSessionFileParser::JSONTraceSessionBase &session,
- Path path);
-
-template <class TPluginSettings>
-bool fromJSON(
- const Value &value,
- lldb_private::TraceSessionFileParser::JSONTraceSession<TPluginSettings>
- &session,
- Path path) {
- ObjectMapper o(value, path);
- return o && o.map("trace", session.trace) &&
- fromJSON(value,
- (lldb_private::TraceSessionFileParser::JSONTraceSessionBase &)
- session,
- path);
-}
-
-} // namespace json
-} // namespace llvm
#endif // LLDB_TARGET_TRACESESSIONPARSER_H
Index: lldb/source/Plugins/Trace/common/TraceSessionFileParser.cpp
===================================================================
--- lldb/source/Plugins/Trace/common/TraceSessionFileParser.cpp
+++ lldb/source/Plugins/Trace/common/TraceSessionFileParser.cpp
@@ -170,55 +170,3 @@
}
return parsed_processes;
}
-
-namespace llvm {
-namespace json {
-
-bool fromJSON(const Value &value, TraceSessionFileParser::JSONAddress &address,
- Path path) {
- Optional<StringRef> s = value.getAsString();
- if (s.hasValue() && !s->getAsInteger(0, address.value))
- return true;
-
- path.report("expected numeric string");
- return false;
-}
-
-bool fromJSON(const Value &value, TraceSessionFileParser::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);
-}
-
-bool fromJSON(const Value &value, TraceSessionFileParser::JSONThread &thread,
- Path path) {
- ObjectMapper o(value, path);
- return o && o.map("tid", thread.tid) && o.map("traceFile", thread.trace_file);
-}
-
-bool fromJSON(const Value &value, TraceSessionFileParser::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);
-}
-
-bool fromJSON(const Value &value,
- TraceSessionFileParser::JSONTracePluginSettings &plugin_settings,
- Path path) {
- ObjectMapper o(value, path);
- return o && o.map("type", plugin_settings.type);
-}
-
-bool fromJSON(const Value &value,
- TraceSessionFileParser::JSONTraceSessionBase &session,
- Path path) {
- ObjectMapper o(value, path);
- return o && o.map("processes", session.processes);
-}
-
-} // namespace json
-} // namespace llvm
Index: lldb/source/Plugins/Trace/common/TraceJSONStructs.h
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Trace/common/TraceJSONStructs.h
@@ -0,0 +1,127 @@
+//===-- TraceJSONStruct.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_TRACEJSONSTRUCTS_H
+#define LLDB_TARGET_TRACEJSONSTRUCTS_H
+
+#include "lldb/lldb-types.h"
+#include "llvm/Support/JSON.h"
+
+namespace lldb_private {
+
+/// C++ structs representing the JSON trace session.
+/// \{
+struct JSONAddress {
+ JSONAddress() = default;
+
+ JSONAddress(lldb::addr_t value) : value(value){};
+
+ lldb::addr_t value;
+};
+
+struct JSONModule {
+ JSONModule() = default;
+
+ JSONModule(std::string system_path, llvm::Optional<std::string> file,
+ JSONAddress load_address, llvm::Optional<std::string> uuid)
+ : system_path(system_path), file(file), load_address(load_address),
+ uuid(uuid) {}
+
+ std::string system_path;
+ llvm::Optional<std::string> file;
+ JSONAddress load_address;
+ llvm::Optional<std::string> uuid;
+};
+
+struct JSONThread {
+ JSONThread() = default;
+
+ JSONThread(int64_t tid, std::string trace_file)
+ : tid(tid), trace_file(trace_file) {}
+
+ int64_t tid;
+ std::string trace_file;
+};
+
+struct JSONProcess {
+ JSONProcess() = default;
+
+ JSONProcess(int64_t pid, std::string triple, std::vector<JSONThread> threads,
+ std::vector<JSONModule> modules)
+ : pid(pid), triple(triple), threads(threads), modules(modules) {}
+
+ int64_t pid;
+ std::string triple;
+ std::vector<JSONThread> threads;
+ std::vector<JSONModule> modules;
+};
+
+struct JSONTracePluginSettings {
+ std::string type;
+};
+
+struct JSONTraceSessionBase {
+ JSONTraceSessionBase() = default;
+
+ JSONTraceSessionBase(std::vector<JSONProcess> processes)
+ : processes(processes) {}
+ std::vector<JSONProcess> processes;
+};
+
+/// The trace plug-in implementation should provide its own TPluginSettings,
+/// which corresponds to the "trace" section of the schema.
+template <class TPluginSettings>
+struct JSONTraceSession : JSONTraceSessionBase {
+ TPluginSettings trace;
+};
+/// \}
+
+} // namespace lldb_private
+
+namespace llvm {
+namespace json {
+
+llvm::json::Value toJSON(const lldb_private::JSONModule &module);
+
+llvm::json::Value toJSON(const lldb_private::JSONThread &thread);
+
+llvm::json::Value toJSON(const lldb_private::JSONProcess &process);
+
+llvm::json::Value
+toJSON(const lldb_private::JSONTraceSessionBase &session_base);
+
+bool fromJSON(const Value &value, lldb_private::JSONAddress &address,
+ Path path);
+
+bool fromJSON(const Value &value, lldb_private::JSONModule &module, Path path);
+
+bool fromJSON(const Value &value, lldb_private::JSONThread &thread, Path path);
+
+bool fromJSON(const Value &value, lldb_private::JSONProcess &process,
+ Path path);
+
+bool fromJSON(const Value &value,
+ lldb_private::JSONTracePluginSettings &plugin_settings,
+ Path path);
+
+bool fromJSON(const Value &value, lldb_private::JSONTraceSessionBase &session,
+ Path path);
+
+template <class TPluginSettings>
+bool fromJSON(const Value &value,
+ lldb_private::JSONTraceSession<TPluginSettings> &session,
+ Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("trace", session.trace) &&
+ fromJSON(value, (lldb_private::JSONTraceSessionBase &)session, path);
+}
+
+} // namespace json
+} // namespace llvm
+
+#endif // LLDB_TARGET_TRACEJSONSTRUCTS_H
Index: lldb/source/Plugins/Trace/common/TraceJSONStructs.cpp
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Trace/common/TraceJSONStructs.cpp
@@ -0,0 +1,107 @@
+//===-- TraceSessionFileStructs.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 "TraceJSONStructs.h"
+#include "ThreadPostMortemTrace.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include <sstream>
+
+using namespace lldb_private;
+namespace llvm {
+namespace json {
+
+llvm::json::Value toJSON(const JSONModule &module) {
+ llvm::json::Object result;
+ result["systemPath"] = module.system_path;
+ if (module.file)
+ result["file"] = *module.file;
+ std::ostringstream oss;
+ oss << "0x" << std::hex << module.load_address.value;
+ std::string str(oss.str());
+ result["loadAddress"] = str;
+ if (module.uuid)
+ result["uuid"] = *module.uuid;
+ return std::move(result);
+}
+
+llvm::json::Value toJSON(const JSONThread &thread) {
+ return Value(Object{{"tid", thread.tid}, {"traceFile", thread.trace_file}});
+}
+
+llvm::json::Value toJSON(const JSONProcess &process) {
+ llvm::json::Object result;
+ result["pid"] = process.pid;
+ result["triple"] = process.triple;
+
+ llvm::json::Array threads_arr;
+ for (JSONThread e : process.threads) {
+ threads_arr.push_back(toJSON(e));
+ }
+ result["threads"] = llvm::json::Value(std::move(threads_arr));
+
+ llvm::json::Array modules_arr;
+ for (JSONModule e : process.modules) {
+ modules_arr.push_back(toJSON(e));
+ }
+ result["modules"] = llvm::json::Value(std::move(modules_arr));
+
+ return std::move(result);
+}
+
+llvm::json::Value toJSON(const JSONTraceSessionBase &session_base) {
+ llvm::json::Array arr;
+ for (JSONProcess e : session_base.processes) {
+ arr.push_back(toJSON(e));
+ }
+ return std::move(arr);
+}
+
+bool fromJSON(const Value &value, JSONAddress &address, Path path) {
+ Optional<StringRef> s = value.getAsString();
+ if (s.hasValue() && !s->getAsInteger(0, address.value))
+ return true;
+
+ path.report("expected numeric string");
+ return false;
+}
+
+bool fromJSON(const 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);
+}
+
+bool fromJSON(const Value &value, JSONThread &thread, Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("tid", thread.tid) && o.map("traceFile", thread.trace_file);
+}
+
+bool fromJSON(const 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);
+}
+
+bool fromJSON(const Value &value, JSONTracePluginSettings &plugin_settings,
+ Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("type", plugin_settings.type);
+}
+
+bool fromJSON(const Value &value, JSONTraceSessionBase &session, Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("processes", session.processes);
+}
+
+} // namespace json
+} // namespace llvm
Index: lldb/source/Plugins/Trace/common/CMakeLists.txt
===================================================================
--- lldb/source/Plugins/Trace/common/CMakeLists.txt
+++ lldb/source/Plugins/Trace/common/CMakeLists.txt
@@ -1,5 +1,6 @@
add_lldb_library(lldbPluginTraceCommon
ThreadPostMortemTrace.cpp
+ TraceJSONStructs.cpp
TraceSessionFileParser.cpp
LINK_LIBS
Index: lldb/source/Commands/Options.td
===================================================================
--- lldb/source/Commands/Options.td
+++ lldb/source/Commands/Options.td
@@ -736,6 +736,16 @@
"of corefile to be saved.">;
}
+
+let Command = "process trace save" in {
+ def process_trace_save_directory: Option<"directory", "d">,
+ Group<1>,
+ Arg<"Value">, Required,
+ Desc<"The directory where the trace will be saved."
+ "It will be created if it does not exist.">;
+}
+
+
let Command = "script import" in {
def script_import_allow_reload : Option<"allow-reload", "r">, Group<1>,
Desc<"Allow the script to be loaded even if it was already loaded before. "
Index: lldb/source/Commands/CommandObjectProcess.cpp
===================================================================
--- lldb/source/Commands/CommandObjectProcess.cpp
+++ lldb/source/Commands/CommandObjectProcess.cpp
@@ -1641,6 +1641,81 @@
}
};
+// CommandObjectProcessTraceSave
+#define LLDB_OPTIONS_process_trace_save
+#include "CommandOptions.inc"
+
+#pragma mark CommandObjectProcessTraceSave
+
+class CommandObjectProcessTraceSave : 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 {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+
+ case 'd': {
+ m_directory.SetFile(option_arg, FileSpec::Style::native);
+ FileSystem::Instance().Resolve(m_directory);
+ break;
+ }
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override{};
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_process_trace_save_options);
+ };
+
+ FileSpec m_directory;
+ };
+
+ Options *GetOptions() override { return &m_options; }
+ CommandObjectProcessTraceSave(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "process trace save",
+ "Save the trace of the current process in the specified directory, "
+ "which will be created if needed."
+ "This will also create a file <directory>/trace.json with the main "
+ "properties of the trace session, 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",
+ "process trace save [<cmd-options>]",
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused |
+ eCommandProcessMustBeTraced) {}
+
+ ~CommandObjectProcessTraceSave() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ ProcessSP process_sp = m_exe_ctx.GetProcessSP();
+
+ TraceSP trace_sp = process_sp->GetTarget().GetTrace();
+
+ if (llvm::Error err =
+ trace_sp->SaveToDisk(m_options.m_directory, process_sp))
+ result.AppendError(toString(std::move(err)));
+ else
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+};
+
// CommandObjectProcessTraceStop
class CommandObjectProcessTraceStop : public CommandObjectParsed {
public:
@@ -1678,6 +1753,8 @@
: CommandObjectMultiword(
interpreter, "trace", "Commands for tracing the current process.",
"process trace <subcommand> [<subcommand objects>]") {
+ LoadSubCommand("save", CommandObjectSP(
+ new CommandObjectProcessTraceSave(interpreter)));
LoadSubCommand("start", CommandObjectSP(new CommandObjectProcessTraceStart(
interpreter)));
LoadSubCommand("stop", CommandObjectSP(
Index: lldb/include/lldb/Target/Trace.h
===================================================================
--- lldb/include/lldb/Target/Trace.h
+++ lldb/include/lldb/Target/Trace.h
@@ -55,6 +55,25 @@
/// A stream object to dump the information to.
virtual void Dump(Stream *s) const = 0;
+ /// Save the trace or the given process to the specified directory, which will
+ /// be created if needed. This will also create a file <directory>/trace.json
+ /// with the main properties of the trace session, 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] directory
+ /// The directory where the trace files will be saved.
+ ///
+ /// \param[in] process_sp
+ /// The process whose trace will be saved to disk.
+ ///
+ /// \return
+ /// \a llvm::success if the operation was successful, or an \a llvm::Error
+ /// otherwise.
+ ///
+ virtual llvm::Error SaveToDisk(FileSpec directory,
+ lldb::ProcessSP &process_sp) = 0;
+
/// Find a trace plug-in using JSON data.
///
/// When loading trace data from disk, the information for the trace data
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits