hanbingwang updated this revision to Diff 365883.
hanbingwang marked an inline comment as done.
hanbingwang added a comment.

*trace.h: 
-rename SaveToDisk() to SaveLiveTraceToDisk()

*IntelPTDecoder.h, IntelPTDecoder.cpp: 
-removed GetRawTrace()

*TraceIntelPT.h, TraceIntelPT.cpp: 
-removed GetThreadBuffer()
-in function SaveToDisk(), replace "process_sp" with "m_live_process"

*TraceIntelPTSessionFileSaver.h, TraceIntelPTSessionFileSaver.cpp:
-renamed to TraceIntelPTSessionSaver.h and TraceIntelPTSessionSaver.cpp
-the function BuildModulesSection() copies modules inside <directory>/modules
-create new constructors for struct JSONTraceIntelPTCPUInfo
-error checking for writing stream to file


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D107669/new/

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/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/TraceIntelPTSessionSaver.cpp
  lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.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,49 @@
+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):
+        self.expect("target create " +
+            os.path.join(self.getSourceDir(), "intelpt-trace", "a.out"))
+        self.expect("b main")
+        self.expect("r")
+        self.expect("thread trace start")
+        self.expect("n")
+        self.expect("thread trace dump instructions", substrs=["""0x0000000000400511    movl   $0x0, -0x4(%rbp)
+    no more data"""])
+        # Save the trace to <trace_copy_dir>
+        self.expect("process trace save -d " +
+            os.path.join(self.getBuildDir(), "intelpt-trace", "trace_copy_dir"))
+        # Load the trace just saved
+        self.expect("trace load -v " +
+            os.path.join(self.getBuildDir(), "intelpt-trace", "trace_copy_dir", "trace.json"),
+            substrs=["intel-pt"])
+        self.expect("thread trace dump instructions", substrs=["""0x0000000000400511    movl   $0x0, -0x4(%rbp)
+    no more data"""])
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.h
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.h
@@ -0,0 +1,140 @@
+//===-- TraceIntelPTSessionSaver.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_TRACEINTELPTSESSIONSAVER_H
+#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTSESSIONSAVER_H
+
+#include "TraceIntelPT.h"
+
+#include "../common/TraceSessionFileParser.h"
+
+namespace lldb_private {
+namespace trace_intel_pt {
+
+class TraceIntelPT;
+
+class TraceIntelPTSessionSaver {
+
+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] live_process
+  ///     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(Process *live_process, 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.
+  ///
+  /// \return
+  ///     \a llvm::success if the operation was successful, or an \a llvm::Error
+  ///     otherwise.
+  ///
+  llvm::Error 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] live_process
+  ///     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(Process *live_process, 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] live_process
+  ///     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(Process *live_process, TraceIntelPT &trace_ipt,
+                      FileSpec directory);
+
+  /// Build modules sub-section inside processes section of the intel-pt trace's
+  /// schema. The original modules will be copied over to the \a
+  /// <directory/modules> folder. Invalid modules are skipped. Saving modules
+  /// have the benifits of making trace saving more self-contained, since both
+  /// trace and modules can be saved and sent to another machine and still gets
+  /// loaded correctly.
+  ///
+  /// \param[in] live_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 libbar.so exists in the path
+  ///     /usr/lib/foo/libbar.so, then it will be copied to
+  ///     <directory>/modules/usr/lib/foo/libbar.so.
+  ///
+  /// \return The modules section to be built.
+  //
+  llvm::Expected<std::vector<JSONModule>>
+  BuildModulesSection(Process *live_process, FileSpec directory);
+};
+
+} // namespace trace_intel_pt
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTSESSIONSAVER_H
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp
@@ -0,0 +1,190 @@
+//===-- TraceIntelPTSessionSaver.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 "TraceIntelPTSessionSaver.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/Support/Error.h"
+#include "llvm/Support/JSON.h"
+
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::trace_intel_pt;
+using namespace llvm;
+
+llvm::Error TraceIntelPTSessionSaver::SaveToDisk(Process *live_process,
+                                                 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(live_process, trace_ipt, directory);
+  if (!json_trace_session_base)
+    return json_trace_session_base.takeError();
+
+  JSONTraceIntelPTSchema json_trace_intel_pt_schema(
+      json_trace_intel_pt_trace.get(), json_trace_session_base.get());
+  return WriteIntelPTSchemaToFile(json_trace_intel_pt_schema, directory);
+}
+
+llvm::Error TraceIntelPTSessionSaver::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;
+  os.close();
+  if (!os)
+    return createStringError(inconvertibleErrorCode(),
+                             formatv("couldn't write to the file {0}",
+                                     trace_path.GetPath().c_str()));
+  return Error::success();
+}
+
+llvm::Expected<JSONTraceIntelPTTrace>
+TraceIntelPTSessionSaver::BuildTraceSection(TraceIntelPT &trace_ipt) {
+  llvm::Expected<pt_cpu> cpu_info = trace_ipt.GetCPUInfo();
+  if (!cpu_info)
+    return cpu_info.takeError();
+
+  JSONTraceIntelPTCPUInfo json_trace_intel_pt_cpu_info(cpu_info.get());
+
+  JSONTraceIntelPTTrace result("intel-pt", json_trace_intel_pt_cpu_info);
+  return result;
+}
+
+llvm::Expected<JSONTraceSessionBase>
+TraceIntelPTSessionSaver::BuildProcessesSection(Process *live_process,
+                                                TraceIntelPT &trace_ipt,
+                                                FileSpec directory) {
+  JSONTraceSessionBase result;
+  Expected<std::vector<JSONThread>> json_threads =
+      BuildThreadsSection(live_process, trace_ipt, directory);
+  if (!json_threads)
+    return json_threads.takeError();
+
+  Expected<std::vector<JSONModule>> json_modules =
+      BuildModulesSection(live_process, directory);
+  if (!json_modules) {
+    return json_modules.takeError();
+  }
+
+  JSONProcess json_process(
+      live_process->GetID(),
+      live_process->GetTarget().GetArchitecture().GetTriple().getTriple(),
+      json_threads.get(), json_modules.get());
+
+  result.processes.push_back(json_process);
+  return result;
+}
+
+llvm::Expected<std::vector<JSONThread>>
+TraceIntelPTSessionSaver::BuildThreadsSection(Process *live_process,
+                                              TraceIntelPT &trace_ipt,
+                                              FileSpec directory) {
+  std::vector<JSONThread> result;
+  for (ThreadSP thread_sp : live_process->Threads()) {
+    JSONThread json_thread;
+    json_thread.tid = thread_sp->GetID();
+    // resolve the directory just in case
+    FileSystem::Instance().Resolve(directory);
+    FileSpec raw_trace_path = directory;
+    raw_trace_path.AppendPathComponent(std::to_string(thread_sp->GetID()) +
+                                       ".trace");
+    json_thread.trace_file = raw_trace_path.GetPath().c_str();
+    result.push_back(json_thread);
+
+    Expected<std::vector<uint8_t>> raw_trace =
+        trace_ipt.GetLiveThreadBuffer(thread_sp->GetID());
+    if (!raw_trace)
+      return raw_trace.takeError();
+
+    std::basic_fstream<char> raw_trace_fs =
+        std::fstream(json_thread.trace_file, std::ios::out | std::ios::binary);
+    raw_trace_fs.write(reinterpret_cast<const char *>(&raw_trace->at(0)),
+                       raw_trace->size() * sizeof(uint8_t));
+    raw_trace_fs.close();
+    if (!raw_trace_fs) {
+      return createStringError(
+          inconvertibleErrorCode(),
+          formatv("couldn't write to the file {0}", json_thread.trace_file));
+    }
+  }
+  return result;
+}
+
+llvm::Expected<std::vector<JSONModule>>
+TraceIntelPTSessionSaver::BuildModulesSection(Process *live_process,
+                                              FileSpec directory) {
+  std::vector<JSONModule> result;
+  ModuleList module_list = live_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() &&
+        !live_process->GetTarget().GetSectionLoadList().IsEmpty()) {
+      load_addr = base_addr.GetLoadAddress(&live_process->GetTarget());
+    }
+    if (load_addr == LLDB_INVALID_ADDRESS)
+      continue;
+
+    FileSystem::Instance().Resolve(directory);
+    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(
+            system_path, path_to_copy_module.GetPath()))
+      return createStringError(
+          inconvertibleErrorCode(),
+          formatv("couldn't write to the file. {0}", ec.message()));
+
+    JSONAddress load_address(load_addr);
+    JSONModule json_module(system_path, path_to_copy_module.GetPath(),
+                           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,86 @@
+//===-- 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"
+#include <intel-pt.h>
+
+namespace lldb_private {
+namespace trace_intel_pt {
+
+struct JSONTraceIntelPTCPUInfo {
+  JSONTraceIntelPTCPUInfo() = default;
+
+  JSONTraceIntelPTCPUInfo(pt_cpu cpu_info) {
+    family = static_cast<int64_t>(cpu_info.family);
+    model = static_cast<int64_t>(cpu_info.model);
+    stepping = static_cast<int64_t>(cpu_info.stepping);
+    vendor = cpu_info.vendor == pcv_intel ? "intel" : "Unknown";
+  }
+
+  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,8 @@
 public:
   void Dump(Stream *s) const override;
 
+  llvm::Error SaveLiveTraceToDisk(FileSpec directory) override;
+
   ~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,13 @@
 #include "DecodedThread.h"
 #include "TraceIntelPTConstants.h"
 #include "TraceIntelPTSessionFileParser.h"
+#include "TraceIntelPTSessionSaver.h"
 #include "lldb/Core/PluginManager.h"
 #include "lldb/Target/Process.h"
 #include "lldb/Target/Target.h"
+#include "lldb/lldb-forward.h"
+#include "llvm/ADT/None.h"
+#include <cstdlib>
 
 using namespace lldb;
 using namespace lldb_private;
@@ -66,6 +70,16 @@
 
 void TraceIntelPT::Dump(Stream *s) const {}
 
+llvm::Error TraceIntelPT::SaveLiveTraceToDisk(FileSpec directory) {
+  RefreshLiveProcessState();
+  if (!m_live_process)
+    return createStringError(inconvertibleErrorCode(),
+                             "Saving live trace requires a live process.");
+
+  return TraceIntelPTSessionSaver().SaveToDisk(m_live_process, *this,
+                                               directory);
+}
+
 Expected<TraceSP> TraceIntelPT::CreateInstanceForSessionFile(
     const json::Value &trace_session_file, StringRef session_file_dir,
     Debugger &debugger) {
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
+  TraceIntelPTSessionSaver.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,80 @@
   }
 };
 
+// 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. "
+            "The directory 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->SaveLiveTraceToDisk(m_options.m_directory))
+      result.AppendError(toString(std::move(err)));
+    else
+      result.SetStatus(eReturnStatusSuccessFinishResult);
+
+    return result.Succeeded();
+  }
+
+  CommandOptions m_options;
+};
+
 // CommandObjectProcessTraceStop
 class CommandObjectProcessTraceStop : public CommandObjectParsed {
 public:
@@ -1678,6 +1752,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,22 @@
   ///     A stream object to dump the information to.
   virtual void Dump(Stream *s) const = 0;
 
+  /// Save the trace of a process that is doing live tracing to the specified
+  /// directory, which will be created if needed. This will also create a
+  /// 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. If not tracing a live process, return an error.
+  ///
+  /// \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.
+  ///
+  virtual llvm::Error SaveLiveTraceToDisk(FileSpec directory) = 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
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to