[Lldb-commits] [lldb] b532dd5 - [trace] Add an option to save a compact trace bundle

Walter Erquinigo via lldb-commits lldb-commits at lists.llvm.org
Wed Jul 13 11:43:35 PDT 2022


Author: Walter Erquinigo
Date: 2022-07-13T11:43:28-07:00
New Revision: b532dd545fcd03562b324d99eb28523f8510e821

URL: https://github.com/llvm/llvm-project/commit/b532dd545fcd03562b324d99eb28523f8510e821
DIFF: https://github.com/llvm/llvm-project/commit/b532dd545fcd03562b324d99eb28523f8510e821.diff

LOG: [trace] Add an option to save a compact trace bundle

A trace bundle contains many trace files, and, in the case of intel pt, the
largest files are often the context switch traces because they are not
compressed by default. As a way to improve this, I'm adding a --compact option
to the `trace save` command that filters out unwanted processes from the
context switch traces. Eventually we can do the same for intel pt traces as
well.

Differential Revision: https://reviews.llvm.org/D129239

Added: 
    

Modified: 
    lldb/bindings/interface/SBTrace.i
    lldb/include/lldb/API/SBTrace.h
    lldb/include/lldb/Target/Trace.h
    lldb/packages/Python/lldbsuite/test/tools/intelpt/intelpt_testcase.py
    lldb/source/API/SBTrace.cpp
    lldb/source/Commands/CommandObjectProcess.cpp
    lldb/source/Commands/CommandObjectTrace.cpp
    lldb/source/Commands/Options.td
    lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.cpp
    lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.h
    lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
    lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
    lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.cpp
    lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.h
    lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td
    lldb/test/API/commands/trace/TestTraceLoad.py
    lldb/test/API/commands/trace/TestTraceSave.py

Removed: 
    


################################################################################
diff  --git a/lldb/bindings/interface/SBTrace.i b/lldb/bindings/interface/SBTrace.i
index 0f5bf0ecc8d94..0d74881a3f3d8 100644
--- a/lldb/bindings/interface/SBTrace.i
+++ b/lldb/bindings/interface/SBTrace.i
@@ -17,6 +17,8 @@ public:
 
   const char *GetStartConfigurationHelp();
 
+  SBFileSpec SaveToDisk(SBError &error, const SBFileSpec &bundle_dir, bool compact = false);
+
   SBError Start(const SBStructuredData &configuration);
 
   SBError Start(const SBThread &thread, const SBStructuredData &configuration);

diff  --git a/lldb/include/lldb/API/SBTrace.h b/lldb/include/lldb/API/SBTrace.h
index d5cf30f56637f..19d759013955c 100644
--- a/lldb/include/lldb/API/SBTrace.h
+++ b/lldb/include/lldb/API/SBTrace.h
@@ -25,6 +25,28 @@ class LLDB_API SBTrace {
   static SBTrace LoadTraceFromFile(SBError &error, SBDebugger &debugger,
                                    const SBFileSpec &trace_description_file);
 
+  /// Save the trace to the specified directory, which will be created if
+  /// needed. This will also create a a file \a <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, or for the
+  /// method \a SBDebugger.LoadTraceFromFile().
+  ///
+  /// \param[out] error
+  ///   This will be set with an error in case of failures.
+  ///
+  /// \param[in] directory
+  ///   The directory where the trace files will be saved.
+  ///
+  /// \param[in] compact
+  ///   Try not to save to disk information irrelevant to the traced processes.
+  ///   Each trace plug-in implements this in a 
diff erent fashion.
+  ///
+  /// \return
+  ///   A \a SBFileSpec pointing to the bundle description file.
+  SBFileSpec SaveToDisk(SBError &error, const SBFileSpec &bundle_dir,
+                        bool compact = false);
+
   /// \return
   ///     A description of the parameters to use for the \a SBTrace::Start
   ///     method, or \b null if the object is invalid.

diff  --git a/lldb/include/lldb/Target/Trace.h b/lldb/include/lldb/Target/Trace.h
index f4d7dee684c35..2591af098b1b5 100644
--- a/lldb/include/lldb/Target/Trace.h
+++ b/lldb/include/lldb/Target/Trace.h
@@ -56,21 +56,24 @@ class Trace : public PluginInterface,
   ///     A stream object to dump the information to.
   virtual void Dump(Stream *s) const = 0;
 
-  /// Save the trace of a live process to the specified directory, which
-  /// will be created if needed.
-  /// This will also create a a file \a <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.
-  /// The process being trace is not a live process, return an error.
+  /// Save the trace to the specified directory, which will be created if
+  /// needed. This will also create a a file \a <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] compact
+  ///   Try not to save to disk information irrelevant to the traced processes.
+  ///   Each trace plug-in implements this in a 
diff erent fashion.
+  ///
   /// \return
-  ///   \a llvm::success if the operation was successful, or an \a llvm::Error
-  ///   otherwise.
-  virtual llvm::Error SaveLiveTraceToDisk(FileSpec directory) = 0;
+  ///   A \a FileSpec pointing to the bundle description file, or an \a
+  ///   llvm::Error otherwise.
+  virtual llvm::Expected<FileSpec> SaveToDisk(FileSpec directory,
+                                              bool compact) = 0;
 
   /// Find a trace plug-in using JSON data.
   ///

diff  --git a/lldb/packages/Python/lldbsuite/test/tools/intelpt/intelpt_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/intelpt/intelpt_testcase.py
index 436cf6e868570..511dd6822b866 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/intelpt/intelpt_testcase.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/intelpt/intelpt_testcase.py
@@ -134,12 +134,24 @@ def traceStopThread(self, thread=None, error=False, substrs=None):
                 command += " " + str(thread.GetIndexID())
             self.expect(command, error=error, substrs=substrs)
 
-    def traceLoad(self, traceDescriptionFilePath="trace.json", error=False, substrs=None):
+    def traceLoad(self, traceDescriptionFilePath, error=False, substrs=None):
         if self.USE_SB_API:
             traceDescriptionFile = lldb.SBFileSpec(traceDescriptionFilePath, True)
             loadTraceError = lldb.SBError()
-            _trace = self.dbg.LoadTraceFromFile(loadTraceError, traceDescriptionFile)
+            self.dbg.LoadTraceFromFile(loadTraceError, traceDescriptionFile)
             self.assertSBError(loadTraceError, error)
         else:
             command = f"trace load -v {traceDescriptionFilePath}"
             self.expect(command, error=error, substrs=substrs)
+
+    def traceSave(self, traceBundleDir, compact=False, error=False, substrs=None):
+        if self.USE_SB_API:
+            save_error = lldb.SBError()
+            self.target().GetTrace().SaveToDisk(
+                save_error, lldb.SBFileSpec(traceBundleDir), compact)
+            self.assertSBError(save_error, error)
+        else:
+            command = f"trace save {traceBundleDir}"
+            if compact:
+                command += " -c"
+            self.expect(command, error=error, substrs=substrs)

diff  --git a/lldb/source/API/SBTrace.cpp b/lldb/source/API/SBTrace.cpp
index fe90032370733..2b1f140161b6d 100644
--- a/lldb/source/API/SBTrace.cpp
+++ b/lldb/source/API/SBTrace.cpp
@@ -43,6 +43,24 @@ SBTrace SBTrace::LoadTraceFromFile(SBError &error, SBDebugger &debugger,
   return SBTrace(trace_or_err.get());
 }
 
+SBFileSpec SBTrace::SaveToDisk(SBError &error, const SBFileSpec &bundle_dir,
+                               bool compact) {
+  LLDB_INSTRUMENT_VA(this, error, bundle_dir, compact);
+
+  error.Clear();
+  SBFileSpec file_spec;
+
+  if (!m_opaque_sp)
+    error.SetErrorString("error: invalid trace");
+  else if (Expected<FileSpec> desc_file =
+               m_opaque_sp->SaveToDisk(bundle_dir.ref(), compact))
+    file_spec.SetFileSpec(*desc_file);
+  else
+    error.SetErrorString(llvm::toString(desc_file.takeError()).c_str());
+
+  return file_spec;
+}
+
 const char *SBTrace::GetStartConfigurationHelp() {
   LLDB_INSTRUMENT_VA(this);
   return m_opaque_sp ? m_opaque_sp->GetStartConfigurationHelp() : nullptr;

diff  --git a/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp
index c76ae99057f2a..d36a574aba7d9 100644
--- a/lldb/source/Commands/CommandObjectProcess.cpp
+++ b/lldb/source/Commands/CommandObjectProcess.cpp
@@ -579,14 +579,14 @@ class CommandObjectProcessContinue : public CommandObjectParsed {
           }
         }
       }
-      
+
       Target *target = m_exe_ctx.GetTargetPtr();
       BreakpointIDList run_to_bkpt_ids;
       // Don't pass an empty run_to_breakpoint list, as Verify will look for the
       // default breakpoint.
       if (m_options.m_run_to_bkpt_args.GetArgumentCount() > 0)
         CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(
-            m_options.m_run_to_bkpt_args, target, result, &run_to_bkpt_ids, 
+            m_options.m_run_to_bkpt_args, target, result, &run_to_bkpt_ids,
             BreakpointName::Permissions::disablePerm);
       if (!result.Succeeded()) {
         return false;
@@ -604,7 +604,7 @@ class CommandObjectProcessContinue : public CommandObjectParsed {
       std::vector<break_id_t> bkpts_disabled;
       std::vector<BreakpointID> locs_disabled;
       if (num_run_to_bkpt_ids != 0) {
-        // Go through the ID's specified, and separate the breakpoints from are 
+        // Go through the ID's specified, and separate the breakpoints from are
         // the breakpoint.location specifications since the latter require
         // special handling.  We also figure out whether there's at least one
         // specifier in the set that is enabled.
@@ -613,22 +613,22 @@ class CommandObjectProcessContinue : public CommandObjectParsed {
         std::unordered_set<break_id_t> bkpts_with_locs_seen;
         BreakpointIDList with_locs;
         bool any_enabled = false;
-  
+
         for (size_t idx = 0; idx < num_run_to_bkpt_ids; idx++) {
           BreakpointID bkpt_id = run_to_bkpt_ids.GetBreakpointIDAtIndex(idx);
           break_id_t bp_id = bkpt_id.GetBreakpointID();
           break_id_t loc_id = bkpt_id.GetLocationID();
-          BreakpointSP bp_sp 
+          BreakpointSP bp_sp
               = bkpt_list.FindBreakpointByID(bp_id);
-          // Note, VerifyBreakpointOrLocationIDs checks for existence, so we 
+          // Note, VerifyBreakpointOrLocationIDs checks for existence, so we
           // don't need to do it again here.
           if (bp_sp->IsEnabled()) {
             if (loc_id == LLDB_INVALID_BREAK_ID) {
-              // A breakpoint (without location) was specified.  Make sure that 
+              // A breakpoint (without location) was specified.  Make sure that
               // at least one of the locations is enabled.
               size_t num_locations = bp_sp->GetNumLocations();
               for (size_t loc_idx = 0; loc_idx < num_locations; loc_idx++) {
-                BreakpointLocationSP loc_sp 
+                BreakpointLocationSP loc_sp
                     = bp_sp->GetLocationAtIndex(loc_idx);
                 if (loc_sp->IsEnabled()) {
                   any_enabled = true;
@@ -641,7 +641,7 @@ class CommandObjectProcessContinue : public CommandObjectParsed {
               if (loc_sp->IsEnabled())
                 any_enabled = true;
             }
-          
+
             // Then sort the bp & bp.loc entries for later use:
             if (bkpt_id.GetLocationID() == LLDB_INVALID_BREAK_ID)
               bkpts_seen.insert(bkpt_id.GetBreakpointID());
@@ -653,14 +653,14 @@ class CommandObjectProcessContinue : public CommandObjectParsed {
         }
         // Do all the error checking here so once we start disabling we don't
         // have to back out half-way through.
-        
+
         // Make sure at least one of the specified breakpoints is enabled.
         if (!any_enabled) {
           result.AppendError("at least one of the continue-to breakpoints must "
                              "be enabled.");
           return false;
         }
-        
+
         // Also, if you specify BOTH a breakpoint and one of it's locations,
         // we flag that as an error, since it won't do what you expect, the
         // breakpoint directive will mean "run to all locations", which is not
@@ -671,7 +671,7 @@ class CommandObjectProcessContinue : public CommandObjectParsed {
                                "one of its locations: {0}", bp_id);
           }
         }
-        
+
         // Now go through the breakpoints in the target, disabling all the ones
         // that the user didn't mention:
         for (BreakpointSP bp_sp : bkpt_list.Breakpoints()) {
@@ -695,7 +695,7 @@ class CommandObjectProcessContinue : public CommandObjectParsed {
             BreakpointLocationSP loc_sp = bp_sp->GetLocationAtIndex(loc_idx);
             tmp_id.SetBreakpointLocationID(loc_idx);
             size_t position = 0;
-            if (!with_locs.FindBreakpointID(tmp_id, &position) 
+            if (!with_locs.FindBreakpointID(tmp_id, &position)
                 && loc_sp->IsEnabled()) {
               locs_disabled.push_back(tmp_id);
               loc_sp->SetEnabled(false);
@@ -723,20 +723,20 @@ class CommandObjectProcessContinue : public CommandObjectParsed {
       Status error;
       // For now we can only do -b with synchronous:
       bool old_sync = GetDebugger().GetAsyncExecution();
-      
+
       if (run_to_bkpt_ids.GetSize() != 0) {
         GetDebugger().SetAsyncExecution(false);
         synchronous_execution = true;
-      } 
+      }
       if (synchronous_execution)
         error = process->ResumeSynchronous(&stream);
       else
         error = process->Resume();
-      
+
       if (run_to_bkpt_ids.GetSize() != 0) {
         GetDebugger().SetAsyncExecution(old_sync);
-      } 
-       
+      }
+
       // Now re-enable the breakpoints we disabled:
       BreakpointList &bkpt_list = target->GetBreakpointList();
       for (break_id_t bp_id : bkpts_disabled) {
@@ -745,10 +745,10 @@ class CommandObjectProcessContinue : public CommandObjectParsed {
           bp_sp->SetEnabled(true);
       }
       for (const BreakpointID &bkpt_id : locs_disabled) {
-        BreakpointSP bp_sp 
+        BreakpointSP bp_sp
             = bkpt_list.FindBreakpointByID(bkpt_id.GetBreakpointID());
         if (bp_sp) {
-          BreakpointLocationSP loc_sp 
+          BreakpointLocationSP loc_sp
               = bp_sp->FindLocationByID(bkpt_id.GetLocationID());
           if (loc_sp)
             loc_sp->SetEnabled(true);
@@ -1731,7 +1731,7 @@ class CommandObjectProcessHandle : public CommandObjectParsed {
   bool DoExecute(Args &signal_args, CommandReturnObject &result) override {
     Target &target = GetSelectedOrDummyTarget();
 
-    // Any signals that are being set should be added to the Target's 
+    // Any signals that are being set should be added to the Target's
     // DummySignals so they will get applied on rerun, etc.
     // If we have a process, however, we can do a more accurate job of vetting
     // the user's options.
@@ -1761,8 +1761,8 @@ class CommandObjectProcessHandle : public CommandObjectParsed {
                          "true or false.\n");
       return false;
     }
-    
-    bool no_actions = (stop_action == -1 && pass_action == -1 
+
+    bool no_actions = (stop_action == -1 && pass_action == -1
         && notify_action == -1);
     if (m_options.only_target_values && !no_actions) {
       result.AppendError("-t is for reporting, not setting, target values.");
@@ -1832,9 +1832,9 @@ class CommandObjectProcessHandle : public CommandObjectParsed {
         }
         auto set_lazy_bool = [] (int action) -> LazyBool {
           LazyBool lazy;
-          if (action == -1) 
+          if (action == -1)
             lazy = eLazyBoolCalculate;
-          else if (action) 
+          else if (action)
             lazy = eLazyBoolYes;
           else
             lazy = eLazyBoolNo;
@@ -1876,7 +1876,7 @@ class CommandObjectProcessHandle : public CommandObjectParsed {
       PrintSignalInformation(result.GetOutputStream(), signal_args,
                              num_signals_set, signals_sp);
     else
-      target.PrintDummySignals(result.GetOutputStream(), 
+      target.PrintDummySignals(result.GetOutputStream(),
           signal_args);
 
     if (num_signals_set > 0)
@@ -1909,80 +1909,6 @@ class CommandObjectProcessTraceStart : public CommandObjectTraceProxy {
   }
 };
 
-// CommandObjectProcessTraceSave
-#define LLDB_OPTIONS_process_trace_save
-#include "CommandOptions.inc"
-
-#pragma mark CommandObjectProcessTraceSave
-
-class CommandObjectProcessTraceSave : public CommandObjectParsed {
-public:
-  class CommandOptions : public Options {
-  public:
-    CommandOptions() { 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:
@@ -2020,8 +1946,6 @@ class CommandObjectMultiwordProcessTrace : public CommandObjectMultiword {
       : 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(

diff  --git a/lldb/source/Commands/CommandObjectTrace.cpp b/lldb/source/Commands/CommandObjectTrace.cpp
index 17aded9ed2a0c..227de2de7065d 100644
--- a/lldb/source/Commands/CommandObjectTrace.cpp
+++ b/lldb/source/Commands/CommandObjectTrace.cpp
@@ -30,6 +30,108 @@ using namespace lldb;
 using namespace lldb_private;
 using namespace llvm;
 
+// CommandObjectTraceSave
+#define LLDB_OPTIONS_trace_save
+#include "CommandOptions.inc"
+
+#pragma mark CommandObjectTraceSave
+
+class CommandObjectTraceSave : public CommandObjectParsed {
+public:
+  class CommandOptions : public Options {
+  public:
+    CommandOptions() { 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 'c': {
+        m_compact = true;
+        break;
+      }
+      default:
+        llvm_unreachable("Unimplemented option");
+      }
+      return error;
+    }
+
+    void OptionParsingStarting(ExecutionContext *execution_context) override {
+      m_compact = false;
+    };
+
+    llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+      return llvm::makeArrayRef(g_trace_save_options);
+    };
+
+    bool m_compact;
+  };
+
+  Options *GetOptions() override { return &m_options; }
+
+  CommandObjectTraceSave(CommandInterpreter &interpreter)
+      : CommandObjectParsed(
+            interpreter, "trace save",
+            "Save the trace of the current target in the specified directory, "
+            "which will be created if needed. "
+            "This directory will contain a trace bundle, with all the "
+            "necessary files the reconstruct the trace session even on a "
+            "
diff erent computer. "
+            "Part of this bundle is the bundle description file with the name "
+            "trace.json. This file can be used by the \"trace load\" command "
+            "to load this trace in LLDB."
+            "Note: if the current target contains information of multiple "
+            "processes or targets, they all will be included in the bundle.",
+            "trace save [<cmd-options>] <bundle_directory>",
+            eCommandRequiresProcess | eCommandTryTargetAPILock |
+                eCommandProcessMustBeLaunched | eCommandProcessMustBePaused |
+                eCommandProcessMustBeTraced) {
+    CommandArgumentData bundle_dir{eArgTypeDirectoryName, eArgRepeatPlain};
+    m_arguments.push_back({bundle_dir});
+  }
+
+  void
+  HandleArgumentCompletion(CompletionRequest &request,
+                           OptionElementVector &opt_element_vector) override {
+    CommandCompletions::InvokeCommonCompletionCallbacks(
+        GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion,
+        request, nullptr);
+  }
+
+  ~CommandObjectTraceSave() override = default;
+
+protected:
+  bool DoExecute(Args &command, CommandReturnObject &result) override {
+    if (command.size() != 1) {
+      result.AppendError("a single path to a directory where the trace bundle "
+                         "will be created is required");
+      return false;
+    }
+
+    FileSpec bundle_dir(command[0].ref());
+    FileSystem::Instance().Resolve(bundle_dir);
+
+    ProcessSP process_sp = m_exe_ctx.GetProcessSP();
+
+    TraceSP trace_sp = process_sp->GetTarget().GetTrace();
+
+    if (llvm::Expected<FileSpec> desc_file =
+            trace_sp->SaveToDisk(bundle_dir, m_options.m_compact)) {
+      result.AppendMessageWithFormatv(
+          "Trace bundle description file written to: {0}", *desc_file);
+      result.SetStatus(eReturnStatusSuccessFinishResult);
+    } else {
+      result.AppendError(toString(desc_file.takeError()));
+    }
+
+    return result.Succeeded();
+  }
+
+  CommandOptions m_options;
+};
+
 // CommandObjectTraceLoad
 #define LLDB_OPTIONS_trace_load
 #include "CommandOptions.inc"
@@ -75,11 +177,19 @@ class CommandObjectTraceLoad : public CommandObjectParsed {
       : CommandObjectParsed(
             interpreter, "trace load",
             "Load a post-mortem processor trace session from a trace bundle.",
-            "trace load") {
-    CommandArgumentData session_file_arg{eArgTypePath, eArgRepeatPlain};
+            "trace load <trace_description_file>") {
+    CommandArgumentData session_file_arg{eArgTypeFilename, eArgRepeatPlain};
     m_arguments.push_back({session_file_arg});
   }
 
+  void
+  HandleArgumentCompletion(CompletionRequest &request,
+                           OptionElementVector &opt_element_vector) override {
+    CommandCompletions::InvokeCommonCompletionCallbacks(
+        GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion,
+        request, nullptr);
+  }
+
   ~CommandObjectTraceLoad() override = default;
 
   Options *GetOptions() override { return &m_options; }
@@ -284,6 +394,8 @@ CommandObjectTrace::CommandObjectTrace(CommandInterpreter &interpreter)
                  CommandObjectSP(new CommandObjectTraceLoad(interpreter)));
   LoadSubCommand("dump",
                  CommandObjectSP(new CommandObjectTraceDump(interpreter)));
+  LoadSubCommand("save",
+                 CommandObjectSP(new CommandObjectTraceSave(interpreter)));
   LoadSubCommand("schema",
                  CommandObjectSP(new CommandObjectTraceSchema(interpreter)));
 }

diff  --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td
index ef505c90825d6..d7134239aac69 100644
--- a/lldb/source/Commands/Options.td
+++ b/lldb/source/Commands/Options.td
@@ -788,14 +788,6 @@ let Command = "process save_core" in {
     "This allows core files to be saved in 
diff erent formats.">;
 }
 
-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. "
@@ -1359,6 +1351,14 @@ let Command = "trace load" in {
     "implementation.">;
 }
 
+let Command = "trace save" in {
+  def trace_save_compact: Option<"compact", "c">,
+    Group<1>,
+    Desc<"Try not to save to disk information irrelevant to the traced "
+        "processes. Each trace plug-in implements this in a 
diff erent "
+        "fashion.">;
+}
+
 let Command = "trace dump" in {
   def trace_dump_verbose : Option<"verbose", "v">, Group<1>,
     Desc<"Show verbose trace information.">;

diff  --git a/lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.cpp b/lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.cpp
index 6b4251a0fcd98..0c468cf7852f7 100644
--- a/lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.cpp
@@ -16,8 +16,13 @@ using namespace llvm;
 /// non-linux platforms.
 /// \{
 #define PERF_RECORD_MISC_SWITCH_OUT (1 << 13)
-#define PERF_RECORD_MAX 19
+
+#define PERF_RECORD_LOST 2
+#define PERF_RECORD_THROTTLE 5
+#define PERF_RECORD_UNTHROTTLE 6
+#define PERF_RECORD_LOST_SAMPLES 13
 #define PERF_RECORD_SWITCH_CPU_WIDE 15
+#define PERF_RECORD_MAX 19
 
 struct perf_event_header {
   uint32_t type;
@@ -54,6 +59,11 @@ struct perf_event_header {
   bool IsContextSwitchRecord() const {
     return type == PERF_RECORD_SWITCH_CPU_WIDE;
   }
+
+  bool IsErrorRecord() const {
+    return type == PERF_RECORD_LOST || type == PERF_RECORD_THROTTLE ||
+           type == PERF_RECORD_UNTHROTTLE || type == PERF_RECORD_LOST_SAMPLES;
+  }
 };
 /// \}
 
@@ -286,3 +296,36 @@ lldb_private::trace_intel_pt::DecodePerfContextSwitchTrace(
 
   return executions;
 }
+
+Expected<std::vector<uint8_t>>
+lldb_private::trace_intel_pt::FilterProcessesFromContextSwitchTrace(
+    llvm::ArrayRef<uint8_t> data, const std::set<lldb::pid_t> &pids) {
+  size_t offset = 0;
+  std::vector<uint8_t> out_data;
+
+  while (offset < data.size()) {
+    const perf_event_header &perf_record =
+        *reinterpret_cast<const perf_event_header *>(data.data() + offset);
+    if (Error err = perf_record.SanityCheck())
+      return std::move(err);
+    bool should_copy = false;
+    if (perf_record.IsContextSwitchRecord()) {
+      const PerfContextSwitchRecord &context_switch_record =
+          *reinterpret_cast<const PerfContextSwitchRecord *>(data.data() +
+                                                             offset);
+      if (pids.count(context_switch_record.pid))
+        should_copy = true;
+    } else if (perf_record.IsErrorRecord()) {
+      should_copy = true;
+    }
+
+    if (should_copy) {
+      for (size_t i = 0; i < perf_record.size; i++) {
+        out_data.push_back(data[offset + i]);
+      }
+    }
+
+    offset += perf_record.size;
+  }
+  return out_data;
+}

diff  --git a/lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.h b/lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.h
index 721aa1d774813..a16a437e1888e 100644
--- a/lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.h
+++ b/lldb/source/Plugins/Trace/intel-pt/PerfContextSwitchDecoder.h
@@ -14,6 +14,7 @@
 
 #include "llvm/Support/Error.h"
 
+#include <set>
 #include <vector>
 
 namespace lldb_private {
@@ -139,6 +140,10 @@ DecodePerfContextSwitchTrace(llvm::ArrayRef<uint8_t> data,
                              lldb::cpu_id_t cpu_id,
                              const LinuxPerfZeroTscConversion &tsc_conversion);
 
+llvm::Expected<std::vector<uint8_t>>
+FilterProcessesFromContextSwitchTrace(llvm::ArrayRef<uint8_t> data,
+                                      const std::set<lldb::pid_t> &pids);
+
 } // namespace trace_intel_pt
 } // namespace lldb_private
 

diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
index 55053805462d2..2bda34bd34d0d 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
@@ -55,9 +55,9 @@ StringRef TraceIntelPT::GetSchema() {
 
 void TraceIntelPT::Dump(Stream *s) const {}
 
-llvm::Error TraceIntelPT::SaveLiveTraceToDisk(FileSpec directory) {
+Expected<FileSpec> TraceIntelPT::SaveToDisk(FileSpec directory, bool compact) {
   RefreshLiveProcessState();
-  return TraceIntelPTBundleSaver().SaveToDisk(*this, directory);
+  return TraceIntelPTBundleSaver().SaveToDisk(*this, directory, compact);
 }
 
 Expected<TraceSP> TraceIntelPT::CreateInstanceForTraceBundle(

diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
index af68539a072ea..a7b95e8108548 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
@@ -25,7 +25,8 @@ class TraceIntelPT : public Trace {
 public:
   void Dump(Stream *s) const override;
 
-  llvm::Error SaveLiveTraceToDisk(FileSpec directory) override;
+  llvm::Expected<FileSpec> SaveToDisk(FileSpec directory,
+                                      bool compact) override;
 
   ~TraceIntelPT() override = default;
 

diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.cpp
index b2ebaee732b88..8be70dc2139be 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.cpp
@@ -7,8 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "TraceIntelPTBundleSaver.h"
+
+#include "PerfContextSwitchDecoder.h"
 #include "TraceIntelPT.h"
 #include "TraceIntelPTJSONStructs.h"
+
 #include "lldb/Core/Module.h"
 #include "lldb/Core/ModuleList.h"
 #include "lldb/Target/Process.h"
@@ -30,6 +33,13 @@ using namespace lldb_private;
 using namespace lldb_private::trace_intel_pt;
 using namespace llvm;
 
+/// Strip the \p directory component from the given \p path. It assumes that \p
+/// directory is a prefix of \p path.
+static std::string GetRelativePath(const FileSpec &directory,
+                                   const FileSpec &path) {
+  return path.GetPath().substr(directory.GetPath().size() + 1);
+}
+
 /// Write a stream of bytes from \p data to the given output file.
 /// It creates or overwrites the output file, but not append.
 static llvm::Error WriteBytesToDisk(FileSpec &output_file,
@@ -57,11 +67,11 @@ static llvm::Error WriteBytesToDisk(FileSpec &output_file,
 ///     The directory where the JSON file will be saved.
 ///
 /// \return
-///     \a llvm::Success if the operation was successful, or an \a llvm::Error
-///     otherwise.
-static llvm::Error
+///     A \a FileSpec pointing to the bundle description file, or an \a
+///     llvm::Error otherwise.
+static Expected<FileSpec>
 SaveTraceBundleDescription(const llvm::json::Value &trace_bundle_description,
-                   const FileSpec &directory) {
+                           const FileSpec &directory) {
   FileSpec trace_path = directory;
   trace_path.AppendPathComponent("trace.json");
   std::ofstream os(trace_path.GetPath());
@@ -71,7 +81,7 @@ SaveTraceBundleDescription(const llvm::json::Value &trace_bundle_description,
     return createStringError(inconvertibleErrorCode(),
                              formatv("couldn't write to the file {0}",
                                      trace_path.GetPath().c_str()));
-  return Error::success();
+  return trace_path;
 }
 
 /// Build the threads sub-section of the trace bundle description file.
@@ -106,7 +116,7 @@ BuildThreadsSection(Process &process, FileSpec directory) {
     if (trace_sp->GetTracedCpus().empty()) {
       FileSpec output_file = threads_dir;
       output_file.AppendPathComponent(std::to_string(tid) + ".intelpt_trace");
-      json_thread.ipt_trace = output_file.GetPath();
+      json_thread.ipt_trace = GetRelativePath(directory, output_file);
 
       llvm::Error err = process.GetTarget().GetTrace()->OnThreadBinaryDataRead(
           tid, IntelPTDataKinds::kIptTrace,
@@ -122,8 +132,68 @@ BuildThreadsSection(Process &process, FileSpec directory) {
   return json_threads;
 }
 
+/// \return
+///   an \a llvm::Error in case of failures, \a None if the trace is not written
+///   to disk because the trace is empty and the \p compact flag is present, or
+///   the FileSpec of the trace file on disk.
+static Expected<Optional<FileSpec>>
+WriteContextSwitchTrace(TraceIntelPT &trace_ipt, lldb::cpu_id_t cpu_id,
+                        const FileSpec &cpus_dir, bool compact) {
+  FileSpec output_context_switch_trace = cpus_dir;
+  output_context_switch_trace.AppendPathComponent(std::to_string(cpu_id) +
+                                                  ".perf_context_switch_trace");
+
+  bool should_skip = false;
+
+  Error err = trace_ipt.OnCpuBinaryDataRead(
+      cpu_id, IntelPTDataKinds::kPerfContextSwitchTrace,
+      [&](llvm::ArrayRef<uint8_t> data) -> llvm::Error {
+        if (!compact)
+          return WriteBytesToDisk(output_context_switch_trace, data);
+
+        std::set<lldb::pid_t> pids;
+        for (Process *process : trace_ipt.GetAllProcesses())
+          pids.insert(process->GetID());
+
+        Expected<std::vector<uint8_t>> compact_context_switch_trace =
+            FilterProcessesFromContextSwitchTrace(data, pids);
+        if (!compact_context_switch_trace)
+          return compact_context_switch_trace.takeError();
+
+        if (compact_context_switch_trace->empty()) {
+          should_skip = true;
+          return Error::success();
+        }
+
+        return WriteBytesToDisk(output_context_switch_trace,
+                                *compact_context_switch_trace);
+      });
+  if (err)
+    return std::move(err);
+
+  if (should_skip)
+    return None;
+  return output_context_switch_trace;
+}
+
+static Expected<FileSpec> WriteIntelPTTrace(TraceIntelPT &trace_ipt,
+                                            lldb::cpu_id_t cpu_id,
+                                            const FileSpec &cpus_dir) {
+  FileSpec output_trace = cpus_dir;
+  output_trace.AppendPathComponent(std::to_string(cpu_id) + ".intelpt_trace");
+
+  Error err = trace_ipt.OnCpuBinaryDataRead(
+      cpu_id, IntelPTDataKinds::kIptTrace,
+      [&](llvm::ArrayRef<uint8_t> data) -> llvm::Error {
+        return WriteBytesToDisk(output_trace, data);
+      });
+  if (err)
+    return std::move(err);
+  return output_trace;
+}
+
 static llvm::Expected<llvm::Optional<std::vector<JSONCpu>>>
-BuildCpusSection(TraceIntelPT &trace_ipt, FileSpec directory) {
+BuildCpusSection(TraceIntelPT &trace_ipt, FileSpec directory, bool compact) {
   if (trace_ipt.GetTracedCpus().empty())
     return None;
 
@@ -135,36 +205,21 @@ BuildCpusSection(TraceIntelPT &trace_ipt, FileSpec directory) {
   for (lldb::cpu_id_t cpu_id : trace_ipt.GetTracedCpus()) {
     JSONCpu json_cpu;
     json_cpu.id = cpu_id;
+    Expected<Optional<FileSpec>> context_switch_trace_path =
+        WriteContextSwitchTrace(trace_ipt, cpu_id, cpus_dir, compact);
+    if (!context_switch_trace_path)
+      return context_switch_trace_path.takeError();
+    if (!*context_switch_trace_path)
+      continue;
+    json_cpu.context_switch_trace =
+        GetRelativePath(directory, **context_switch_trace_path);
 
-    {
-      FileSpec output_trace = cpus_dir;
-      output_trace.AppendPathComponent(std::to_string(cpu_id) +
-                                       ".intelpt_trace");
-      json_cpu.ipt_trace = output_trace.GetPath();
-
-      llvm::Error err = trace_ipt.OnCpuBinaryDataRead(
-          cpu_id, IntelPTDataKinds::kIptTrace,
-          [&](llvm::ArrayRef<uint8_t> data) -> llvm::Error {
-            return WriteBytesToDisk(output_trace, data);
-          });
-      if (err)
-        return std::move(err);
-    }
-
-    {
-      FileSpec output_context_switch_trace = cpus_dir;
-      output_context_switch_trace.AppendPathComponent(
-          std::to_string(cpu_id) + ".perf_context_switch_trace");
-      json_cpu.context_switch_trace = output_context_switch_trace.GetPath();
+    if (Expected<FileSpec> ipt_trace_path =
+            WriteIntelPTTrace(trace_ipt, cpu_id, cpus_dir))
+      json_cpu.ipt_trace = GetRelativePath(directory, *ipt_trace_path);
+    else
+      return ipt_trace_path.takeError();
 
-      llvm::Error err = trace_ipt.OnCpuBinaryDataRead(
-          cpu_id, IntelPTDataKinds::kPerfContextSwitchTrace,
-          [&](llvm::ArrayRef<uint8_t> data) -> llvm::Error {
-            return WriteBytesToDisk(output_context_switch_trace, data);
-          });
-      if (err)
-        return std::move(err);
-    }
     json_cpus.push_back(std::move(json_cpu));
   }
   return json_cpus;
@@ -222,14 +277,14 @@ BuildModulesSection(Process &process, FileSpec directory) {
     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()))
+    if (std::error_code ec =
+            llvm::sys::fs::copy_file(file, path_to_copy_module.GetPath()))
       return createStringError(
           inconvertibleErrorCode(),
           formatv("couldn't write to the file. {0}", ec.message()));
 
     json_modules.push_back(
-        JSONModule{system_path, path_to_copy_module.GetPath(),
+        JSONModule{system_path, GetRelativePath(directory, path_to_copy_module),
                    JSONUINT64{load_addr}, module_sp->GetUUID().GetAsString()});
   }
   return json_modules;
@@ -280,8 +335,9 @@ BuildProcessesSection(TraceIntelPT &trace_ipt, const FileSpec &directory) {
   return processes;
 }
 
-Error TraceIntelPTBundleSaver::SaveToDisk(TraceIntelPT &trace_ipt,
-                                           FileSpec directory) {
+Expected<FileSpec> TraceIntelPTBundleSaver::SaveToDisk(TraceIntelPT &trace_ipt,
+                                                       FileSpec directory,
+                                                       bool compact) {
   if (std::error_code ec =
           sys::fs::create_directories(directory.GetPath().c_str()))
     return llvm::errorCodeToError(ec);
@@ -299,7 +355,7 @@ Error TraceIntelPTBundleSaver::SaveToDisk(TraceIntelPT &trace_ipt,
     return json_processes.takeError();
 
   Expected<Optional<std::vector<JSONCpu>>> json_cpus =
-      BuildCpusSection(trace_ipt, directory);
+      BuildCpusSection(trace_ipt, directory, compact);
   if (!json_cpus)
     return json_cpus.takeError();
 

diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.h
index c36677e1c00d8..7224636f0c749 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.h
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.h
@@ -31,10 +31,16 @@ class TraceIntelPTBundleSaver {
   /// \param[in] directory
   ///     The directory where the trace bundle will be created.
   ///
+  /// \param[in] compact
+  ///     Filter out information irrelevant to the traced processes in the
+  ///     context switch and intel pt traces when using per-cpu mode. This
+  ///     effectively reduces the size of those traces.
+  ///
   /// \return
-  ///     \a llvm::success if the operation was successful, or an \a llvm::Error
-  ///     otherwise.
-  llvm::Error SaveToDisk(TraceIntelPT &trace_ipt, FileSpec directory);
+  ///   A \a FileSpec pointing to the bundle description file, or an \a
+  ///   llvm::Error otherwise.
+  llvm::Expected<FileSpec> SaveToDisk(TraceIntelPT &trace_ipt,
+                                      FileSpec directory, bool compact);
 };
 
 } // namespace trace_intel_pt

diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td
index 15d169551db0b..6f30e0b91c8c0 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td
@@ -90,13 +90,3 @@ let Command = "process trace start intel pt" in {
          "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.">;
-}

diff  --git a/lldb/test/API/commands/trace/TestTraceLoad.py b/lldb/test/API/commands/trace/TestTraceLoad.py
index 80cfef3aad2ed..1699600c9e73a 100644
--- a/lldb/test/API/commands/trace/TestTraceLoad.py
+++ b/lldb/test/API/commands/trace/TestTraceLoad.py
@@ -20,6 +20,39 @@ def testLoadMultiCoreTrace(self):
           substrs=["67911: [tsc=40450075477799536] 0x0000000000400bd7    addl   $0x1, -0x4(%rbp)",
                    "m.out`bar() + 26 at multi_thread.cpp:20:6"])
 
+    @testSBAPIAndCommands
+    def testLoadCompactMultiCoreTrace(self):
+        src_dir = self.getSourceDir()
+        trace_description_file_path = os.path.join(src_dir, "intelpt-multi-core-trace", "trace.json")
+        self.traceLoad(traceDescriptionFilePath=trace_description_file_path, substrs=["intel-pt"])
+
+        self.expect("thread trace dump info 2", substrs=["Total number of continuous executions found: 153"])
+
+        # we'll save the trace in compact format
+        compact_trace_bundle_dir = os.path.join(self.getBuildDir(), "intelpt-multi-core-trace-compact")
+        self.traceSave(compact_trace_bundle_dir, compact=True)
+
+        # we'll delete the previous target and make sure it's trace object is deleted
+        self.dbg.DeleteTarget(self.dbg.GetTargetAtIndex(0))
+        self.expect("thread trace dump instructions 2 -t", substrs=["error: invalid target"], error=True)
+
+        # we'll load the compact trace and make sure it works
+        self.traceLoad(os.path.join(compact_trace_bundle_dir, "trace.json"), substrs=["intel-pt"])
+        self.expect("thread trace dump instructions 2 -t",
+          substrs=["19522: [tsc=40450075478109270] (error) expected tracing enabled event",
+                   "m.out`foo() + 65 at multi_thread.cpp:12:21",
+                   "19520: [tsc=40450075477657246] 0x0000000000400ba7    jg     0x400bb3"])
+        self.expect("thread trace dump instructions 3 -t",
+          substrs=["67911: [tsc=40450075477799536] 0x0000000000400bd7    addl   $0x1, -0x4(%rbp)",
+                   "m.out`bar() + 26 at multi_thread.cpp:20:6"])
+
+        # This reduced the number of continuous executions to look at
+        self.expect("thread trace dump info 2", substrs=["Total number of continuous executions found: 3"])
+
+        # We clean up for the next run of this test
+        self.dbg.DeleteTarget(self.dbg.GetTargetAtIndex(0))
+
+
     @testSBAPIAndCommands
     def testLoadMultiCoreTraceWithStringNumbers(self):
         src_dir = self.getSourceDir()

diff  --git a/lldb/test/API/commands/trace/TestTraceSave.py b/lldb/test/API/commands/trace/TestTraceSave.py
index b5212ef1b13f6..8c4a0c30f2b91 100644
--- a/lldb/test/API/commands/trace/TestTraceSave.py
+++ b/lldb/test/API/commands/trace/TestTraceSave.py
@@ -14,7 +14,7 @@ class TestTraceSave(TraceIntelPTTestCaseBase):
 
     def testErrorMessages(self):
         # We first check the output when there are no targets
-        self.expect("process trace save",
+        self.expect("trace save",
             substrs=["error: invalid target, create a target using the 'target create' command"],
             error=True)
 
@@ -22,7 +22,7 @@ def testErrorMessages(self):
         self.expect("target create " +
             os.path.join(self.getSourceDir(), "intelpt-trace", "a.out"))
 
-        self.expect("process trace save",
+        self.expect("trace save",
             substrs=["error: Command requires a current process."],
             error=True)
 
@@ -30,7 +30,7 @@ def testErrorMessages(self):
         self.expect("b main")
         self.expect("run")
 
-        self.expect("process trace save",
+        self.expect("trace save",
             substrs=["error: Process is not being traced"],
             error=True)
 
@@ -43,12 +43,12 @@ def testSaveToInvalidDir(self):
         self.expect("n")
 
         # Check the output when saving without providing the directory argument
-        self.expect("process trace save -d",
-            substrs=["error: last option requires an argument"],
+        self.expect("trace save ",
+            substrs=["error: a single path to a directory where the trace bundle will be created is required"],
             error=True)
 
         # Check the output when saving to an invalid directory
-        self.expect("process trace save -d /",
+        self.expect("trace save /",
             substrs=["error: couldn't write to the file"],
             error=True)
 
@@ -58,7 +58,7 @@ def testSaveWhenNotLiveTrace(self):
             substrs=["intel-pt"])
 
         # Check the output when not doing live tracing
-        self.expect("process trace save -d " +
+        self.expect("trace save " +
             os.path.join(self.getBuildDir(), "intelpt-trace", "trace_not_live_dir"))
 
     def testSaveMultiCpuTrace(self):
@@ -76,7 +76,7 @@ def testSaveMultiCpuTrace(self):
         self.expect("b 7")
 
         output_dir = os.path.join(self.getBuildDir(), "intelpt-trace", "trace_save")
-        self.expect("process trace save -d " + output_dir)
+        self.expect("trace save " + output_dir)
 
         def checkSessionBundle(session_file_path):
             with open(session_file_path) as session_file:
@@ -107,7 +107,7 @@ def checkSessionBundle(session_file_path):
         output_dir = os.path.join(self.getBuildDir(), "intelpt-trace", "trace_save")
         self.expect("trace load " + os.path.join(output_dir, "trace.json"))
         output_copy_dir = os.path.join(self.getBuildDir(), "intelpt-trace", "copy_trace_save")
-        self.expect("process trace save -d " + output_copy_dir)
+        self.expect("trace save " + output_copy_dir)
 
         # We now check that the new bundle is correct on its own
         copied_trace_session_file = os.path.join(output_copy_dir, "trace.json")
@@ -153,7 +153,7 @@ def testSaveTrace(self):
         last_ten_instructions = res.GetOutput()
 
         # Now, save the trace to <trace_copy_dir>
-        self.expect("process trace save -d " +
+        self.expect("trace save " +
             os.path.join(self.getBuildDir(), "intelpt-trace", "trace_copy_dir"))
 
         # Load the trace just saved


        


More information about the lldb-commits mailing list