[Lldb-commits] [lldb] fc5ef57 - [trace][intelpt] Support system-wide tracing [12] - Support multi-core trace load and save

Walter Erquinigo via lldb-commits lldb-commits at lists.llvm.org
Wed Jun 15 13:28:44 PDT 2022


Author: Walter Erquinigo
Date: 2022-06-15T13:28:36-07:00
New Revision: fc5ef57c7df5805c828de2e3e334c2cf74648e58

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

LOG: [trace][intelpt] Support system-wide tracing [12] - Support multi-core trace load and save

:q!
This diff is massive, but it's because it connects the client with lldb-server
and also ensures that the postmortem case works.

- Flatten the postmortem trace schema. The reason is that the schema has become quite complex due to the new multicore case, which defeats the original purpose of having a schema that could work for every trace plug-in. At this point, it's better that each trace plug-in defines it's own full schema. This means that the only common field is "type".
-- Because of this new approach, I merged the "common" trace load and saving functionalities into the IntelPT one. This simplified the code quite a bit. If we eventually implement another trace plug-in, we can see then what we could reuse.
-- The new schema, which is flattened, has now better comments and is parsed better. A change I did was to disallow hex addresses, because they are a bit error prone. I'm asking now to print the address in decimal.
-- Renamed "intel" to "GenuineIntel" in the schema because that's what you see in /proc/cpuinfo.
- Implemented reading the context switch trace data buffer. I had to do
some refactors to do that cleanly.
-- A major change that I did here was to simplify the perf_event circular buffer reading logic. It was too complex. Maybe the original Intel author had something different in mind.
- Implemented all the necessary bits to read trace.json files with per-core data.
- Implemented all the necessary bits to save to disk per-core trace session.
- Added a test that ensures that parsing and saving to disk works.

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

Added: 
    

Modified: 
    lldb/docs/lldb-gdb-remote.txt
    lldb/docs/use/intel_pt.rst
    lldb/include/lldb/Target/Trace.h
    lldb/include/lldb/Utility/TraceGDBRemotePackets.h
    lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp
    lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.cpp
    lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.h
    lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.cpp
    lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.h
    lldb/source/Plugins/Process/Linux/IntelPTProcessTrace.h
    lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.cpp
    lldb/source/Plugins/Process/Linux/IntelPTThreadTraceCollection.cpp
    lldb/source/Plugins/Process/Linux/IntelPTThreadTraceCollection.h
    lldb/source/Plugins/Process/Linux/Perf.cpp
    lldb/source/Plugins/Process/Linux/Perf.h
    lldb/source/Plugins/Trace/common/CMakeLists.txt
    lldb/source/Plugins/Trace/common/ThreadPostMortemTrace.cpp
    lldb/source/Plugins/Trace/common/ThreadPostMortemTrace.h
    lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
    lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
    lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp
    lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h
    lldb/source/Plugins/Trace/intel-pt/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/source/Target/Trace.cpp
    lldb/source/Utility/TraceGDBRemotePackets.cpp
    lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp
    lldb/test/API/commands/trace/TestTraceLoad.py
    lldb/test/API/commands/trace/TestTraceSave.py
    lldb/test/API/commands/trace/TestTraceSchema.py
    lldb/test/API/commands/trace/intelpt-trace-multi-file/multi-file-no-ld.json
    lldb/test/API/commands/trace/intelpt-trace/trace.json
    lldb/test/API/commands/trace/intelpt-trace/trace_2threads.json
    lldb/test/API/commands/trace/intelpt-trace/trace_bad.json
    lldb/test/API/commands/trace/intelpt-trace/trace_bad2.json
    lldb/test/API/commands/trace/intelpt-trace/trace_bad3.json
    lldb/test/API/commands/trace/intelpt-trace/trace_bad4.json
    lldb/test/API/commands/trace/intelpt-trace/trace_bad5.json
    lldb/test/API/commands/trace/intelpt-trace/trace_bad_image.json
    lldb/test/API/commands/trace/intelpt-trace/trace_wrong_cpu.json
    lldb/unittests/Process/Linux/PerfTests.cpp
    lldb/unittests/Utility/TraceGDBRemotePacketsTest.cpp

Removed: 
    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/common/TraceSessionSaver.cpp
    lldb/source/Plugins/Trace/common/TraceSessionSaver.h


################################################################################
diff  --git a/lldb/docs/lldb-gdb-remote.txt b/lldb/docs/lldb-gdb-remote.txt
index 1996e3f8fac9d..e62707bd7db4d 100644
--- a/lldb/docs/lldb-gdb-remote.txt
+++ b/lldb/docs/lldb-gdb-remote.txt
@@ -534,7 +534,7 @@ read packet: OK/E<error code>;AAAAAAAAA
 //    "tscPerfZeroConversion": {
 //      "timeMult": <decimal integer>,
 //      "timeShift": <decimal integer>,
-//      "timeSero": <decimal integer>,
+//      "timeZero": <decimal integer>,
 //    }
 //----------------------------------------------------------------------
 

diff  --git a/lldb/docs/use/intel_pt.rst b/lldb/docs/use/intel_pt.rst
index 8b80b4a25b75b..ae6f72011ace4 100644
--- a/lldb/docs/use/intel_pt.rst
+++ b/lldb/docs/use/intel_pt.rst
@@ -182,14 +182,12 @@ For example
 ::
 
   {
-    "trace": {
-      "type": "intel-pt",
-      "pt_cpu": {
-        "vendor": "intel",
-        "family": 6,
-        "model": 79,
-        "stepping": 1
-      }
+    "type": "intel-pt",
+    "cpuInfo": {
+      "vendor": "GenuineIntel",
+      "family": 6,
+      "model": 79,
+      "stepping": 1
     },
     "processes": [
       {
@@ -198,14 +196,14 @@ For example
         "threads": [
           {
             "tid": 815455,
-            "traceFile": "trace.file" # raw thread-specific trace from the AUX buffer
+            "traceBuffer": "trace.file" # raw thread-specific trace from the AUX buffer
           }
         ],
         "modules": [ # this are all the shared libraries + the main executable
           {
             "file": "a.out", # optional if it's the same as systemPath
             "systemPath": "a.out",
-            "loadAddress": "0x0000000000400000",
+            "loadAddress": 4194304,
           },
           {
             "file": "libfoo.so",
@@ -251,4 +249,4 @@ References
 - Some details about how Meta is using Intel Processor Trace can be found in this blog_ post.
 
 .. _document: https://docs.google.com/document/d/1cOVTGp1sL_HBXjP9eB7qjVtDNr5xnuZvUUtv43G5eVI
-.. _blog: https://engineering.fb.com/2021/04/27/developer-tools/reverse-debugging/
\ No newline at end of file
+.. _blog: https://engineering.fb.com/2021/04/27/developer-tools/reverse-debugging/

diff  --git a/lldb/include/lldb/Target/Trace.h b/lldb/include/lldb/Target/Trace.h
index 88af9d3b221dc..534c87a2d5b6c 100644
--- a/lldb/include/lldb/Target/Trace.h
+++ b/lldb/include/lldb/Target/Trace.h
@@ -240,6 +240,7 @@ class Trace : public PluginInterface,
 
   using OnBinaryDataReadCallback =
       std::function<llvm::Error(llvm::ArrayRef<uint8_t> data)>;
+
   /// Fetch binary data associated with a thread, either live or postmortem, and
   /// pass it to the given callback. The reason of having a callback is to free
   /// the caller from having to manage the life cycle of the data and to hide
@@ -265,23 +266,77 @@ class Trace : public PluginInterface,
   llvm::Error OnThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind,
                                      OnBinaryDataReadCallback callback);
 
-  /// Get the current traced live process.
+  /// Fetch binary data associated with a core, either live or postmortem, and
+  /// pass it to the given callback. The reason of having a callback is to free
+  /// the caller from having to manage the life cycle of the data and to hide
+  /// the 
diff erent data fetching procedures that exist for live and post mortem
+  /// cores.
+  ///
+  /// The fetched data is not persisted after the callback is invoked.
+  ///
+  /// \param[in] core_id
+  ///     The core who owns the data.
+  ///
+  /// \param[in] kind
+  ///     The kind of data to read.
+  ///
+  /// \param[in] callback
+  ///     The callback to be invoked once the data was successfully read. Its
+  ///     return value, which is an \a llvm::Error, is returned by this
+  ///     function.
   ///
   /// \return
-  ///     The current traced live process. If it's not a live process,
-  ///     return \a nullptr.
-  Process *GetLiveProcess();
+  ///     An \a llvm::Error if the data couldn't be fetched, or the return value
+  ///     of the callback, otherwise.
+  llvm::Error OnCoreBinaryDataRead(lldb::core_id_t core_id,
+                                   llvm::StringRef kind,
+                                   OnBinaryDataReadCallback callback);
+
+  /// \return
+  ///     All the currently traced processes.
+  std::vector<Process *> GetAllProcesses();
+
+  /// \return
+  ///     The list of cores being traced. Might be empty depending on the
+  ///     plugin.
+  llvm::ArrayRef<lldb::core_id_t> GetTracedCores();
 
 protected:
+  /// Get the currently traced live process.
+  ///
+  /// \return
+  ///     If it's not a live process, return \a nullptr.
+  Process *GetLiveProcess();
+
+  /// Get the currently traced postmortem processes.
+  ///
+  /// \return
+  ///     If it's not a live process session, return an empty list.
+  llvm::ArrayRef<Process *> GetPostMortemProcesses();
+
   /// Implementation of \a OnThreadBinaryDataRead() for live threads.
   llvm::Error OnLiveThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind,
                                          OnBinaryDataReadCallback callback);
 
+  /// Implementation of \a OnLiveBinaryDataRead() for live cores.
+  llvm::Error OnLiveCoreBinaryDataRead(lldb::core_id_t core,
+                                       llvm::StringRef kind,
+                                       OnBinaryDataReadCallback callback);
+
   /// Implementation of \a OnThreadBinaryDataRead() for post mortem threads.
   llvm::Error
   OnPostMortemThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind,
                                    OnBinaryDataReadCallback callback);
 
+  /// Implementation of \a OnCoreBinaryDataRead() for post mortem cores.
+  llvm::Error OnPostMortemCoreBinaryDataRead(lldb::core_id_t core_id,
+                                             llvm::StringRef kind,
+                                             OnBinaryDataReadCallback callback);
+
+  /// Helper method for reading a data file and passing its data to the given
+  /// callback.
+  llvm::Error OnDataFileRead(FileSpec file, OnBinaryDataReadCallback callback);
+
   /// Get the file path containing data of a postmortem thread given a data
   /// identifier.
   ///
@@ -297,6 +352,21 @@ class Trace : public PluginInterface,
   llvm::Expected<FileSpec> GetPostMortemThreadDataFile(lldb::tid_t tid,
                                                        llvm::StringRef kind);
 
+  /// Get the file path containing data of a postmortem core given a data
+  /// identifier.
+  ///
+  /// \param[in] core_id
+  ///     The core whose data is requested.
+  ///
+  /// \param[in] kind
+  ///     The kind of data requested.
+  ///
+  /// \return
+  ///     The file spec containing the requested data, or an \a llvm::Error in
+  ///     case of failures.
+  llvm::Expected<FileSpec> GetPostMortemCoreDataFile(lldb::core_id_t core_id,
+                                                     llvm::StringRef kind);
+
   /// Associate a given thread with a data file using a data identifier.
   ///
   /// \param[in] tid
@@ -310,6 +380,19 @@ class Trace : public PluginInterface,
   void SetPostMortemThreadDataFile(lldb::tid_t tid, llvm::StringRef kind,
                                    FileSpec file_spec);
 
+  /// Associate a given core with a data file using a data identifier.
+  ///
+  /// \param[in] core_id
+  ///     The core associated with the data file.
+  ///
+  /// \param[in] kind
+  ///     The kind of data being registered.
+  ///
+  /// \param[in] file_spec
+  ///     The path of the data file.
+  void SetPostMortemCoreDataFile(lldb::core_id_t core_id, llvm::StringRef kind,
+                                 FileSpec file_spec);
+
   /// Get binary data of a live thread given a data identifier.
   ///
   /// \param[in] tid
@@ -324,6 +407,20 @@ class Trace : public PluginInterface,
   llvm::Expected<std::vector<uint8_t>>
   GetLiveThreadBinaryData(lldb::tid_t tid, llvm::StringRef kind);
 
+  /// Get binary data of a live core given a data identifier.
+  ///
+  /// \param[in] core_id
+  ///     The core whose data is requested.
+  ///
+  /// \param[in] kind
+  ///     The kind of data requested.
+  ///
+  /// \return
+  ///     A vector of bytes with the requested data, or an \a llvm::Error in
+  ///     case of failures.
+  llvm::Expected<std::vector<uint8_t>>
+  GetLiveCoreBinaryData(lldb::core_id_t core_id, llvm::StringRef kind);
+
   /// Get binary data of the current process given a data identifier.
   ///
   /// \param[in] kind
@@ -339,10 +436,16 @@ class Trace : public PluginInterface,
   llvm::Optional<size_t> GetLiveThreadBinaryDataSize(lldb::tid_t tid,
                                                      llvm::StringRef kind);
 
+  /// Get the size of the data returned by \a GetLiveCoreBinaryData
+  llvm::Optional<size_t> GetLiveCoreBinaryDataSize(lldb::core_id_t core_id,
+                                                   llvm::StringRef kind);
+
   /// Get the size of the data returned by \a GetLiveProcessBinaryData
   llvm::Optional<size_t> GetLiveProcessBinaryDataSize(llvm::StringRef kind);
+
   /// Constructor for post mortem processes
-  Trace() = default;
+  Trace(llvm::ArrayRef<lldb::ProcessSP> postmortem_processes,
+        llvm::Optional<std::vector<lldb::core_id_t>> postmortem_cores);
 
   /// Constructor for a live process
   Trace(Process &live_process) : m_live_process(&live_process) {}
@@ -400,6 +503,10 @@ class Trace : public PluginInterface,
   /// Process traced by this object if doing live tracing. Otherwise it's null.
   Process *m_live_process = nullptr;
 
+  /// Portmortem processes traced by this object if doing non-live tracing.
+  /// Otherwise it's empty.
+  std::vector<Process *> m_postmortem_processes;
+
   /// These data kinds are returned by lldb-server when fetching the state of
   /// the tracing session. The size in bytes can be used later for fetching the
   /// data in batches.
@@ -409,16 +516,30 @@ class Trace : public PluginInterface,
   llvm::DenseMap<lldb::tid_t, std::unordered_map<std::string, size_t>>
       m_live_thread_data;
 
+  /// core id -> data kind -> size
+  llvm::DenseMap<lldb::core_id_t, std::unordered_map<std::string, size_t>>
+      m_live_core_data;
   /// data kind -> size
   std::unordered_map<std::string, size_t> m_live_process_data;
   /// \}
 
+  /// The list of cores being traced. Might be \b None depending on the plug-in.
+  llvm::Optional<std::vector<lldb::core_id_t>> m_cores;
+
   /// Postmortem traces can specific additional data files, which are
   /// represented in this variable using a data kind identifier for each file.
+  /// \{
+
   /// tid -> data kind -> file
   llvm::DenseMap<lldb::tid_t, std::unordered_map<std::string, FileSpec>>
       m_postmortem_thread_data;
 
+  /// core id -> data kind -> file
+  llvm::DenseMap<lldb::core_id_t, std::unordered_map<std::string, FileSpec>>
+      m_postmortem_core_data;
+
+  /// \}
+
   llvm::Optional<std::string> m_live_refresh_error;
 };
 

diff  --git a/lldb/include/lldb/Utility/TraceGDBRemotePackets.h b/lldb/include/lldb/Utility/TraceGDBRemotePackets.h
index d7b5a37e4cccb..2854f2f48129d 100644
--- a/lldb/include/lldb/Utility/TraceGDBRemotePackets.h
+++ b/lldb/include/lldb/Utility/TraceGDBRemotePackets.h
@@ -133,7 +133,9 @@ struct TraceGetStateResponse {
   std::vector<TraceThreadState> traced_threads;
   std::vector<TraceBinaryData> process_binary_data;
   llvm::Optional<std::vector<TraceCoreState>> cores;
-  std::vector<std::string> warnings;
+  llvm::Optional<std::vector<std::string>> warnings;
+
+  void AddWarning(llvm::StringRef warning);
 };
 
 bool fromJSON(const llvm::json::Value &value, TraceGetStateResponse &packet,
@@ -151,6 +153,8 @@ struct TraceGetBinaryDataRequest {
   std::string kind;
   /// Optional tid if the data is related to a thread.
   llvm::Optional<lldb::tid_t> tid;
+  /// Optional core id if the data is related to a cpu core.
+  llvm::Optional<lldb::tid_t> core_id;
   /// Offset in bytes from where to start reading the data.
   uint64_t offset;
   /// Number of bytes to read.

diff  --git a/lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp b/lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp
index 29ff87f5b4df1..a36d6dcce6fad 100644
--- a/lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp
+++ b/lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp
@@ -182,32 +182,39 @@ Expected<json::Value> IntelPTCollector::GetState() {
           FetchPerfTscConversionParameters())
     state.tsc_perf_zero_conversion = *tsc_conversion;
   else
-    state.warnings.push_back(toString(tsc_conversion.takeError()));
+    state.AddWarning(toString(tsc_conversion.takeError()));
   return toJSON(state);
 }
 
 Expected<std::vector<uint8_t>>
 IntelPTCollector::GetBinaryData(const TraceGetBinaryDataRequest &request) {
-  if (request.kind == IntelPTDataKinds::kTraceBuffer) {
-    if (!request.tid)
-      return createStringError(
-          inconvertibleErrorCode(),
-          "Getting a trace buffer without a tid is currently unsupported");
+  if (request.kind == IntelPTDataKinds::kProcFsCpuInfo)
+    return GetProcfsCpuInfo();
 
-    if (m_process_trace_up && m_process_trace_up->TracesThread(*request.tid))
-      return m_process_trace_up->GetBinaryData(request);
+  if (m_process_trace_up) {
+    Expected<Optional<std::vector<uint8_t>>> data =
+        m_process_trace_up->TryGetBinaryData(request);
+    if (!data)
+      return data.takeError();
+    if (*data)
+      return **data;
+  }
 
-    if (Expected<IntelPTSingleBufferTrace &> trace =
-            m_thread_traces.GetTracedThread(*request.tid))
-      return trace->GetTraceBuffer(request.offset, request.size);
-    else
-      return trace.takeError();
-  } else if (request.kind == IntelPTDataKinds::kProcFsCpuInfo) {
-    return GetProcfsCpuInfo();
+  {
+    Expected<Optional<std::vector<uint8_t>>> data =
+        m_thread_traces.TryGetBinaryData(request);
+    if (!data)
+      return data.takeError();
+    if (*data)
+      return **data;
   }
-  return createStringError(inconvertibleErrorCode(),
-                           "Unsuported trace binary data kind: %s",
-                           request.kind.c_str());
+
+  return createStringError(
+      inconvertibleErrorCode(),
+      formatv("Can't fetch data kind {0} for core_id {1}, tid {2} and "
+              "\"process tracing\" mode {3}",
+              request.kind, request.core_id, request.tid,
+              m_process_trace_up ? "enabled" : "not enabled"));
 }
 
 bool IntelPTCollector::IsSupported() {

diff  --git a/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.cpp b/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.cpp
index ea58cf6a6e315..572a867ed4c18 100644
--- a/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.cpp
+++ b/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.cpp
@@ -70,8 +70,8 @@ static Expected<PerfEvent> CreateContextSwitchTracePerfEvent(
   if (Expected<PerfEvent> perf_event = PerfEvent::Init(
           attr, /*pid=*/None, core_id,
           intelpt_core_trace.GetPerfEvent().GetFd(), /*flags=*/0)) {
-    if (Error mmap_err =
-            perf_event->MmapMetadataAndBuffers(data_buffer_numpages, 0)) {
+    if (Error mmap_err = perf_event->MmapMetadataAndBuffers(
+            data_buffer_numpages, 0, /*data_buffer_write=*/false)) {
       return std::move(mmap_err);
     }
     return perf_event;
@@ -190,7 +190,21 @@ Error IntelPTMultiCoreTrace::TraceStop(lldb::tid_t tid) {
                            "per-core process tracing is enabled.");
 }
 
-Expected<std::vector<uint8_t>>
-IntelPTMultiCoreTrace::GetBinaryData(const TraceGetBinaryDataRequest &request) {
-  return createStringError(inconvertibleErrorCode(), "Unimplemented");
+Expected<Optional<std::vector<uint8_t>>>
+IntelPTMultiCoreTrace::TryGetBinaryData(
+    const TraceGetBinaryDataRequest &request) {
+  if (!request.core_id)
+    return None;
+  auto it = m_traces_per_core.find(*request.core_id);
+  if (it == m_traces_per_core.end())
+    return createStringError(
+        inconvertibleErrorCode(),
+        formatv("Core {0} is not being traced", *request.core_id));
+
+  if (request.kind == IntelPTDataKinds::kTraceBuffer)
+    return it->second.first.GetTraceBuffer(request.offset, request.size);
+  if (request.kind == IntelPTDataKinds::kPerfContextSwitchTrace)
+    return it->second.second.ReadFlushedOutDataCyclicBuffer(request.offset,
+                                                            request.size);
+  return None;
 }

diff  --git a/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.h b/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.h
index 30fb5dea8075d..ff5693938614e 100644
--- a/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.h
+++ b/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.h
@@ -80,8 +80,8 @@ class IntelPTMultiCoreTrace : public IntelPTProcessTrace {
 
   llvm::Error TraceStop(lldb::tid_t tid) override;
 
-  llvm::Expected<std::vector<uint8_t>>
-  GetBinaryData(const TraceGetBinaryDataRequest &request) override;
+  llvm::Expected<llvm::Optional<std::vector<uint8_t>>>
+  TryGetBinaryData(const TraceGetBinaryDataRequest &request) override;
 
 private:
   /// This assumes that all underlying perf_events for each core are part of the

diff  --git a/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.cpp b/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.cpp
index 1545b213851a7..0346233f41315 100644
--- a/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.cpp
+++ b/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.cpp
@@ -46,13 +46,10 @@ TraceIntelPTGetStateResponse IntelPTPerThreadProcessTrace::GetState() {
   return state;
 }
 
-Expected<std::vector<uint8_t>> IntelPTPerThreadProcessTrace::GetBinaryData(
+Expected<llvm::Optional<std::vector<uint8_t>>>
+IntelPTPerThreadProcessTrace::TryGetBinaryData(
     const TraceGetBinaryDataRequest &request) {
-  if (Expected<IntelPTSingleBufferTrace &> trace =
-          m_thread_traces.GetTracedThread(*request.tid))
-    return trace->GetTraceBuffer(request.offset, request.size);
-  else
-    return trace.takeError();
+  return m_thread_traces.TryGetBinaryData(request);
 }
 
 Expected<IntelPTProcessTraceUP>

diff  --git a/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.h b/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.h
index f1ae8ef16954b..2f28a12fc4f60 100644
--- a/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.h
+++ b/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.h
@@ -44,8 +44,8 @@ class IntelPTPerThreadProcessTrace : public IntelPTProcessTrace {
 
   TraceIntelPTGetStateResponse GetState() override;
 
-  llvm::Expected<std::vector<uint8_t>>
-  GetBinaryData(const TraceGetBinaryDataRequest &request) override;
+  llvm::Expected<llvm::Optional<std::vector<uint8_t>>>
+  TryGetBinaryData(const TraceGetBinaryDataRequest &request) override;
 
 private:
   IntelPTPerThreadProcessTrace(const TraceIntelPTStartRequest &request)

diff  --git a/lldb/source/Plugins/Process/Linux/IntelPTProcessTrace.h b/lldb/source/Plugins/Process/Linux/IntelPTProcessTrace.h
index 06fc346e92db1..1c6b44e2395bb 100644
--- a/lldb/source/Plugins/Process/Linux/IntelPTProcessTrace.h
+++ b/lldb/source/Plugins/Process/Linux/IntelPTProcessTrace.h
@@ -36,9 +36,12 @@ class IntelPTProcessTrace {
   /// \copydoc IntelPTThreadTraceCollection::TraceStop()
   virtual llvm::Error TraceStop(lldb::tid_t tid) = 0;
 
-  /// Get binary data owned by this instance.
-  virtual llvm::Expected<std::vector<uint8_t>>
-  GetBinaryData(const TraceGetBinaryDataRequest &request) = 0;
+  /// \return
+  ///   \b None if this instance doesn't support the requested data, an \a
+  ///   llvm::Error if this isntance supports it but fails at fetching it, and
+  ///   \b Error::success() otherwise.
+  virtual llvm::Expected<llvm::Optional<std::vector<uint8_t>>>
+  TryGetBinaryData(const TraceGetBinaryDataRequest &request) = 0;
 };
 
 using IntelPTProcessTraceUP = std::unique_ptr<IntelPTProcessTrace>;

diff  --git a/lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.cpp b/lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.cpp
index 2d3aad52dbb12..8f7909b4072c7 100644
--- a/lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.cpp
+++ b/lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.cpp
@@ -270,7 +270,8 @@ IntelPTSingleBufferTrace::Start(const TraceIntelPTStartRequest &request,
 
   if (Expected<PerfEvent> perf_event = PerfEvent::Init(*attr, tid, core_id)) {
     if (Error mmap_err = perf_event->MmapMetadataAndBuffers(
-            /*num_data_pages=*/0, aux_buffer_numpages)) {
+            /*num_data_pages=*/0, aux_buffer_numpages,
+            /*data_buffer_write=*/true)) {
       return std::move(mmap_err);
     }
     return IntelPTSingleBufferTrace(std::move(*perf_event));

diff  --git a/lldb/source/Plugins/Process/Linux/IntelPTThreadTraceCollection.cpp b/lldb/source/Plugins/Process/Linux/IntelPTThreadTraceCollection.cpp
index d7206b193e8f6..bd06e033f1606 100644
--- a/lldb/source/Plugins/Process/Linux/IntelPTThreadTraceCollection.cpp
+++ b/lldb/source/Plugins/Process/Linux/IntelPTThreadTraceCollection.cpp
@@ -71,3 +71,21 @@ void IntelPTThreadTraceCollection::Clear() {
 size_t IntelPTThreadTraceCollection::GetTracedThreadsCount() const {
   return m_thread_traces.size();
 }
+
+llvm::Expected<llvm::Optional<std::vector<uint8_t>>>
+IntelPTThreadTraceCollection::TryGetBinaryData(
+    const TraceGetBinaryDataRequest &request) {
+  if (!request.tid)
+    return None;
+  if (request.kind != IntelPTDataKinds::kTraceBuffer)
+    return None;
+
+  if (!TracesThread(*request.tid))
+    return None;
+
+  if (Expected<IntelPTSingleBufferTrace &> trace =
+          GetTracedThread(*request.tid))
+    return trace->GetTraceBuffer(request.offset, request.size);
+  else
+    return trace.takeError();
+}

diff  --git a/lldb/source/Plugins/Process/Linux/IntelPTThreadTraceCollection.h b/lldb/source/Plugins/Process/Linux/IntelPTThreadTraceCollection.h
index f343e436d692a..8a6b5a26352ac 100644
--- a/lldb/source/Plugins/Process/Linux/IntelPTThreadTraceCollection.h
+++ b/lldb/source/Plugins/Process/Linux/IntelPTThreadTraceCollection.h
@@ -59,6 +59,10 @@ class IntelPTThreadTraceCollection {
 
   size_t GetTracedThreadsCount() const;
 
+  /// \copydoc IntelPTProcessTrace::TryGetBinaryData()
+  llvm::Expected<llvm::Optional<std::vector<uint8_t>>>
+  TryGetBinaryData(const TraceGetBinaryDataRequest &request);
+
 private:
   llvm::DenseMap<lldb::tid_t, IntelPTSingleBufferTrace> m_thread_traces;
   /// Total actual thread buffer size in bytes

diff  --git a/lldb/source/Plugins/Process/Linux/Perf.cpp b/lldb/source/Plugins/Process/Linux/Perf.cpp
index d11a54e702592..2caf5c1d6905d 100644
--- a/lldb/source/Plugins/Process/Linux/Perf.cpp
+++ b/lldb/source/Plugins/Process/Linux/Perf.cpp
@@ -24,54 +24,6 @@ using namespace lldb_private;
 using namespace process_linux;
 using namespace llvm;
 
-void lldb_private::process_linux::ReadCyclicBuffer(
-    llvm::MutableArrayRef<uint8_t> &dst, llvm::ArrayRef<uint8_t> src,
-    size_t src_cyc_index, size_t offset) {
-
-  Log *log = GetLog(POSIXLog::Trace);
-
-  if (dst.empty() || src.empty()) {
-    dst = dst.drop_back(dst.size());
-    return;
-  }
-
-  if (dst.data() == nullptr || src.data() == nullptr) {
-    dst = dst.drop_back(dst.size());
-    return;
-  }
-
-  if (src_cyc_index > src.size()) {
-    dst = dst.drop_back(dst.size());
-    return;
-  }
-
-  if (offset >= src.size()) {
-    LLDB_LOG(log, "Too Big offset ");
-    dst = dst.drop_back(dst.size());
-    return;
-  }
-
-  llvm::SmallVector<ArrayRef<uint8_t>, 2> parts = {
-      src.slice(src_cyc_index), src.take_front(src_cyc_index)};
-
-  if (offset > parts[0].size()) {
-    parts[1] = parts[1].slice(offset - parts[0].size());
-    parts[0] = parts[0].drop_back(parts[0].size());
-  } else if (offset == parts[0].size()) {
-    parts[0] = parts[0].drop_back(parts[0].size());
-  } else {
-    parts[0] = parts[0].slice(offset);
-  }
-  auto next = dst.begin();
-  auto bytes_left = dst.size();
-  for (auto part : parts) {
-    size_t chunk_size = std::min(part.size(), bytes_left);
-    next = std::copy_n(part.begin(), chunk_size, next);
-    bytes_left -= chunk_size;
-  }
-  dst = dst.drop_back(bytes_left);
-}
-
 Expected<LinuxPerfZeroTscConversion>
 lldb_private::process_linux::LoadPerfTscConversionParameters() {
   lldb::pid_t pid = getpid();
@@ -84,8 +36,10 @@ lldb_private::process_linux::LoadPerfTscConversionParameters() {
   Expected<PerfEvent> perf_event = PerfEvent::Init(attr, pid);
   if (!perf_event)
     return perf_event.takeError();
-  if (Error mmap_err = perf_event->MmapMetadataAndBuffers(/*num_data_pages*/ 0,
-                                                          /*num_aux_pages*/ 0))
+  if (Error mmap_err =
+          perf_event->MmapMetadataAndBuffers(/*num_data_pages=*/0,
+                                             /*num_aux_pages=*/0,
+                                             /*data_buffer_write=*/false))
     return std::move(mmap_err);
 
   perf_event_mmap_page &mmap_metada = perf_event->GetMetadataPage();
@@ -155,11 +109,12 @@ PerfEvent::DoMmap(void *addr, size_t length, int prot, int flags,
   return resource_handle::MmapUP(mmap_result, length);
 }
 
-llvm::Error PerfEvent::MmapMetadataAndDataBuffer(size_t num_data_pages) {
+llvm::Error PerfEvent::MmapMetadataAndDataBuffer(size_t num_data_pages,
+                                                 bool data_buffer_write) {
   size_t mmap_size = (num_data_pages + 1) * getpagesize();
-  if (Expected<resource_handle::MmapUP> mmap_metadata_data =
-          DoMmap(nullptr, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, 0,
-                 "metadata and data buffer")) {
+  if (Expected<resource_handle::MmapUP> mmap_metadata_data = DoMmap(
+          nullptr, mmap_size, PROT_READ | (data_buffer_write ? PROT_WRITE : 0),
+          MAP_SHARED, 0, "metadata and data buffer")) {
     m_metadata_data_base = std::move(mmap_metadata_data.get());
     return Error::success();
   } else
@@ -171,6 +126,7 @@ llvm::Error PerfEvent::MmapAuxBuffer(size_t num_aux_pages) {
     return Error::success();
 
   perf_event_mmap_page &metadata_page = GetMetadataPage();
+
   metadata_page.aux_offset =
       metadata_page.data_offset + metadata_page.data_size;
   metadata_page.aux_size = num_aux_pages * getpagesize();
@@ -185,7 +141,8 @@ llvm::Error PerfEvent::MmapAuxBuffer(size_t num_aux_pages) {
 }
 
 llvm::Error PerfEvent::MmapMetadataAndBuffers(size_t num_data_pages,
-                                              size_t num_aux_pages) {
+                                              size_t num_aux_pages,
+                                              bool data_buffer_write) {
   if (num_data_pages != 0 && !isPowerOf2_64(num_data_pages))
     return llvm::createStringError(
         llvm::inconvertibleErrorCode(),
@@ -196,7 +153,7 @@ llvm::Error PerfEvent::MmapMetadataAndBuffers(size_t num_data_pages,
         llvm::inconvertibleErrorCode(),
         llvm::formatv("Number of aux pages must be a power of 2, got: {0}",
                       num_aux_pages));
-  if (Error err = MmapMetadataAndDataBuffer(num_data_pages))
+  if (Error err = MmapMetadataAndDataBuffer(num_data_pages, data_buffer_write))
     return err;
   if (Error err = MmapAuxBuffer(num_aux_pages))
     return err;
@@ -222,18 +179,74 @@ ArrayRef<uint8_t> PerfEvent::GetAuxBuffer() const {
            static_cast<size_t>(mmap_metadata.aux_size)};
 }
 
+Expected<std::vector<uint8_t>>
+PerfEvent::ReadFlushedOutDataCyclicBuffer(size_t offset, size_t size) {
+  CollectionState previous_state = m_collection_state;
+  if (Error err = DisableWithIoctl())
+    return std::move(err);
+
+  /**
+   * The data buffer and aux buffer have 
diff erent implementations
+   * with respect to their definition of head pointer. In the case
+   * of Aux data buffer the head always wraps around the aux buffer
+   * and we don't need to care about it, whereas the data_head keeps
+   * increasing and needs to be wrapped by modulus operator
+   */
+  perf_event_mmap_page &mmap_metadata = GetMetadataPage();
+
+  ArrayRef<uint8_t> data = GetDataBuffer();
+  uint64_t data_head = mmap_metadata.data_head;
+  uint64_t data_size = mmap_metadata.data_size;
+  std::vector<uint8_t> output;
+  output.reserve(size);
+
+  if (data_head > data_size) {
+    uint64_t actual_data_head = data_head % data_size;
+    // The buffer has wrapped
+    for (uint64_t i = actual_data_head + offset;
+         i < data_size && output.size() < size; i++)
+      output.push_back(data[i]);
+
+    // We need to find the starting position for the left part if the offset was
+    // too big
+    uint64_t left_part_start = actual_data_head + offset >= data_size
+                                   ? actual_data_head + offset - data_size
+                                   : 0;
+    for (uint64_t i = left_part_start;
+         i < actual_data_head && output.size() < size; i++)
+      output.push_back(data[i]);
+  } else {
+    for (uint64_t i = offset; i < data_head && output.size() < size; i++)
+      output.push_back(data[i]);
+  }
+
+  if (previous_state == CollectionState::Enabled) {
+    if (Error err = EnableWithIoctl())
+      return std::move(err);
+  }
+
+  if (output.size() != size)
+    return createStringError(inconvertibleErrorCode(),
+                             formatv("Requested {0} bytes of perf_event data "
+                                     "buffer but only {1} are available",
+                                     size, output.size()));
+
+  return data;
+}
+
 Expected<std::vector<uint8_t>>
 PerfEvent::ReadFlushedOutAuxCyclicBuffer(size_t offset, size_t size) {
   CollectionState previous_state = m_collection_state;
   if (Error err = DisableWithIoctl())
     return std::move(err);
 
-  std::vector<uint8_t> data(size, 0);
   perf_event_mmap_page &mmap_metadata = GetMetadataPage();
-  Log *log = GetLog(POSIXLog::Trace);
-  uint64_t head = mmap_metadata.aux_head;
 
-  LLDB_LOG(log, "Aux size -{0} , Head - {1}", mmap_metadata.aux_size, head);
+  ArrayRef<uint8_t> data = GetAuxBuffer();
+  uint64_t aux_head = mmap_metadata.aux_head;
+  uint64_t aux_size = mmap_metadata.aux_size;
+  std::vector<uint8_t> output;
+  output.reserve(size);
 
   /**
    * When configured as ring buffer, the aux buffer keeps wrapping around
@@ -247,14 +260,28 @@ PerfEvent::ReadFlushedOutAuxCyclicBuffer(size_t offset, size_t size) {
    *
    * */
 
-  MutableArrayRef<uint8_t> buffer(data);
-  ReadCyclicBuffer(buffer, GetAuxBuffer(), static_cast<size_t>(head), offset);
+  for (uint64_t i = aux_head + offset; i < aux_size && output.size() < size;
+       i++)
+    output.push_back(data[i]);
+
+  // We need to find the starting position for the left part if the offset was
+  // too big
+  uint64_t left_part_start =
+      aux_head + offset >= aux_size ? aux_head + offset - aux_size : 0;
+  for (uint64_t i = left_part_start; i < aux_head && output.size() < size; i++)
+    output.push_back(data[i]);
 
   if (previous_state == CollectionState::Enabled) {
     if (Error err = EnableWithIoctl())
       return std::move(err);
   }
 
+  if (output.size() != size)
+    return createStringError(inconvertibleErrorCode(),
+                             formatv("Requested {0} bytes of perf_event aux "
+                                     "buffer but only {1} are available",
+                                     size, output.size()));
+
   return data;
 }
 
@@ -286,7 +313,7 @@ Error PerfEvent::EnableWithIoctl() {
 
 size_t PerfEvent::GetEffectiveDataBufferSize() const {
   perf_event_mmap_page &mmap_metadata = GetMetadataPage();
-  if (mmap_metadata.data_head < mmap_metadata.data_size)
+  if (mmap_metadata.data_head <= mmap_metadata.data_size)
     return mmap_metadata.data_head;
   else
     return mmap_metadata.data_size; // The buffer has wrapped.

diff  --git a/lldb/source/Plugins/Process/Linux/Perf.h b/lldb/source/Plugins/Process/Linux/Perf.h
index 6bb943a30b891..cb80304c7b9e2 100644
--- a/lldb/source/Plugins/Process/Linux/Perf.h
+++ b/lldb/source/Plugins/Process/Linux/Perf.h
@@ -74,24 +74,6 @@ using MmapUP = std::unique_ptr<void, resource_handle::MmapDeleter>;
 
 } // namespace resource_handle
 
-/// Read data from a cyclic buffer
-///
-/// \param[in] [out] buf
-///     Destination buffer, the buffer will be truncated to written size.
-///
-/// \param[in] src
-///     Source buffer which must be a cyclic buffer.
-///
-/// \param[in] src_cyc_index
-///     The index pointer (start of the valid data in the cyclic
-///     buffer).
-///
-/// \param[in] offset
-///     The offset to begin reading the data in the cyclic buffer.
-void ReadCyclicBuffer(llvm::MutableArrayRef<uint8_t> &dst,
-                      llvm::ArrayRef<uint8_t> src, size_t src_cyc_index,
-                      size_t offset);
-
 /// Thin wrapper of the perf_event_open API.
 ///
 /// Exposes the metadata page and data and aux buffers of a perf event.
@@ -174,11 +156,16 @@ class PerfEvent {
   ///     A value of 0 effectively is a no-op and no data is mmap'ed for this
   ///     buffer.
   ///
+  /// \param[in] data_buffer_write
+  ///     Whether to mmap the data buffer with WRITE permissions. This changes
+  ///     the behavior of how the kernel writes to the data buffer.
+  ///
   /// \return
   ///   \a llvm::Error::success if the mmap operations succeeded,
   ///   or an \a llvm::Error otherwise.
   llvm::Error MmapMetadataAndBuffers(size_t num_data_pages,
-                                     size_t num_aux_pages);
+                                     size_t num_aux_pages,
+                                     bool data_buffer_write);
 
   /// Get the file descriptor associated with the perf event.
   long GetFd() const;
@@ -237,6 +224,25 @@ class PerfEvent {
   llvm::Expected<std::vector<uint8_t>>
   ReadFlushedOutAuxCyclicBuffer(size_t offset, size_t size);
 
+  /// Read the data buffer managed by this perf event. To ensure that the
+  /// data is up-to-date and is not corrupted by read-write race conditions, the
+  /// underlying perf_event is paused during read, and later it's returned to
+  /// its initial state. The returned data will be linear, i.e. it will fix the
+  /// circular wrapping the might exist int he buffer.
+  ///
+  /// \param[in] offset
+  ///     Offset of the data to read.
+  ///
+  /// \param[in] size
+  ///     Number of bytes to read.
+  ///
+  /// \return
+  ///     A vector with the requested binary data. The vector will have the
+  ///     size of the requested \a size. Non-available positions will be
+  ///     filled with zeroes.
+  llvm::Expected<std::vector<uint8_t>>
+  ReadFlushedOutDataCyclicBuffer(size_t offset, size_t size);
+
   /// Use the ioctl API to disable the perf event and all the events in its
   /// group. This doesn't terminate the perf event.
   ///
@@ -290,7 +296,12 @@ class PerfEvent {
   ///     Number of pages in the data buffer to mmap, must be a power of 2.
   ///     A value of 0 is useful for "dummy" events that only want to access
   ///     the metadata, \a perf_event_mmap_page, or the aux buffer.
-  llvm::Error MmapMetadataAndDataBuffer(size_t num_data_pages);
+  ///
+  /// \param[in] data_buffer_write
+  ///     Whether to mmap the data buffer with WRITE permissions. This changes
+  ///     the behavior of how the kernel writes to the data buffer.
+  llvm::Error MmapMetadataAndDataBuffer(size_t num_data_pages,
+                                        bool data_buffer_write);
 
   /// Mmap the aux buffer of the perf event.
   ///

diff  --git a/lldb/source/Plugins/Trace/common/CMakeLists.txt b/lldb/source/Plugins/Trace/common/CMakeLists.txt
index 279115222f8e0..ee7afddb15f72 100644
--- a/lldb/source/Plugins/Trace/common/CMakeLists.txt
+++ b/lldb/source/Plugins/Trace/common/CMakeLists.txt
@@ -1,8 +1,5 @@
 add_lldb_library(lldbPluginTraceCommon
   ThreadPostMortemTrace.cpp
-  TraceJSONStructs.cpp
-  TraceSessionFileParser.cpp
-  TraceSessionSaver.cpp
 
   LINK_LIBS
     lldbCore

diff  --git a/lldb/source/Plugins/Trace/common/ThreadPostMortemTrace.cpp b/lldb/source/Plugins/Trace/common/ThreadPostMortemTrace.cpp
index 45d6f3b0e0983..37239b9d4c710 100644
--- a/lldb/source/Plugins/Trace/common/ThreadPostMortemTrace.cpp
+++ b/lldb/source/Plugins/Trace/common/ThreadPostMortemTrace.cpp
@@ -16,6 +16,7 @@
 
 using namespace lldb;
 using namespace lldb_private;
+using namespace llvm;
 
 void ThreadPostMortemTrace::RefreshStateAfterStop() {}
 
@@ -36,6 +37,6 @@ ThreadPostMortemTrace::CreateRegisterContextForFrame(StackFrame *frame) {
 
 bool ThreadPostMortemTrace::CalculateStopInfo() { return false; }
 
-const FileSpec &ThreadPostMortemTrace::GetTraceFile() const {
+const Optional<FileSpec> &ThreadPostMortemTrace::GetTraceFile() const {
   return m_trace_file;
 }

diff  --git a/lldb/source/Plugins/Trace/common/ThreadPostMortemTrace.h b/lldb/source/Plugins/Trace/common/ThreadPostMortemTrace.h
index 9cfe754ae0e4e..14ea2f1f5f7d6 100644
--- a/lldb/source/Plugins/Trace/common/ThreadPostMortemTrace.h
+++ b/lldb/source/Plugins/Trace/common/ThreadPostMortemTrace.h
@@ -32,7 +32,7 @@ class ThreadPostMortemTrace : public Thread {
   ///     The file that contains the list of instructions that were traced when
   ///     this thread was being executed.
   ThreadPostMortemTrace(Process &process, lldb::tid_t tid,
-                        const FileSpec &trace_file)
+                        const llvm::Optional<FileSpec> &trace_file)
       : Thread(process, tid), m_trace_file(trace_file) {}
 
   void RefreshStateAfterStop() override;
@@ -44,7 +44,7 @@ class ThreadPostMortemTrace : public Thread {
 
   /// \return
   ///   The trace file of this thread.
-  const FileSpec &GetTraceFile() const;
+  const llvm::Optional<FileSpec> &GetTraceFile() const;
 
 protected:
   bool CalculateStopInfo() override;
@@ -52,7 +52,7 @@ class ThreadPostMortemTrace : public Thread {
   lldb::RegisterContextSP m_thread_reg_ctx_sp;
 
 private:
-  FileSpec m_trace_file;
+  llvm::Optional<FileSpec> m_trace_file;
 };
 
 } // namespace lldb_private

diff  --git a/lldb/source/Plugins/Trace/common/TraceJSONStructs.cpp b/lldb/source/Plugins/Trace/common/TraceJSONStructs.cpp
deleted file mode 100644
index 1773a60037013..0000000000000
--- a/lldb/source/Plugins/Trace/common/TraceJSONStructs.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-//===-- 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 json_module;
-  json_module["systemPath"] = module.system_path;
-  if (module.file)
-    json_module["file"] = *module.file;
-  std::ostringstream oss;
-  oss << "0x" << std::hex << module.load_address.value;
-  json_module["loadAddress"] = oss.str();
-  if (module.uuid)
-    json_module["uuid"] = *module.uuid;
-  return std::move(json_module);
-}
-
-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 json_process;
-  json_process["pid"] = process.pid;
-  json_process["triple"] = process.triple;
-
-  llvm::json::Array threads_arr;
-  for (JSONThread e : process.threads)
-    threads_arr.push_back(toJSON(e));
-
-  json_process["threads"] = llvm::json::Value(std::move(threads_arr));
-
-  llvm::json::Array modules_arr;
-  for (JSONModule e : process.modules)
-    modules_arr.push_back(toJSON(e));
-
-  json_process["modules"] = llvm::json::Value(std::move(modules_arr));
-
-  return std::move(json_process);
-}
-
-llvm::json::Value toJSON(const JSONTraceSessionBase &session) {
-  llvm::json::Array arr;
-  for (JSONProcess e : session.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

diff  --git a/lldb/source/Plugins/Trace/common/TraceJSONStructs.h b/lldb/source/Plugins/Trace/common/TraceJSONStructs.h
deleted file mode 100644
index e01c33bf0d6af..0000000000000
--- a/lldb/source/Plugins/Trace/common/TraceJSONStructs.h
+++ /dev/null
@@ -1,98 +0,0 @@
-//===-- 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 {
-
-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;
-};
-
-} // 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

diff  --git a/lldb/source/Plugins/Trace/common/TraceSessionFileParser.cpp b/lldb/source/Plugins/Trace/common/TraceSessionFileParser.cpp
deleted file mode 100644
index b26704ca34be8..0000000000000
--- a/lldb/source/Plugins/Trace/common/TraceSessionFileParser.cpp
+++ /dev/null
@@ -1,172 +0,0 @@
-//===-- TraceSessionFileParser.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 "TraceSessionFileParser.h"
-#include "ThreadPostMortemTrace.h"
-
-#include <sstream>
-
-#include "lldb/Core/Debugger.h"
-#include "lldb/Core/Module.h"
-#include "lldb/Target/Process.h"
-#include "lldb/Target/Target.h"
-
-using namespace lldb;
-using namespace lldb_private;
-using namespace llvm;
-
-void TraceSessionFileParser::NormalizePath(lldb_private::FileSpec &file_spec) {
-  if (file_spec.IsRelative())
-    file_spec.PrependPathComponent(m_session_file_dir);
-}
-
-Error TraceSessionFileParser::ParseModule(lldb::TargetSP &target_sp,
-                                          const JSONModule &module) {
-  FileSpec system_file_spec(module.system_path);
-  NormalizePath(system_file_spec);
-
-  FileSpec local_file_spec(module.file.hasValue() ? *module.file
-                                                  : module.system_path);
-  NormalizePath(local_file_spec);
-
-  ModuleSpec module_spec;
-  module_spec.GetFileSpec() = local_file_spec;
-  module_spec.GetPlatformFileSpec() = system_file_spec;
-
-  if (module.uuid.hasValue())
-    module_spec.GetUUID().SetFromStringRef(*module.uuid);
-
-  Status error;
-  ModuleSP module_sp =
-      target_sp->GetOrCreateModule(module_spec, /*notify*/ false, &error);
-
-  if (error.Fail())
-    return error.ToError();
-
-  bool load_addr_changed = false;
-  module_sp->SetLoadAddress(*target_sp, module.load_address.value, false,
-                            load_addr_changed);
-  return llvm::Error::success();
-}
-
-Error TraceSessionFileParser::CreateJSONError(json::Path::Root &root,
-                                              const json::Value &value) {
-  std::string err;
-  raw_string_ostream os(err);
-  root.printErrorContext(value, os);
-  return createStringError(
-      std::errc::invalid_argument, "%s\n\nContext:\n%s\n\nSchema:\n%s",
-      toString(root.getError()).c_str(), os.str().c_str(), m_schema.data());
-}
-
-std::string TraceSessionFileParser::BuildSchema(StringRef plugin_schema) {
-  std::ostringstream schema_builder;
-  schema_builder << "{\n  \"trace\": ";
-  schema_builder << plugin_schema.data() << ",";
-  schema_builder << R"(
-  "processes": [
-    {
-      "pid": integer,
-      "triple": string, // llvm-triple
-      "threads": [
-        {
-          "tid": integer,
-          "traceFile": string
-        }
-      ],
-      "modules": [
-        {
-          "systemPath": string, // original path of the module at runtime
-          "file"?: string, // copy of the file if not available at "systemPath"
-          "loadAddress": string, // string address in hex or decimal form
-          "uuid"?: string,
-        }
-      ]
-    }
-  ]
-  // Notes:
-  // All paths are either absolute or relative to the session file.
-}
-)";
-  return schema_builder.str();
-}
-
-ThreadPostMortemTraceSP
-TraceSessionFileParser::ParseThread(ProcessSP &process_sp,
-                                    const JSONThread &thread) {
-  lldb::tid_t tid = static_cast<lldb::tid_t>(thread.tid);
-
-  FileSpec trace_file(thread.trace_file);
-  NormalizePath(trace_file);
-
-  ThreadPostMortemTraceSP thread_sp =
-      std::make_shared<ThreadPostMortemTrace>(*process_sp, tid, trace_file);
-  process_sp->GetThreadList().AddThread(thread_sp);
-  return thread_sp;
-}
-
-Expected<TraceSessionFileParser::ParsedProcess>
-TraceSessionFileParser::ParseProcess(const JSONProcess &process) {
-  TargetSP target_sp;
-  Status error = m_debugger.GetTargetList().CreateTarget(
-      m_debugger, /*user_exe_path*/ StringRef(), process.triple,
-      eLoadDependentsNo,
-      /*platform_options*/ nullptr, target_sp);
-
-  if (!target_sp)
-    return error.ToError();
-
-  ParsedProcess parsed_process;
-  parsed_process.target_sp = target_sp;
-
-  ProcessSP process_sp = target_sp->CreateProcess(
-      /*listener*/ nullptr, "trace",
-      /*crash_file*/ nullptr,
-      /*can_connect*/ false);
-
-  process_sp->SetID(static_cast<lldb::pid_t>(process.pid));
-
-  for (const JSONThread &thread : process.threads)
-    parsed_process.threads.push_back(ParseThread(process_sp, thread));
-
-  for (const JSONModule &module : process.modules)
-    if (Error err = ParseModule(target_sp, module))
-      return std::move(err);
-
-  if (!process.threads.empty())
-    process_sp->GetThreadList().SetSelectedThreadByIndexID(0);
-
-  // We invoke DidAttach to create a correct stopped state for the process and
-  // its threads.
-  ArchSpec process_arch;
-  process_sp->DidAttach(process_arch);
-
-  return parsed_process;
-}
-
-Expected<std::vector<TraceSessionFileParser::ParsedProcess>>
-TraceSessionFileParser::ParseCommonSessionFile(
-    const JSONTraceSessionBase &session) {
-  std::vector<ParsedProcess> parsed_processes;
-
-  auto onError = [&]() {
-    // Delete all targets that were created so far in case of failures
-    for (ParsedProcess &parsed_process : parsed_processes)
-      m_debugger.GetTargetList().DeleteTarget(parsed_process.target_sp);
-  };
-
-  for (const JSONProcess &process : session.processes) {
-    if (Expected<ParsedProcess> parsed_process = ParseProcess(process))
-      parsed_processes.push_back(std::move(*parsed_process));
-    else {
-      onError();
-      return parsed_process.takeError();
-    }
-  }
-  return parsed_processes;
-}

diff  --git a/lldb/source/Plugins/Trace/common/TraceSessionFileParser.h b/lldb/source/Plugins/Trace/common/TraceSessionFileParser.h
deleted file mode 100644
index 19cc2f59ded73..0000000000000
--- a/lldb/source/Plugins/Trace/common/TraceSessionFileParser.h
+++ /dev/null
@@ -1,93 +0,0 @@
-//===-- TraceSessionFileParser.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_TRACESESSIONPARSER_H
-#define LLDB_TARGET_TRACESESSIONPARSER_H
-
-#include "ThreadPostMortemTrace.h"
-#include "TraceJSONStructs.h"
-
-namespace lldb_private {
-
-/// \class TraceSessionFileParser TraceSessionFileParser.h
-///
-/// Base class for parsing the common information of JSON trace session files.
-/// Contains the basic C++ structs that represent the JSON data, which include
-/// \a JSONTraceSession as the root object.
-///
-/// See \a Trace::FindPlugin for more information regarding these JSON files.
-class TraceSessionFileParser {
-public:
-
-  /// Helper struct holding the objects created when parsing a process
-  struct ParsedProcess {
-    lldb::TargetSP target_sp;
-    std::vector<lldb::ThreadPostMortemTraceSP> threads;
-  };
-
-  TraceSessionFileParser(Debugger &debugger, llvm::StringRef session_file_dir,
-                         llvm::StringRef schema)
-      : m_debugger(debugger), m_session_file_dir(session_file_dir),
-        m_schema(schema) {}
-
-  /// Build the full schema for a Trace plug-in.
-  ///
-  /// \param[in] plugin_schema
-  ///   The subschema that corresponds to the "trace" section of the schema.
-  ///
-  /// \return
-  ///   The full schema containing the common attributes and the plug-in
-  ///   specific attributes.
-  static std::string BuildSchema(llvm::StringRef plugin_schema);
-
-  /// Parse the fields common to all trace session schemas.
-  ///
-  /// \param[in] session
-  ///     The session json objects already deserialized.
-  ///
-  /// \return
-  ///     A list of \a ParsedProcess containing all threads and targets created
-  ///     during the parsing, or an error in case of failures. In case of
-  ///     errors, no side effects are produced.
-  llvm::Expected<std::vector<ParsedProcess>>
-  ParseCommonSessionFile(const JSONTraceSessionBase &session);
-
-protected:
-  /// Resolve non-absolute paths relative to the session file folder. It
-  /// modifies the given file_spec.
-  void NormalizePath(lldb_private::FileSpec &file_spec);
-
-  lldb::ThreadPostMortemTraceSP ParseThread(lldb::ProcessSP &process_sp,
-                                            const JSONThread &thread);
-
-  llvm::Expected<ParsedProcess> ParseProcess(const JSONProcess &process);
-
-  llvm::Error ParseModule(lldb::TargetSP &target_sp, const JSONModule &module);
-
-  /// Create a user-friendly error message upon a JSON-parsing failure using the
-  /// \a json::ObjectMapper functionality.
-  ///
-  /// \param[in] root
-  ///   The \a llvm::json::Path::Root used to parse the JSON \a value.
-  ///
-  /// \param[in] value
-  ///   The json value that failed to parse.
-  ///
-  /// \return
-  ///   An \a llvm::Error containing the user-friendly error message.
-  llvm::Error CreateJSONError(llvm::json::Path::Root &root,
-                              const llvm::json::Value &value);
-
-  Debugger &m_debugger;
-  std::string m_session_file_dir;
-  llvm::StringRef m_schema;
-};
-} // namespace lldb_private
-
-
-#endif // LLDB_TARGET_TRACESESSIONPARSER_H

diff  --git a/lldb/source/Plugins/Trace/common/TraceSessionSaver.cpp b/lldb/source/Plugins/Trace/common/TraceSessionSaver.cpp
deleted file mode 100644
index e1d62062b8e79..0000000000000
--- a/lldb/source/Plugins/Trace/common/TraceSessionSaver.cpp
+++ /dev/null
@@ -1,148 +0,0 @@
-//===-- TraceSessionSaver.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 "TraceSessionSaver.h"
-
-#include "lldb/Core/Module.h"
-#include "lldb/Core/Value.h"
-#include "lldb/Target/Process.h"
-#include "lldb/Target/SectionLoadList.h"
-#include "lldb/Target/Target.h"
-#include "lldb/lldb-types.h"
-#include "llvm/Support/Error.h"
-#include "llvm/Support/JSON.h"
-
-#include <fstream>
-
-using namespace lldb;
-using namespace lldb_private;
-using namespace llvm;
-
-llvm::Error TraceSessionSaver::WriteSessionToFile(
-    const llvm::json::Value &trace_session_description, FileSpec directory) {
-
-  FileSpec trace_path = directory;
-  trace_path.AppendPathComponent("trace.json");
-  std::ofstream os(trace_path.GetPath());
-  os << std::string(formatv("{0:2}", trace_session_description));
-  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<JSONTraceSessionBase> TraceSessionSaver::BuildProcessesSection(
-    Process &live_process, llvm::StringRef raw_thread_trace_data_kind,
-    FileSpec directory) {
-
-  JSONTraceSessionBase json_session_description;
-  Expected<std::vector<JSONThread>> json_threads =
-      BuildThreadsSection(live_process, raw_thread_trace_data_kind, 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();
-
-  json_session_description.processes.push_back(JSONProcess{
-      static_cast<int64_t>(live_process.GetID()),
-      live_process.GetTarget().GetArchitecture().GetTriple().getTriple(),
-      json_threads.get(), json_modules.get()});
-  return json_session_description;
-}
-
-llvm::Expected<std::vector<JSONThread>> TraceSessionSaver::BuildThreadsSection(
-    Process &live_process, llvm::StringRef raw_thread_trace_data_kind,
-    FileSpec directory) {
-  std::vector<JSONThread> json_threads;
-  for (ThreadSP thread_sp : live_process.Threads()) {
-    TraceSP trace_sp = live_process.GetTarget().GetTrace();
-    lldb::tid_t tid = thread_sp->GetID();
-    if (!trace_sp->IsTraced(tid))
-      continue;
-
-    // resolve the directory just in case
-    FileSystem::Instance().Resolve(directory);
-    FileSpec raw_trace_path = directory;
-    raw_trace_path.AppendPathComponent(std::to_string(tid) + ".trace");
-    json_threads.push_back(JSONThread{static_cast<int64_t>(tid),
-                                      raw_trace_path.GetPath().c_str()});
-
-    llvm::Error err =
-        live_process.GetTarget().GetTrace()->OnThreadBinaryDataRead(
-            tid, raw_thread_trace_data_kind,
-            [&](llvm::ArrayRef<uint8_t> data) -> llvm::Error {
-              std::basic_fstream<char> raw_trace_fs =
-                  std::fstream(raw_trace_path.GetPath().c_str(),
-                               std::ios::out | std::ios::binary);
-              raw_trace_fs.write(reinterpret_cast<const char *>(&data[0]),
-                                 data.size() * sizeof(uint8_t));
-              raw_trace_fs.close();
-              if (!raw_trace_fs)
-                return createStringError(
-                    inconvertibleErrorCode(),
-                    formatv("couldn't write to the file {0}",
-                            raw_trace_path.GetPath().c_str()));
-              return Error::success();
-            });
-    if (err)
-      return std::move(err);
-  }
-  return json_threads;
-}
-
-llvm::Expected<std::vector<JSONModule>>
-TraceSessionSaver::BuildModulesSection(Process &live_process,
-                                       FileSpec directory) {
-  std::vector<JSONModule> json_modules;
-  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()));
-
-    json_modules.push_back(
-        JSONModule{system_path, path_to_copy_module.GetPath(),
-                   JSONAddress{load_addr}, module_sp->GetUUID().GetAsString()});
-  }
-  return json_modules;
-}

diff  --git a/lldb/source/Plugins/Trace/common/TraceSessionSaver.h b/lldb/source/Plugins/Trace/common/TraceSessionSaver.h
deleted file mode 100644
index c490fd747ffc2..0000000000000
--- a/lldb/source/Plugins/Trace/common/TraceSessionSaver.h
+++ /dev/null
@@ -1,102 +0,0 @@
-//===-- SessionSaver.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_TRACESESSIONSAVER_H
-#define LLDB_TARGET_TRACESESSIONSAVER_H
-
-#include "TraceJSONStructs.h"
-
-namespace lldb_private {
-
-class TraceSessionSaver {
-
-public:
-  /// Save the trace session description JSON object inside the given directory
-  /// as a file named \a trace.json.
-  ///
-  /// \param[in] trace_session_description
-  ///     The trace's session, as JSON Object.
-  ///
-  /// \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.
-  static llvm::Error
-  WriteSessionToFile(const llvm::json::Value &trace_session_description,
-                     FileSpec directory);
-
-  /// Build the processes section of the trace session description file. Besides
-  /// returning the processes information, this method saves to disk all modules
-  /// and raw traces corresponding to the traced threads of the given process.
-  ///
-  /// \param[in] live_process
-  ///     The process being traced.
-  ///
-  /// \param[in] raw_thread_trace_data_kind
-  ///     Identifier for the data kind of the raw trace for each thread.
-  ///
-  /// \param[in] directory
-  ///     The directory where files will be saved when building the processes
-  ///     section.
-  ///
-  /// \return
-  ///     The processes section or \a llvm::Error in case of failures.
-  static llvm::Expected<JSONTraceSessionBase>
-  BuildProcessesSection(Process &live_process,
-                        llvm::StringRef raw_thread_trace_data_kind,
-                        FileSpec directory);
-
-  /// Build the threads sub-section of the trace session description file.
-  /// For each traced thread, its raw trace is also written to the file
-  /// \a thread_id_.trace inside of the given directory.
-  ///
-  /// \param[in] live_process
-  ///     The process being traced.
-  ///
-  /// \param[in] raw_thread_trace_data_kind
-  ///     Identifier for the data kind of the raw trace for each thread.
-  ///
-  /// \param[in] directory
-  ///     The directory where files will be saved when building the threads
-  ///     section.
-  ///
-  /// \return
-  ///     The threads section or \a llvm::Error in case of failures.
-  static llvm::Expected<std::vector<JSONThread>>
-  BuildThreadsSection(Process &live_process,
-                      llvm::StringRef raw_thread_trace_data_kind,
-                      FileSpec directory);
-
-  /// Build modules sub-section of the trace's session. The original modules
-  /// will be copied over to the \a <directory/modules> folder. Invalid modules
-  /// are skipped.
-  /// Copying the modules has the benefit of making these trace session
-  /// directories self-contained, as the raw traces and modules are part of the
-  /// output directory and can be sent to another machine, where lldb can load
-  /// them and replicate exactly the same trace session.
-  ///
-  /// \param[in] 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 \a libbar.so exists in the path
-  ///     \a /usr/lib/foo/libbar.so, then it will be copied to
-  ///     \a <directory>/modules/usr/lib/foo/libbar.so.
-  ///
-  /// \return
-  ///     The modules section or \a llvm::Error in case of failures.
-  static llvm::Expected<std::vector<JSONModule>>
-  BuildModulesSection(Process &live_process, FileSpec directory);
-};
-} // namespace lldb_private
-
-#endif // LLDB_TARGET_TRACESESSIONSAVER_H

diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
index df3192a8bd9bf..997b46b709e93 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
@@ -74,15 +74,28 @@ Expected<TraceSP> TraceIntelPT::CreateInstanceForLiveProcess(Process &process) {
   return instance;
 }
 
-TraceIntelPT::TraceIntelPT(
-    const pt_cpu &cpu_info,
-    const std::vector<ThreadPostMortemTraceSP> &traced_threads)
-    : m_cpu_info(cpu_info) {
+TraceIntelPT::TraceIntelPT(JSONTraceSession &session,
+                           ArrayRef<ProcessSP> traced_processes,
+                           ArrayRef<ThreadPostMortemTraceSP> traced_threads)
+    : Trace(traced_processes, session.GetCoreIds()),
+      m_cpu_info(session.cpu_info),
+      m_tsc_conversion(session.tsc_perf_zero_conversion) {
   for (const ThreadPostMortemTraceSP &thread : traced_threads) {
     m_thread_decoders.emplace(thread->GetID(),
                               std::make_unique<ThreadDecoder>(thread, *this));
-    SetPostMortemThreadDataFile(thread->GetID(), IntelPTDataKinds::kTraceBuffer,
-                                thread->GetTraceFile());
+    if (const Optional<FileSpec> &trace_file = thread->GetTraceFile()) {
+      SetPostMortemThreadDataFile(thread->GetID(),
+                                  IntelPTDataKinds::kTraceBuffer, *trace_file);
+    }
+  }
+  if (session.cores) {
+    for (const JSONCore &core : *session.cores) {
+      SetPostMortemCoreDataFile(core.core_id, IntelPTDataKinds::kTraceBuffer,
+                                FileSpec(core.trace_buffer));
+      SetPostMortemCoreDataFile(core.core_id,
+                                IntelPTDataKinds::kPerfContextSwitchTrace,
+                                FileSpec(core.context_switch_trace));
+    }
   }
 }
 
@@ -240,6 +253,12 @@ Expected<pt_cpu> TraceIntelPT::GetCPUInfo() {
   return *m_cpu_info;
 }
 
+llvm::Optional<LinuxPerfZeroTscConversion>
+TraceIntelPT::GetPerfZeroTscConversion() {
+  RefreshLiveProcessState();
+  return m_tsc_conversion;
+}
+
 Error TraceIntelPT::DoRefreshLiveProcessState(TraceGetStateResponse state,
                                               StringRef json_response) {
   m_thread_decoders.clear();

diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
index dd4f3e8430445..608f4184d1b71 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
@@ -146,8 +146,11 @@ class TraceIntelPT : public Trace {
   llvm::Error OnThreadBufferRead(lldb::tid_t tid,
                                  OnBinaryDataReadCallback callback);
 
+  /// Get or fetch the cpu information from, for example, /proc/cpuinfo.
   llvm::Expected<pt_cpu> GetCPUInfo();
 
+  /// Get or fetch the values used to convert to and from TSCs and nanos.
+  llvm::Optional<LinuxPerfZeroTscConversion> GetPerfZeroTscConversion();
 
   /// \return
   ///     The timer object for this trace.
@@ -158,12 +161,20 @@ class TraceIntelPT : public Trace {
 
   llvm::Expected<pt_cpu> GetCPUInfoForLiveProcess();
 
+  /// Postmortem trace constructor
+  ///
+  /// \param[in] session
+  ///     The definition file for the postmortem session.
+  ///
+  /// \param[in] traces_proceses
+  ///     The processes traced in the live session.
+  ///
   /// \param[in] trace_threads
-  ///     ThreadTrace instances, which are not live-processes and whose trace
-  ///     files are fixed.
-  TraceIntelPT(
-      const pt_cpu &cpu_info,
-      const std::vector<lldb::ThreadPostMortemTraceSP> &traced_threads);
+  ///     The threads traced in the live session. They must belong to the
+  ///     processes mentioned above.
+  TraceIntelPT(JSONTraceSession &session,
+               llvm::ArrayRef<lldb::ProcessSP> traced_processes,
+               llvm::ArrayRef<lldb::ThreadPostMortemTraceSP> traced_threads);
 
   /// Constructor for live processes
   TraceIntelPT(Process &live_process)

diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp
index e36751e235dca..b637666504956 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp
@@ -15,45 +15,130 @@ using namespace lldb;
 using namespace lldb_private;
 using namespace lldb_private::trace_intel_pt;
 using namespace llvm;
+using namespace llvm::json;
 
-namespace llvm {
-namespace json {
+namespace lldb_private {
+namespace trace_intel_pt {
 
-bool fromJSON(const Value &value, JSONTraceIntelPTSettings &plugin_settings,
-              Path path) {
+Optional<std::vector<lldb::core_id_t>> JSONTraceSession::GetCoreIds() {
+  if (!cores)
+    return None;
+  std::vector<lldb::core_id_t> core_ids;
+  for (const JSONCore &core : *cores)
+    core_ids.push_back(core.core_id);
+  return core_ids;
+}
+
+json::Value toJSON(const JSONModule &module) {
+  json::Object json_module;
+  json_module["systemPath"] = module.system_path;
+  if (module.file)
+    json_module["file"] = *module.file;
+  json_module["loadAddress"] = module.load_address;
+  if (module.uuid)
+    json_module["uuid"] = *module.uuid;
+  return std::move(json_module);
+}
+
+bool fromJSON(const json::Value &value, JSONModule &module, Path path) {
+  ObjectMapper o(value, path);
+  return o && o.map("systemPath", module.system_path) &&
+         o.map("file", module.file) &&
+         o.map("loadAddress", module.load_address) &&
+         o.map("uuid", module.uuid);
+}
+
+json::Value toJSON(const JSONThread &thread) {
+  return json::Object{{"tid", thread.tid},
+                      {"traceBuffer", thread.trace_buffer}};
+}
+
+bool fromJSON(const json::Value &value, JSONThread &thread, Path path) {
+  ObjectMapper o(value, path);
+  return o && o.map("tid", thread.tid) &&
+         o.map("traceBuffer", thread.trace_buffer);
+}
+
+json::Value toJSON(const JSONProcess &process) {
+  return Object{
+      {"pid", process.pid},
+      {"triple", process.triple},
+      {"threads", process.threads},
+      {"modules", process.modules},
+  };
+}
+
+bool fromJSON(const json::Value &value, JSONProcess &process, Path path) {
   ObjectMapper o(value, path);
-  return o && o.map("cpuInfo", plugin_settings.cpuInfo) &&
-         fromJSON(value, (JSONTracePluginSettings &)plugin_settings, path);
+  return o && o.map("pid", process.pid) && o.map("triple", process.triple) &&
+         o.map("threads", process.threads) && o.map("modules", process.modules);
+}
+
+json::Value toJSON(const JSONCore &core) {
+  return Object{
+      {"coreId", core.core_id},
+      {"traceBuffer", core.trace_buffer},
+      {"contextSwitchTrace", core.context_switch_trace},
+  };
 }
 
-bool fromJSON(const json::Value &value, JSONTraceIntelPTCPUInfo &cpu_info,
-              Path path) {
+bool fromJSON(const json::Value &value, JSONCore &core, 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);
+  uint64_t core_id;
+  if (!o || !o.map("coreId", core_id) ||
+      !o.map("traceBuffer", core.trace_buffer) ||
+      !o.map("contextSwitchTrace", core.context_switch_trace))
+    return false;
+  core.core_id = core_id;
+  return true;
 }
 
-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}});
+json::Value toJSON(const pt_cpu &cpu_info) {
+  return Object{
+      {"vendor", cpu_info.vendor == pcv_intel ? "GenuineIntel" : "Unknown"},
+      {"family", cpu_info.family},
+      {"model", cpu_info.model},
+      {"stepping", cpu_info.stepping},
+  };
 }
 
-llvm::json::Value toJSON(const JSONTraceIntelPTTrace &trace) {
-  llvm::json::Object json_trace;
-  json_trace["type"] = trace.type;
-  json_trace["cpuInfo"] = toJSON(trace.cpuInfo);
-  return std::move(json_trace);
+bool fromJSON(const json::Value &value, pt_cpu &cpu_info, Path path) {
+  ObjectMapper o(value, path);
+  std::string vendor;
+  uint64_t family, model, stepping;
+  if (!o || !o.map("vendor", vendor) || !o.map("family", family) ||
+      !o.map("model", model) || !o.map("stepping", stepping))
+    return false;
+  cpu_info.vendor = vendor == "GenuineIntel" ? pcv_intel : pcv_unknown;
+  cpu_info.family = family;
+  cpu_info.model = model;
+  cpu_info.stepping = stepping;
+  return true;
 }
 
-llvm::json::Value toJSON(const JSONTraceIntelPTSession &session) {
-  llvm::json::Object json_session;
-  json_session["trace"] = toJSON(session.ipt_trace);
-  json_session["processes"] = toJSON(session.session_base);
-  return std::move(json_session);
+json::Value toJSON(const JSONTraceSession &session) {
+  return Object{{"type", session.type},
+                {"processes", session.processes},
+                // We have to do this because the compiler fails at doing it
+                // automatically because pt_cpu is not in a namespace
+                {"cpuInfo", toJSON(session.cpu_info)},
+                {"cores", session.cores},
+                {"tscPerfZeroConversion", session.tsc_perf_zero_conversion}};
+}
+
+bool fromJSON(const json::Value &value, JSONTraceSession &session, Path path) {
+  ObjectMapper o(value, path);
+  if (!o || !o.map("processes", session.processes) ||
+      !o.map("type", session.type) || !o.map("cores", session.cores) ||
+      !o.map("tscPerfZeroConversion", session.tsc_perf_zero_conversion))
+    return false;
+  // We have to do this because the compiler fails at doing it automatically
+  // because pt_cpu is not in a namespace
+  if (!fromJSON(*value.getAsObject()->get("cpuInfo"), session.cpu_info,
+                path.field("cpuInfo")))
+    return false;
+  return true;
 }
 
-} // namespace json
-} // namespace llvm
+} // namespace trace_intel_pt
+} // namespace lldb_private

diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h
index ec024f27b8c9d..af4c365d1c49e 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h
@@ -9,67 +9,84 @@
 #ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTJSONSTRUCTS_H
 #define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTJSONSTRUCTS_H
 
-#include "../common/TraceJSONStructs.h"
+#include "lldb/lldb-types.h"
+
+#include "lldb/Utility/TraceIntelPTGDBRemotePackets.h"
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/Support/JSON.h"
+
 #include <intel-pt.h>
+#include <vector>
 
 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";
-  }
+struct JSONModule {
+  std::string system_path;
+  llvm::Optional<std::string> file;
+  uint64_t load_address;
+  llvm::Optional<std::string> uuid;
+};
 
-  int64_t family;
-  int64_t model;
-  int64_t stepping;
-  std::string vendor;
+struct JSONThread {
+  int64_t tid;
+  llvm::Optional<std::string> trace_buffer;
 };
 
-struct JSONTraceIntelPTTrace {
-  std::string type;
-  JSONTraceIntelPTCPUInfo cpuInfo;
+struct JSONProcess {
+  int64_t pid;
+  std::string triple;
+  std::vector<JSONThread> threads;
+  std::vector<JSONModule> modules;
 };
 
-struct JSONTraceIntelPTSession {
-  JSONTraceIntelPTTrace ipt_trace;
-  JSONTraceSessionBase session_base;
+struct JSONCore {
+  lldb::core_id_t core_id;
+  std::string trace_buffer;
+  std::string context_switch_trace;
 };
 
-struct JSONTraceIntelPTSettings : JSONTracePluginSettings {
-  JSONTraceIntelPTCPUInfo cpuInfo;
+struct JSONTraceSession {
+  std::string type;
+  pt_cpu cpu_info;
+  std::vector<JSONProcess> processes;
+  llvm::Optional<std::vector<JSONCore>> cores;
+  llvm::Optional<LinuxPerfZeroTscConversion> tsc_perf_zero_conversion;
+
+  llvm::Optional<std::vector<lldb::core_id_t>> GetCoreIds();
 };
 
-} // namespace trace_intel_pt
-} // namespace lldb_private
+llvm::json::Value toJSON(const JSONModule &module);
+
+llvm::json::Value toJSON(const JSONThread &thread);
 
-namespace llvm {
-namespace json {
+llvm::json::Value toJSON(const JSONProcess &process);
 
-bool fromJSON(
-    const Value &value,
-    lldb_private::trace_intel_pt::JSONTraceIntelPTSettings &plugin_settings,
-    Path path);
+llvm::json::Value toJSON(const JSONCore &core);
 
-bool fromJSON(const llvm::json::Value &value,
-              lldb_private::trace_intel_pt::JSONTraceIntelPTCPUInfo &packet,
+llvm::json::Value toJSON(const pt_cpu &cpu_info);
+
+llvm::json::Value toJSON(const JSONTraceSession &session);
+
+bool fromJSON(const llvm::json::Value &value, JSONModule &module,
               llvm::json::Path path);
 
-llvm::json::Value
-toJSON(const lldb_private::trace_intel_pt::JSONTraceIntelPTCPUInfo &cpu_info);
+bool fromJSON(const llvm::json::Value &value, JSONThread &thread,
+              llvm::json::Path path);
+
+bool fromJSON(const llvm::json::Value &value, JSONProcess &process,
+              llvm::json::Path path);
 
-llvm::json::Value
-toJSON(const lldb_private::trace_intel_pt::JSONTraceIntelPTTrace &trace);
+bool fromJSON(const llvm::json::Value &value, JSONCore &core,
+              llvm::json::Path path);
 
-llvm::json::Value
-toJSON(const lldb_private::trace_intel_pt::JSONTraceIntelPTSession &session);
+bool fromJSON(const llvm::json::Value &value, pt_cpu &cpu_info,
+              llvm::json::Path path);
 
-} // namespace json
-} // namespace llvm
+bool fromJSON(const llvm::json::Value &value, JSONTraceSession &session,
+              llvm::json::Path path);
+} // namespace trace_intel_pt
+} // namespace lldb_private
 
 #endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTJSONSTRUCTS_H

diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
index 7e2c39a20255c..10ed88e8a06d7 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
@@ -10,44 +10,221 @@
 
 #include "../common/ThreadPostMortemTrace.h"
 #include "TraceIntelPT.h"
+#include "TraceIntelPTJSONStructs.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
 
 using namespace lldb;
 using namespace lldb_private;
 using namespace lldb_private::trace_intel_pt;
 using namespace llvm;
 
+void TraceIntelPTSessionFileParser::NormalizePath(
+    lldb_private::FileSpec &file_spec) {
+  if (file_spec.IsRelative())
+    file_spec.PrependPathComponent(m_session_file_dir);
+}
+
+Error TraceIntelPTSessionFileParser::ParseModule(lldb::TargetSP &target_sp,
+                                                 const JSONModule &module) {
+  FileSpec system_file_spec(module.system_path);
+  NormalizePath(system_file_spec);
+
+  FileSpec local_file_spec(module.file.hasValue() ? *module.file
+                                                  : module.system_path);
+  NormalizePath(local_file_spec);
+
+  ModuleSpec module_spec;
+  module_spec.GetFileSpec() = local_file_spec;
+  module_spec.GetPlatformFileSpec() = system_file_spec;
+
+  if (module.uuid.hasValue())
+    module_spec.GetUUID().SetFromStringRef(*module.uuid);
+
+  Status error;
+  ModuleSP module_sp =
+      target_sp->GetOrCreateModule(module_spec, /*notify*/ false, &error);
+
+  if (error.Fail())
+    return error.ToError();
+
+  bool load_addr_changed = false;
+  module_sp->SetLoadAddress(*target_sp, module.load_address, false,
+                            load_addr_changed);
+  return llvm::Error::success();
+}
+
+Error TraceIntelPTSessionFileParser::CreateJSONError(json::Path::Root &root,
+                                                     const json::Value &value) {
+  std::string err;
+  raw_string_ostream os(err);
+  root.printErrorContext(value, os);
+  return createStringError(
+      std::errc::invalid_argument, "%s\n\nContext:\n%s\n\nSchema:\n%s",
+      toString(root.getError()).c_str(), os.str().c_str(), GetSchema().data());
+}
+
+ThreadPostMortemTraceSP
+TraceIntelPTSessionFileParser::ParseThread(ProcessSP &process_sp,
+                                           const JSONThread &thread) {
+  lldb::tid_t tid = static_cast<lldb::tid_t>(thread.tid);
+
+  Optional<FileSpec> trace_file;
+  if (thread.trace_buffer) {
+    trace_file.emplace(*thread.trace_buffer);
+    NormalizePath(*trace_file);
+  }
+
+  ThreadPostMortemTraceSP thread_sp =
+      std::make_shared<ThreadPostMortemTrace>(*process_sp, tid, trace_file);
+  process_sp->GetThreadList().AddThread(thread_sp);
+  return thread_sp;
+}
+
+Expected<TraceIntelPTSessionFileParser::ParsedProcess>
+TraceIntelPTSessionFileParser::ParseProcess(const JSONProcess &process) {
+  TargetSP target_sp;
+  Status error = m_debugger.GetTargetList().CreateTarget(
+      m_debugger, /*user_exe_path*/ StringRef(), process.triple,
+      eLoadDependentsNo,
+      /*platform_options*/ nullptr, target_sp);
+
+  if (!target_sp)
+    return error.ToError();
+
+  ParsedProcess parsed_process;
+  parsed_process.target_sp = target_sp;
+
+  ProcessSP process_sp = target_sp->CreateProcess(
+      /*listener*/ nullptr, "trace",
+      /*crash_file*/ nullptr,
+      /*can_connect*/ false);
+
+  process_sp->SetID(static_cast<lldb::pid_t>(process.pid));
+
+  for (const JSONThread &thread : process.threads)
+    parsed_process.threads.push_back(ParseThread(process_sp, thread));
+
+  for (const JSONModule &module : process.modules)
+    if (Error err = ParseModule(target_sp, module))
+      return std::move(err);
+
+  if (!process.threads.empty())
+    process_sp->GetThreadList().SetSelectedThreadByIndexID(0);
+
+  // We invoke DidAttach to create a correct stopped state for the process and
+  // its threads.
+  ArchSpec process_arch;
+  process_sp->DidAttach(process_arch);
+
+  return parsed_process;
+}
+
+Expected<std::vector<TraceIntelPTSessionFileParser::ParsedProcess>>
+TraceIntelPTSessionFileParser::ParseSessionFile(
+    const JSONTraceSession &session) {
+  std::vector<ParsedProcess> parsed_processes;
+
+  auto HandleError = [&](Error &&err) {
+    // Delete all targets that were created so far in case of failures
+    for (ParsedProcess &parsed_process : parsed_processes)
+      m_debugger.GetTargetList().DeleteTarget(parsed_process.target_sp);
+    return std::move(err);
+  };
+
+  for (const JSONProcess &process : session.processes) {
+    if (Expected<ParsedProcess> parsed_process = ParseProcess(process))
+      parsed_processes.push_back(std::move(*parsed_process));
+    else
+      return HandleError(parsed_process.takeError());
+  }
+  return parsed_processes;
+}
+
 StringRef TraceIntelPTSessionFileParser::GetSchema() {
   static std::string schema;
   if (schema.empty()) {
-    schema = TraceSessionFileParser::BuildSchema(R"({
-    "type": "intel-pt",
-    "cpuInfo": {
-      "vendor": "intel" | "unknown",
-      "family": integer,
-      "model": integer,
-      "stepping": integer
+    schema = R"({
+  "type": "intel-pt",
+  "cpuInfo": {
+    // CPU information gotten from, for example, /proc/cpuinfo.
+
+    "vendor": "GenuineIntel" | "unknown",
+    "family": integer,
+    "model": integer,
+    "stepping": integer
+  },
+  "processes": [
+    {
+      "pid": integer,
+      "triple": string,
+          // clang/llvm target triple.
+      "threads": [
+        {
+          "tid": integer,
+          "traceBuffer"?: string
+              // Path to the raw Intel PT buffer file for this thread.
+        }
+      ],
+      "modules": [
+        {
+          "systemPath": string,
+              // Original path of the module at runtime.
+          "file"?: string,
+              // Path to a copy of the file if not available at "systemPath".
+          "loadAddress": integer,
+              // Lowest address of the sections of the module loaded on memory.
+          "uuid"?: string,
+              // Build UUID for the file for sanity checks.
+        }
+      ]
+    }
+  ],
+  "cores"?: [
+    {
+      "coreId": integer,
+          // Id of this CPU core.
+      "traceBuffer": string,
+          // Path to the raw Intel PT buffer for this core.
+      "contextSwitchTrace": string,
+          // Path to the raw perf_event_open context switch trace file for this core.
     }
-  })");
+  ],
+  "tscPerfZeroConversion"?: {
+    // Values used to convert between TSCs and nanoseconds. See the time_zero
+    // section in https://man7.org/linux/man-pages/man2/perf_event_open.2.html
+    // for for information.
+
+    "timeMult": integer,
+    "timeShift": integer,
+    "timeZero": integer,
   }
-  return schema;
 }
 
-pt_cpu TraceIntelPTSessionFileParser::ParsePTCPU(
-    const JSONTraceIntelPTCPUInfo &cpu_info) {
-  return {cpu_info.vendor.compare("intel") == 0 ? pcv_intel : pcv_unknown,
-          static_cast<uint16_t>(cpu_info.family),
-          static_cast<uint8_t>(cpu_info.model),
-          static_cast<uint8_t>(cpu_info.stepping)};
+Notes:
+
+- All paths are either absolute or relative to folder containing the session file.
+- "cores" is provided if and only if processes[].threads[].traceBuffer is not provided.
+- "tscPerfZeroConversion" must be provided if "cores" is provided.
+ })";
+  }
+  return schema;
 }
 
 TraceSP TraceIntelPTSessionFileParser::CreateTraceIntelPTInstance(
-    const pt_cpu &cpu_info, std::vector<ParsedProcess> &parsed_processes) {
+    JSONTraceSession &session, std::vector<ParsedProcess> &parsed_processes) {
   std::vector<ThreadPostMortemTraceSP> threads;
-  for (const ParsedProcess &parsed_process : parsed_processes)
+  std::vector<ProcessSP> processes;
+  for (const ParsedProcess &parsed_process : parsed_processes) {
+    processes.push_back(parsed_process.target_sp->GetProcessSP());
     threads.insert(threads.end(), parsed_process.threads.begin(),
                    parsed_process.threads.end());
+  }
 
-  TraceSP trace_instance(new TraceIntelPT(cpu_info, threads));
+  TraceSP trace_instance(new TraceIntelPT(session, processes, threads));
   for (const ParsedProcess &parsed_process : parsed_processes)
     parsed_process.target_sp->SetTrace(trace_instance);
 
@@ -56,14 +233,13 @@ TraceSP TraceIntelPTSessionFileParser::CreateTraceIntelPTInstance(
 
 Expected<TraceSP> TraceIntelPTSessionFileParser::Parse() {
   json::Path::Root root("traceSession");
-  JSONTraceSession<JSONTraceIntelPTSettings> session;
-  if (!json::fromJSON(m_trace_session_file, session, root))
+  JSONTraceSession session;
+  if (!fromJSON(m_trace_session_file, session, root))
     return CreateJSONError(root, m_trace_session_file);
 
   if (Expected<std::vector<ParsedProcess>> parsed_processes =
-          ParseCommonSessionFile(session))
-    return CreateTraceIntelPTInstance(ParsePTCPU(session.trace.cpuInfo),
-                                      *parsed_processes);
+          ParseSessionFile(session))
+    return CreateTraceIntelPTInstance(session, *parsed_processes);
   else
     return parsed_processes.takeError();
 }

diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
index 6bf39cad1e168..9a54cc1854d13 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
@@ -9,7 +9,7 @@
 #ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTSESSIONFILEPARSER_H
 #define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTSESSIONFILEPARSER_H
 
-#include "../common/TraceSessionFileParser.h"
+#include "../common/ThreadPostMortemTrace.h"
 #include "TraceIntelPTJSONStructs.h"
 
 namespace lldb_private {
@@ -17,16 +17,28 @@ namespace trace_intel_pt {
 
 class TraceIntelPT;
 
-class TraceIntelPTSessionFileParser : public TraceSessionFileParser {
+class TraceIntelPTSessionFileParser {
 public:
+  /// Helper struct holding the objects created when parsing a process
+  struct ParsedProcess {
+    lldb::TargetSP target_sp;
+    std::vector<lldb::ThreadPostMortemTraceSP> threads;
+  };
 
-  /// See \a TraceSessionFileParser::TraceSessionFileParser for the description
-  /// of these fields.
+  /// \param[in] debugger
+  ///   The debugger that will own the targets to create.
+  ///
+  /// \param[in] trace_session_file
+  ///   The contents of the main trace session definition file that follows the
+  ///   schema of the intel pt trace plug-in.
+  ///
+  /// \param[in] session_file_dir
+  ///   The folder where the trace session file is located.
   TraceIntelPTSessionFileParser(Debugger &debugger,
                                 const llvm::json::Value &trace_session_file,
                                 llvm::StringRef session_file_dir)
-      : TraceSessionFileParser(debugger, session_file_dir, GetSchema()),
-        m_trace_session_file(trace_session_file) {}
+      : m_debugger(debugger), m_trace_session_file(trace_session_file),
+        m_session_file_dir(session_file_dir) {}
 
   /// \return
   ///   The JSON schema for the session data.
@@ -41,13 +53,41 @@ class TraceIntelPTSessionFileParser : public TraceSessionFileParser {
   llvm::Expected<lldb::TraceSP> Parse();
 
   lldb::TraceSP
-  CreateTraceIntelPTInstance(const pt_cpu &cpu_info,
+  CreateTraceIntelPTInstance(JSONTraceSession &session,
                              std::vector<ParsedProcess> &parsed_processes);
 
 private:
-  static pt_cpu ParsePTCPU(const JSONTraceIntelPTCPUInfo &cpu_info);
+  /// Resolve non-absolute paths relative to the session file folder. It
+  /// modifies the given file_spec.
+  void NormalizePath(lldb_private::FileSpec &file_spec);
+
+  lldb::ThreadPostMortemTraceSP ParseThread(lldb::ProcessSP &process_sp,
+                                            const JSONThread &thread);
+
+  llvm::Expected<ParsedProcess> ParseProcess(const JSONProcess &process);
+
+  llvm::Error ParseModule(lldb::TargetSP &target_sp, const JSONModule &module);
+
+  /// Create a user-friendly error message upon a JSON-parsing failure using the
+  /// \a json::ObjectMapper functionality.
+  ///
+  /// \param[in] root
+  ///   The \a llvm::json::Path::Root used to parse the JSON \a value.
+  ///
+  /// \param[in] value
+  ///   The json value that failed to parse.
+  ///
+  /// \return
+  ///   An \a llvm::Error containing the user-friendly error message.
+  llvm::Error CreateJSONError(llvm::json::Path::Root &root,
+                              const llvm::json::Value &value);
+
+  llvm::Expected<std::vector<ParsedProcess>>
+  ParseSessionFile(const JSONTraceSession &session);
 
+  Debugger &m_debugger;
   const llvm::json::Value &m_trace_session_file;
+  std::string m_session_file_dir;
 };
 
 } // namespace trace_intel_pt

diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp
index 9d6d7e4f68ee8..15e3ead908032 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp
@@ -7,7 +7,6 @@
 //===----------------------------------------------------------------------===//
 
 #include "TraceIntelPTSessionSaver.h"
-#include "../common/TraceSessionSaver.h"
 #include "TraceIntelPT.h"
 #include "TraceIntelPTJSONStructs.h"
 #include "lldb/Core/Module.h"
@@ -31,42 +30,280 @@ using namespace lldb_private;
 using namespace lldb_private::trace_intel_pt;
 using namespace llvm;
 
-llvm::Error TraceIntelPTSessionSaver::SaveToDisk(TraceIntelPT &trace_ipt,
-                                                 FileSpec directory) {
-  Process *live_process = trace_ipt.GetLiveProcess();
-  if (live_process == nullptr)
+static llvm::Error WriteBytesToDisk(FileSpec &output_file,
+                                    ArrayRef<uint8_t> data) {
+  std::basic_fstream<char> out_fs = std::fstream(
+      output_file.GetPath().c_str(), std::ios::out | std::ios::binary);
+  out_fs.write(reinterpret_cast<const char *>(&data[0]),
+               data.size() * sizeof(uint8_t));
+  out_fs.close();
+  if (!out_fs)
     return createStringError(inconvertibleErrorCode(),
-                             "Saving a trace requires a live process.");
+                             formatv("couldn't write to the file {0}",
+                                     output_file.GetPath().c_str()));
+  return Error::success();
+}
 
-  if (std::error_code ec =
-          sys::fs::create_directories(directory.GetPath().c_str()))
-    return llvm::errorCodeToError(ec);
+/// Save the trace session description JSON object inside the given directory
+/// as a file named \a trace.json.
+///
+/// \param[in] trace_session_json
+///     The trace's session, as JSON Object.
+///
+/// \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.
+static llvm::Error
+WriteSessionToFile(const llvm::json::Value &trace_session_json,
+                   const FileSpec &directory) {
+  FileSpec trace_path = directory;
+  trace_path.AppendPathComponent("trace.json");
+  std::ofstream os(trace_path.GetPath());
+  os << std::string(formatv("{0:2}", trace_session_json));
+  os.close();
+  if (!os)
+    return createStringError(inconvertibleErrorCode(),
+                             formatv("couldn't write to the file {0}",
+                                     trace_path.GetPath().c_str()));
+  return Error::success();
+}
+
+/// Build the threads sub-section of the trace session description file.
+/// Any associated binary files are created inside the given directory.
+///
+/// \param[in] process
+///     The process being traced.
+///
+/// \param[in] directory
+///     The directory where files will be saved when building the threads
+///     section.
+///
+/// \return
+///     The threads section or \a llvm::Error in case of failures.
+static llvm::Expected<std::vector<JSONThread>>
+BuildThreadsSection(Process &process, FileSpec directory) {
+  std::vector<JSONThread> json_threads;
+  TraceSP trace_sp = process.GetTarget().GetTrace();
+
+  FileSpec threads_dir = directory;
+  threads_dir.AppendPathComponent("threads");
+  FileSystem::Instance().Resolve(threads_dir);
+  sys::fs::create_directories(threads_dir.GetCString());
+
+  for (ThreadSP thread_sp : process.Threads()) {
+    lldb::tid_t tid = thread_sp->GetID();
+    if (!trace_sp->IsTraced(tid))
+      continue;
+
+    JSONThread json_thread;
+    json_thread.tid = tid;
+
+    if (trace_sp->GetTracedCores().empty()) {
+      FileSpec output_file = threads_dir;
+      output_file.AppendPathComponent(std::to_string(tid) + ".intelpt_trace");
+      json_thread.trace_buffer = output_file.GetPath();
+
+      llvm::Error err = process.GetTarget().GetTrace()->OnThreadBinaryDataRead(
+          tid, IntelPTDataKinds::kTraceBuffer,
+          [&](llvm::ArrayRef<uint8_t> data) -> llvm::Error {
+            return WriteBytesToDisk(output_file, data);
+          });
+      if (err)
+        return std::move(err);
+    }
+
+    json_threads.push_back(std::move(json_thread));
+  }
+  return json_threads;
+}
+
+static llvm::Expected<llvm::Optional<std::vector<JSONCore>>>
+BuildCoresSection(TraceIntelPT &trace_ipt, FileSpec directory) {
+  if (trace_ipt.GetTracedCores().empty())
+    return None;
+
+  std::vector<JSONCore> json_cores;
+  FileSpec cores_dir = directory;
+  cores_dir.AppendPathComponent("cores");
+  FileSystem::Instance().Resolve(cores_dir);
+  sys::fs::create_directories(cores_dir.GetCString());
+
+  for (lldb::core_id_t core_id : trace_ipt.GetTracedCores()) {
+    JSONCore json_core;
+    json_core.core_id = core_id;
+
+    {
+      FileSpec output_trace = cores_dir;
+      output_trace.AppendPathComponent(std::to_string(core_id) +
+                                       ".intelpt_trace");
+      json_core.trace_buffer = output_trace.GetPath();
+
+      llvm::Error err = trace_ipt.OnCoreBinaryDataRead(
+          core_id, IntelPTDataKinds::kTraceBuffer,
+          [&](llvm::ArrayRef<uint8_t> data) -> llvm::Error {
+            return WriteBytesToDisk(output_trace, data);
+          });
+      if (err)
+        return std::move(err);
+    }
 
-  llvm::Expected<JSONTraceIntelPTTrace> json_intel_pt_trace =
-      BuildTraceSection(trace_ipt);
-  if (!json_intel_pt_trace)
-    return json_intel_pt_trace.takeError();
+    {
+      FileSpec output_context_switch_trace = cores_dir;
+      output_context_switch_trace.AppendPathComponent(
+          std::to_string(core_id) + ".perf_context_switch_trace");
+      json_core.context_switch_trace = output_context_switch_trace.GetPath();
 
-  llvm::Expected<JSONTraceSessionBase> json_session_description =
-      TraceSessionSaver::BuildProcessesSection(
-          *live_process, IntelPTDataKinds::kTraceBuffer, directory);
+      llvm::Error err = trace_ipt.OnCoreBinaryDataRead(
+          core_id, IntelPTDataKinds::kPerfContextSwitchTrace,
+          [&](llvm::ArrayRef<uint8_t> data) -> llvm::Error {
+            return WriteBytesToDisk(output_context_switch_trace, data);
+          });
+      if (err)
+        return std::move(err);
+    }
+    json_cores.push_back(std::move(json_core));
+  }
+  return json_cores;
+}
+
+/// Build modules sub-section of the trace's session. The original modules
+/// will be copied over to the \a <directory/modules> folder. Invalid modules
+/// are skipped.
+/// Copying the modules has the benefit of making these trace session
+/// directories self-contained, as the raw traces and modules are part of the
+/// output directory and can be sent to another machine, where lldb can load
+/// them and replicate exactly the same trace session.
+///
+/// \param[in] process
+///     The process being traced.
+///
+/// \param[in] directory
+///     The directory where the modules files will be saved when building
+///     the modules section.
+///     Example: If a module \a libbar.so exists in the path
+///     \a /usr/lib/foo/libbar.so, then it will be copied to
+///     \a <directory>/modules/usr/lib/foo/libbar.so.
+///
+/// \return
+///     The modules section or \a llvm::Error in case of failures.
+static llvm::Expected<std::vector<JSONModule>>
+BuildModulesSection(Process &process, FileSpec directory) {
+  std::vector<JSONModule> json_modules;
+  ModuleList module_list = process.GetTarget().GetImages();
+  for (size_t i = 0; i < module_list.GetSize(); ++i) {
+    ModuleSP module_sp(module_list.GetModuleAtIndex(i));
+    if (!module_sp)
+      continue;
+    std::string system_path = module_sp->GetPlatformFileSpec().GetPath();
+    // TODO: support memory-only libraries like [vdso]
+    if (!module_sp->GetFileSpec().IsAbsolute())
+      continue;
+
+    std::string file = module_sp->GetFileSpec().GetPath();
+    ObjectFile *objfile = module_sp->GetObjectFile();
+    if (objfile == nullptr)
+      continue;
+
+    lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;
+    Address base_addr(objfile->GetBaseAddress());
+    if (base_addr.IsValid() &&
+        !process.GetTarget().GetSectionLoadList().IsEmpty())
+      load_addr = base_addr.GetLoadAddress(&process.GetTarget());
+
+    if (load_addr == LLDB_INVALID_ADDRESS)
+      continue;
 
-  if (!json_session_description)
-    return json_session_description.takeError();
+    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());
 
-  JSONTraceIntelPTSession json_intel_pt_session{json_intel_pt_trace.get(),
-                                                json_session_description.get()};
+    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()));
 
-  return TraceSessionSaver::WriteSessionToFile(
-      llvm::json::toJSON(json_intel_pt_session), directory);
+    json_modules.push_back(JSONModule{system_path,
+                                      path_to_copy_module.GetPath(), load_addr,
+                                      module_sp->GetUUID().GetAsString()});
+  }
+  return json_modules;
 }
 
-llvm::Expected<JSONTraceIntelPTTrace>
-TraceIntelPTSessionSaver::BuildTraceSection(TraceIntelPT &trace_ipt) {
-  llvm::Expected<pt_cpu> cpu_info = trace_ipt.GetCPUInfo();
+/// Build the processes section of the trace session description file. Besides
+/// returning the processes information, this method saves to disk all modules
+/// and raw traces corresponding to the traced threads of the given process.
+///
+/// \param[in] process
+///     The process being traced.
+///
+/// \param[in] directory
+///     The directory where files will be saved when building the processes
+///     section.
+///
+/// \return
+///     The processes section or \a llvm::Error in case of failures.
+static llvm::Expected<JSONProcess>
+BuildProcessSection(Process &process, const FileSpec &directory) {
+  Expected<std::vector<JSONThread>> json_threads =
+      BuildThreadsSection(process, directory);
+  if (!json_threads)
+    return json_threads.takeError();
+
+  Expected<std::vector<JSONModule>> json_modules =
+      BuildModulesSection(process, directory);
+  if (!json_modules)
+    return json_modules.takeError();
+
+  return JSONProcess{
+      static_cast<int64_t>(process.GetID()),
+      process.GetTarget().GetArchitecture().GetTriple().getTriple(),
+      json_threads.get(), json_modules.get()};
+}
+
+/// See BuildProcessSection()
+static llvm::Expected<std::vector<JSONProcess>>
+BuildProcessesSection(TraceIntelPT &trace_ipt, const FileSpec &directory) {
+  std::vector<JSONProcess> processes;
+  for (Process *process : trace_ipt.GetAllProcesses()) {
+    if (llvm::Expected<JSONProcess> json_process =
+            BuildProcessSection(*process, directory))
+      processes.push_back(std::move(*json_process));
+    else
+      return json_process.takeError();
+  }
+  return processes;
+}
+
+Error TraceIntelPTSessionSaver::SaveToDisk(TraceIntelPT &trace_ipt,
+                                           FileSpec directory) {
+  if (std::error_code ec =
+          sys::fs::create_directories(directory.GetPath().c_str()))
+    return llvm::errorCodeToError(ec);
+
+  Expected<pt_cpu> cpu_info = trace_ipt.GetCPUInfo();
   if (!cpu_info)
     return cpu_info.takeError();
 
-  return JSONTraceIntelPTTrace{"intel-pt",
-                               JSONTraceIntelPTCPUInfo(cpu_info.get())};
+  Expected<std::vector<JSONProcess>> json_processes =
+      BuildProcessesSection(trace_ipt, directory);
+
+  if (!json_processes)
+    return json_processes.takeError();
+
+  Expected<Optional<std::vector<JSONCore>>> json_cores =
+      BuildCoresSection(trace_ipt, directory);
+  if (!json_cores)
+    return json_cores.takeError();
+
+  JSONTraceSession json_intel_pt_session{"intel-pt", *cpu_info, *json_processes,
+                                         *json_cores,
+                                         trace_ipt.GetPerfZeroTscConversion()};
+
+  return WriteSessionToFile(toJSON(json_intel_pt_session), directory);
 }

diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.h
index 943519f959e9d..e7ddff813992f 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.h
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.h
@@ -11,15 +11,12 @@
 
 #include "TraceIntelPT.h"
 
-#include "../common/TraceJSONStructs.h"
+#include "TraceIntelPTJSONStructs.h"
 
 namespace lldb_private {
 namespace trace_intel_pt {
 
-class TraceIntelPT;
-
 class TraceIntelPTSessionSaver {
-
 public:
   /// Save the Intel PT trace of a live process to the specified directory,
   /// which will be created if needed. This will also create a file
@@ -38,17 +35,6 @@ class TraceIntelPTSessionSaver {
   ///     \a llvm::success if the operation was successful, or an \a llvm::Error
   ///     otherwise.
   llvm::Error SaveToDisk(TraceIntelPT &trace_ipt, FileSpec directory);
-
-private:
-  /// Build trace section of the intel-pt trace session description file.
-  ///
-  /// \param[in] trace_ipt
-  ///     The Intel PT trace.
-  ///
-  /// \return
-  ///     The trace section  an \a llvm::Error in case of failures.
-  llvm::Expected<JSONTraceIntelPTTrace>
-  BuildTraceSection(TraceIntelPT &trace_ipt);
 };
 
 } // namespace trace_intel_pt

diff  --git a/lldb/source/Target/Trace.cpp b/lldb/source/Target/Trace.cpp
index 16d816e640181..5c16482acae3a 100644
--- a/lldb/source/Target/Trace.cpp
+++ b/lldb/source/Target/Trace.cpp
@@ -27,26 +27,16 @@ using namespace llvm;
 // Helper structs used to extract the type of a trace session json without
 // having to parse the entire object.
 
-struct JSONSimplePluginSettings {
-  std::string type;
-};
-
 struct JSONSimpleTraceSession {
-  JSONSimplePluginSettings trace;
+  std::string type;
 };
 
 namespace llvm {
 namespace json {
 
-bool fromJSON(const Value &value, JSONSimplePluginSettings &plugin_settings,
-              Path path) {
-  json::ObjectMapper o(value, path);
-  return o && o.map("type", plugin_settings.type);
-}
-
 bool fromJSON(const Value &value, JSONSimpleTraceSession &session, Path path) {
   json::ObjectMapper o(value, path);
-  return o && o.map("trace", session.trace);
+  return o && o.map("type", session.type);
 }
 
 } // namespace json
@@ -69,10 +59,10 @@ Trace::FindPluginForPostMortemProcess(Debugger &debugger,
     return root.getError();
 
   if (auto create_callback =
-          PluginManager::GetTraceCreateCallback(json_session.trace.type))
+          PluginManager::GetTraceCreateCallback(json_session.type))
     return create_callback(trace_session_file, session_file_dir, debugger);
 
-  return createInvalidPlugInError(json_session.trace.type);
+  return createInvalidPlugInError(json_session.type);
 }
 
 Expected<lldb::TraceSP> Trace::FindPluginForLiveProcess(llvm::StringRef name,
@@ -136,6 +126,18 @@ Optional<size_t> Trace::GetLiveThreadBinaryDataSize(lldb::tid_t tid,
   return single_thread_data_it->second;
 }
 
+Optional<size_t> Trace::GetLiveCoreBinaryDataSize(lldb::core_id_t core_id,
+                                                  llvm::StringRef kind) {
+  auto it = m_live_core_data.find(core_id);
+  if (it == m_live_core_data.end())
+    return None;
+  std::unordered_map<std::string, size_t> &single_core_data = it->second;
+  auto single_thread_data_it = single_core_data.find(kind.str());
+  if (single_thread_data_it == single_core_data.end())
+    return None;
+  return single_thread_data_it->second;
+}
+
 Optional<size_t> Trace::GetLiveProcessBinaryDataSize(llvm::StringRef kind) {
   auto data_it = m_live_process_data.find(kind.str());
   if (data_it == m_live_process_data.end())
@@ -155,8 +157,26 @@ Trace::GetLiveThreadBinaryData(lldb::tid_t tid, llvm::StringRef kind) {
         "Tracing data \"%s\" is not available for thread %" PRIu64 ".",
         kind.data(), tid);
 
-  TraceGetBinaryDataRequest request{GetPluginName().str(), kind.str(), tid, 0,
-                                    *size};
+  TraceGetBinaryDataRequest request{GetPluginName().str(), kind.str(),   tid,
+                                    /*core_id=*/None,      /*offset=*/0, *size};
+  return m_live_process->TraceGetBinaryData(request);
+}
+
+Expected<std::vector<uint8_t>>
+Trace::GetLiveCoreBinaryData(lldb::core_id_t core_id, llvm::StringRef kind) {
+  if (!m_live_process)
+    return createStringError(inconvertibleErrorCode(),
+                             "Tracing requires a live process.");
+  llvm::Optional<size_t> size = GetLiveCoreBinaryDataSize(core_id, kind);
+  if (!size)
+    return createStringError(
+        inconvertibleErrorCode(),
+        "Tracing data \"%s\" is not available for core_id %" PRIu64 ".",
+        kind.data(), core_id);
+
+  TraceGetBinaryDataRequest request{GetPluginName().str(), kind.str(),
+                                    /*tid=*/None,          core_id,
+                                    /*offset=*/0,          *size};
   return m_live_process->TraceGetBinaryData(request);
 }
 
@@ -171,8 +191,9 @@ Trace::GetLiveProcessBinaryData(llvm::StringRef kind) {
         inconvertibleErrorCode(),
         "Tracing data \"%s\" is not available for the process.", kind.data());
 
-  TraceGetBinaryDataRequest request{GetPluginName().str(), kind.str(), None, 0,
-                                    *size};
+  TraceGetBinaryDataRequest request{GetPluginName().str(), kind.str(),
+                                    /*tid=*/None,          /*core_id*/ None,
+                                    /*offset=*/0,          *size};
   return m_live_process->TraceGetBinaryData(request);
 }
 
@@ -190,6 +211,7 @@ const char *Trace::RefreshLiveProcessState() {
   m_stop_id = new_stop_id;
   m_live_thread_data.clear();
   m_live_refresh_error.reset();
+  m_cores.reset();
 
   auto HandleError = [&](Error &&err) -> const char * {
     m_live_refresh_error = toString(std::move(err));
@@ -205,8 +227,10 @@ const char *Trace::RefreshLiveProcessState() {
   if (!live_process_state)
     return HandleError(live_process_state.takeError());
 
-  for (std::string &warning : live_process_state->warnings)
-    LLDB_LOG(log, "Warning when fetching the trace state: {0}", warning);
+  if (live_process_state->warnings) {
+    for (std::string &warning : *live_process_state->warnings)
+      LLDB_LOG(log, "== Warning when fetching the trace state: {0}", warning);
+  }
 
   for (const TraceThreadState &thread_state :
        live_process_state->traced_threads) {
@@ -214,6 +238,21 @@ const char *Trace::RefreshLiveProcessState() {
       m_live_thread_data[thread_state.tid][item.kind] = item.size;
   }
 
+  LLDB_LOG(log, "== Found {0} threads being traced",
+           live_process_state->traced_threads.size());
+
+  if (live_process_state->cores) {
+    m_cores.emplace();
+    for (const TraceCoreState &core_state : *live_process_state->cores) {
+      m_cores->push_back(core_state.core_id);
+      for (const TraceBinaryData &item : core_state.binary_data)
+        m_live_core_data[core_state.core_id][item.kind] = item.size;
+    }
+    LLDB_LOG(log, "== Found {0} cpu cores being traced",
+            live_process_state->cores->size());
+  }
+
+
   for (const TraceBinaryData &item : live_process_state->process_binary_data)
     m_live_process_data[item.kind] = item.size;
 
@@ -224,8 +263,25 @@ const char *Trace::RefreshLiveProcessState() {
   return nullptr;
 }
 
+Trace::Trace(ArrayRef<ProcessSP> postmortem_processes,
+             Optional<std::vector<lldb::core_id_t>> postmortem_cores) {
+  for (ProcessSP process_sp : postmortem_processes)
+    m_postmortem_processes.push_back(process_sp.get());
+  m_cores = postmortem_cores;
+}
+
 Process *Trace::GetLiveProcess() { return m_live_process; }
 
+ArrayRef<Process *> Trace::GetPostMortemProcesses() {
+  return m_postmortem_processes;
+}
+
+std::vector<Process *> Trace::GetAllProcesses() {
+  if (Process *proc = GetLiveProcess())
+    return {proc};
+  return GetPostMortemProcesses();
+}
+
 uint32_t Trace::GetStopID() {
   RefreshLiveProcessState();
   return m_stop_id;
@@ -252,11 +308,39 @@ Trace::GetPostMortemThreadDataFile(lldb::tid_t tid, llvm::StringRef kind) {
   return it2->second;
 }
 
+llvm::Expected<FileSpec>
+Trace::GetPostMortemCoreDataFile(lldb::core_id_t core_id,
+                                 llvm::StringRef kind) {
+  auto NotFoundError = [&]() {
+    return createStringError(
+        inconvertibleErrorCode(),
+        formatv("The core with id={0} doesn't have the tracing data {1}",
+                core_id, kind));
+  };
+
+  auto it = m_postmortem_core_data.find(core_id);
+  if (it == m_postmortem_core_data.end())
+    return NotFoundError();
+
+  std::unordered_map<std::string, FileSpec> &data_kind_to_file_spec_map =
+      it->second;
+  auto it2 = data_kind_to_file_spec_map.find(kind.str());
+  if (it2 == data_kind_to_file_spec_map.end())
+    return NotFoundError();
+  return it2->second;
+}
+
 void Trace::SetPostMortemThreadDataFile(lldb::tid_t tid, llvm::StringRef kind,
                                         FileSpec file_spec) {
   m_postmortem_thread_data[tid][kind.str()] = file_spec;
 }
 
+void Trace::SetPostMortemCoreDataFile(lldb::core_id_t core_id,
+                                      llvm::StringRef kind,
+                                      FileSpec file_spec) {
+  m_postmortem_core_data[core_id][kind.str()] = file_spec;
+}
+
 llvm::Error
 Trace::OnLiveThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind,
                                   OnBinaryDataReadCallback callback) {
@@ -266,14 +350,19 @@ Trace::OnLiveThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind,
   return callback(*data);
 }
 
-llvm::Error
-Trace::OnPostMortemThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind,
-                                        OnBinaryDataReadCallback callback) {
-  Expected<FileSpec> file = GetPostMortemThreadDataFile(tid, kind);
-  if (!file)
-    return file.takeError();
+llvm::Error Trace::OnLiveCoreBinaryDataRead(lldb::core_id_t core_id,
+                                            llvm::StringRef kind,
+                                            OnBinaryDataReadCallback callback) {
+  Expected<std::vector<uint8_t>> data = GetLiveCoreBinaryData(core_id, kind);
+  if (!data)
+    return data.takeError();
+  return callback(*data);
+}
+
+llvm::Error Trace::OnDataFileRead(FileSpec file,
+                                  OnBinaryDataReadCallback callback) {
   ErrorOr<std::unique_ptr<MemoryBuffer>> trace_or_error =
-      MemoryBuffer::getFile(file->GetPath());
+      MemoryBuffer::getFile(file.GetPath());
   if (std::error_code err = trace_or_error.getError())
     return errorCodeToError(err);
 
@@ -284,10 +373,47 @@ Trace::OnPostMortemThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind,
   return callback(array_ref);
 }
 
+llvm::Error
+Trace::OnPostMortemThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind,
+                                        OnBinaryDataReadCallback callback) {
+  Expected<FileSpec> file = GetPostMortemThreadDataFile(tid, kind);
+  if (!file)
+    return file.takeError();
+  return OnDataFileRead(*file, callback);
+}
+
+llvm::Error
+Trace::OnPostMortemCoreBinaryDataRead(lldb::core_id_t core_id,
+                                      llvm::StringRef kind,
+                                      OnBinaryDataReadCallback callback) {
+  Expected<FileSpec> file = GetPostMortemCoreDataFile(core_id, kind);
+  if (!file)
+    return file.takeError();
+  return OnDataFileRead(*file, callback);
+}
+
 llvm::Error Trace::OnThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind,
                                           OnBinaryDataReadCallback callback) {
+  RefreshLiveProcessState();
   if (m_live_process)
     return OnLiveThreadBinaryDataRead(tid, kind, callback);
   else
     return OnPostMortemThreadBinaryDataRead(tid, kind, callback);
 }
+
+llvm::Error Trace::OnCoreBinaryDataRead(lldb::core_id_t core_id,
+                                        llvm::StringRef kind,
+                                        OnBinaryDataReadCallback callback) {
+  RefreshLiveProcessState();
+  if (m_live_process)
+    return OnLiveCoreBinaryDataRead(core_id, kind, callback);
+  else
+    return OnPostMortemCoreBinaryDataRead(core_id, kind, callback);
+}
+
+ArrayRef<lldb::core_id_t> Trace::GetTracedCores() {
+  RefreshLiveProcessState();
+  if (m_cores)
+    return *m_cores;
+  return {};
+}

diff  --git a/lldb/source/Utility/TraceGDBRemotePackets.cpp b/lldb/source/Utility/TraceGDBRemotePackets.cpp
index 313de1a271f4e..944cfd78e36c8 100644
--- a/lldb/source/Utility/TraceGDBRemotePackets.cpp
+++ b/lldb/source/Utility/TraceGDBRemotePackets.cpp
@@ -101,7 +101,7 @@ bool fromJSON(const json::Value &value, TraceGetStateResponse &packet,
   return o && o.map("tracedThreads", packet.traced_threads) &&
          o.map("processBinaryData", packet.process_binary_data) &&
          o.map("cores", packet.cores) &&
-         o.mapOptional("warnings", packet.warnings);
+         o.map("warnings", packet.warnings);
 }
 
 json::Value toJSON(const TraceGetStateResponse &packet) {
@@ -111,6 +111,12 @@ json::Value toJSON(const TraceGetStateResponse &packet) {
                             {"warnings", packet.warnings}});
 }
 
+void TraceGetStateResponse::AddWarning(StringRef warning) {
+  if (!warnings)
+    warnings.emplace();
+  warnings->push_back(warning.data());
+}
+
 bool fromJSON(const json::Value &value, TraceCoreState &packet,
               json::Path path) {
   ObjectMapper o(value, path);
@@ -135,6 +141,7 @@ json::Value toJSON(const TraceGetBinaryDataRequest &packet) {
                             {"kind", packet.kind},
                             {"offset", packet.offset},
                             {"tid", packet.tid},
+                            {"coreId", packet.core_id},
                             {"size", packet.size}});
 }
 
@@ -143,7 +150,7 @@ bool fromJSON(const json::Value &value, TraceGetBinaryDataRequest &packet,
   ObjectMapper o(value, path);
   return o && o.map("type", packet.type) && o.map("kind", packet.kind) &&
          o.map("tid", packet.tid) && o.map("offset", packet.offset) &&
-         o.map("size", packet.size);
+         o.map("size", packet.size) && o.map("coreId", packet.core_id);
 }
 /// \}
 

diff  --git a/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp b/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp
index 505d7262b643a..6ffc3e95cb37f 100644
--- a/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp
+++ b/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp
@@ -60,7 +60,6 @@ std::chrono::nanoseconds LinuxPerfZeroTscConversion::ToNanos(uint64_t tsc) {
 
 json::Value toJSON(const LinuxPerfZeroTscConversion &packet) {
   return json::Value(json::Object{
-      {"kind", "tscPerfZeroConversion"},
       {"timeMult", packet.time_mult},
       {"timeShift", packet.time_shift},
       {"timeZero", packet.time_zero},

diff  --git a/lldb/test/API/commands/trace/TestTraceLoad.py b/lldb/test/API/commands/trace/TestTraceLoad.py
index 731cade7587bc..d1351c09ef6e1 100644
--- a/lldb/test/API/commands/trace/TestTraceLoad.py
+++ b/lldb/test/API/commands/trace/TestTraceLoad.py
@@ -62,23 +62,24 @@ def testLoadInvalidTraces(self):
 
 Context:
 {
+  "cpuInfo": { ... },
   "processes": [
     /* error: expected object */
     123
   ],
-  "trace": { ... }
+  "type": "intel-pt"
 }
 
 Schema:
 {
-  "trace": {
-    "type": "intel-pt",
-    "cpuInfo": {
-      "vendor": "intel" | "unknown",
-      "family": integer,
-      "model": integer,
-      "stepping": integer
-    }
+  "type": "intel-pt",
+  "cpuInfo": {
+    // CPU information gotten from, for example, /proc/cpuinfo.
+
+    "vendor": "GenuineIntel" | "unknown",
+    "family": integer,
+    "model": integer,
+    "stepping": integer
   },'''])
 
         # Now we test a missing field in the global session file
@@ -87,25 +88,22 @@ def testLoadInvalidTraces(self):
 
         # Now we test a missing field in the intel-pt settings
         self.expect("trace load -v " + os.path.join(src_dir, "intelpt-trace", "trace_bad4.json"), error=True,
-            substrs=['''error: missing value at traceSession.trace.cpuInfo.family
+            substrs=['''error: missing value at traceSession.cpuInfo.family
 
 Context:
 {
+  "cpuInfo": /* error: missing value */ {
+    "model": 79,
+    "stepping": 1,
+    "vendor": "GenuineIntel"
+  },
   "processes": [],
-  "trace": {
-    "cpuInfo": /* error: missing value */ {
-      "model": 79,
-      "stepping": 1,
-      "vendor": "intel"
-    },
-    "type": "intel-pt"
-  }
+  "type": "intel-pt"
 }''', "Schema"])
 
         # Now we test an incorrect load address in the intel-pt settings
         self.expect("trace load -v " + os.path.join(src_dir, "intelpt-trace", "trace_bad5.json"), error=True,
-            substrs=['error: expected numeric string at traceSession.processes[0].modules[0].loadAddress',
-                     '"loadAddress": /* error: expected numeric string */ 400000,', "Schema"])
+            substrs=['error: missing value at traceSession.processes[1].pid', "Schema"])
 
         # The following wrong schema will have a valid target and an invalid one. In the case of failure,
         # no targets should be created.

diff  --git a/lldb/test/API/commands/trace/TestTraceSave.py b/lldb/test/API/commands/trace/TestTraceSave.py
index 4037c729ca924..9b82e636d0ae6 100644
--- a/lldb/test/API/commands/trace/TestTraceSave.py
+++ b/lldb/test/API/commands/trace/TestTraceSave.py
@@ -1,9 +1,15 @@
 import lldb
+import json
 from intelpt_testcase import *
 from lldbsuite.test.lldbtest import *
 from lldbsuite.test import lldbutil
 from lldbsuite.test.decorators import *
 
+def find(predicate, seq):
+  for item in seq:
+    if predicate(item):
+      return item
+
 class TestTraceSave(TraceIntelPTTestCaseBase):
     mydir = TestBase.compute_mydir(__file__)
 
@@ -54,10 +60,79 @@ def testSaveWhenNotLiveTrace(self):
 
         # Check the output when not doing live tracing
         self.expect("process trace save -d " +
-            os.path.join(self.getBuildDir(), "intelpt-trace", "trace_not_live_dir"),
-            substrs=["error: Saving a trace requires a live process."],
-            error=True)
+            os.path.join(self.getBuildDir(), "intelpt-trace", "trace_not_live_dir"))
+
+    def testSaveMultiCoreTrace(self):
+        '''
+            This test starts a per-core tracing session, then saves the session to disk, and
+            finally it loads it again.
+        '''
+        self.skipIfPerCoreTracingIsNotSupported()
+
+        self.expect("target create " +
+            os.path.join(self.getSourceDir(), "intelpt-trace", "a.out"))
+        self.expect("b main")
+        self.expect("r")
+        self.expect("process trace start --per-core-tracing")
+        self.expect("b 7")
 
+        output_dir = os.path.join(self.getBuildDir(), "intelpt-trace", "trace_save")
+        self.expect("process trace save -d " + output_dir)
+
+        def checkSessionBundle(session_file_path):
+            with open(session_file_path) as session_file:
+                session = json.load(session_file)
+                # We expect tsc conversion info
+                self.assertTrue("tscPerfZeroConversion" in session)
+                # We expect at least one core
+                self.assertGreater(len(session["cores"]), 0)
+
+                # We expect the required trace files to be created
+                for core in session["cores"]:
+                    core_files_prefix = os.path.join(output_dir, "cores", str(core["coreId"]))
+                    self.assertTrue(os.path.exists(core_files_prefix + ".intelpt_trace"))
+                    self.assertTrue(os.path.exists(core_files_prefix + ".perf_context_switch_trace"))
+
+                # We expect at least one one process
+                self.assertGreater(len(session["processes"]), 0)
+                for process in session["processes"]:
+                    # We expect at least one thread
+                    self.assertGreater(len(process["threads"]), 0)
+                    # We don't expect thread traces
+                    for thread in process["threads"]:
+                        self.assertTrue(("traceBuffer" not in thread) or (thread["traceBuffer"] is None))
+
+        original_trace_session_file = os.path.join(output_dir, "trace.json")
+        checkSessionBundle(original_trace_session_file)
+
+        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)
+
+        # We now check that the new bundle is correct on its own
+        copied_trace_session_file = os.path.join(output_copy_dir, "trace.json")
+        checkSessionBundle(copied_trace_session_file)
+
+        # We finally check that the new bundle has the same information as the original one
+        with open(original_trace_session_file) as original_file:
+            original = json.load(original_file)
+            with open(copied_trace_session_file) as copy_file:
+                copy = json.load(copy_file)
+
+                self.assertEqual(len(original["processes"]), len(copy["processes"]))
+
+                for process in original["processes"]:
+                    copied_process = find(lambda proc : proc["pid"] == process["pid"], copy["processes"])
+                    self.assertTrue(copied_process is not None)
+
+                    for thread in process["threads"]:
+                        copied_thread = find(lambda thr : thr["tid"] == thread["tid"], copied_process["threads"])
+                        self.assertTrue(copied_thread is not None)
+
+                for core in original["cores"]:
+                    copied_core = find(lambda cor : cor["coreId"] == core["coreId"], copy["cores"])
+                    self.assertTrue(copied_core is not None)
 
     def testSaveTrace(self):
         self.expect("target create " +

diff  --git a/lldb/test/API/commands/trace/TestTraceSchema.py b/lldb/test/API/commands/trace/TestTraceSchema.py
index cd80c5479f72c..8ba2809537b7a 100644
--- a/lldb/test/API/commands/trace/TestTraceSchema.py
+++ b/lldb/test/API/commands/trace/TestTraceSchema.py
@@ -9,7 +9,7 @@ class TestTraceLoad(TraceIntelPTTestCaseBase):
     mydir = TestBase.compute_mydir(__file__)
 
     def testSchema(self):
-        self.expect("trace schema intel-pt", substrs=["trace", "triple", "threads", "traceFile"])
+        self.expect("trace schema intel-pt", substrs=["triple", "threads", "traceBuffer"])
 
     def testInvalidPluginSchema(self):
         self.expect("trace schema invalid-plugin", error=True,
@@ -17,12 +17,12 @@ def testInvalidPluginSchema(self):
 
     def testAllSchemas(self):
         self.expect("trace schema all", substrs=['''{
-  "trace": {
-    "type": "intel-pt",
-    "cpuInfo": {
-      "vendor": "intel" | "unknown",
-      "family": integer,
-      "model": integer,
-      "stepping": integer
-    }
+  "type": "intel-pt",
+  "cpuInfo": {
+    // CPU information gotten from, for example, /proc/cpuinfo.
+
+    "vendor": "GenuineIntel" | "unknown",
+    "family": integer,
+    "model": integer,
+    "stepping": integer
   },'''])

diff  --git a/lldb/test/API/commands/trace/intelpt-trace-multi-file/multi-file-no-ld.json b/lldb/test/API/commands/trace/intelpt-trace-multi-file/multi-file-no-ld.json
index b8c6d32473dbd..6b44b92edd58e 100644
--- a/lldb/test/API/commands/trace/intelpt-trace-multi-file/multi-file-no-ld.json
+++ b/lldb/test/API/commands/trace/intelpt-trace-multi-file/multi-file-no-ld.json
@@ -1,12 +1,10 @@
 {
-  "trace": {
-    "type": "intel-pt",
-    "cpuInfo": {
-      "vendor": "intel",
-      "family": 6,
-      "model": 79,
-      "stepping": 1
-    }
+  "type": "intel-pt",
+  "cpuInfo": {
+    "vendor": "GenuineIntel",
+    "family": 6,
+    "model": 79,
+    "stepping": 1
   },
   "processes": [
     {
@@ -15,26 +13,26 @@
       "threads": [
         {
           "tid": 815455,
-          "traceFile": "multi-file.trace"
+          "traceBuffer": "multi-file.trace"
         }
       ],
       "modules": [
         {
           "file": "a.out",
           "systemPath": "a.out",
-          "loadAddress": "0x0000000000400000",
+          "loadAddress": 4194304,
           "uuid": "D2414468-7112-B7C5-408D-FF07E30D5B17-A5BFD2C4"
         },
         {
           "file": "libfoo.so",
           "systemPath": "libfoo.so",
-          "loadAddress": "0x00007ffff7bd9000",
+          "loadAddress": 140737349783552,
           "uuid": "B30FFEDA-8BB2-3D08-4580-C5937ED11E2B-21BE778C"
         },
         {
           "file": "libbar.so",
           "systemPath": "libbar.so",
-          "loadAddress": "0x00007ffff79d7000",
+          "loadAddress": 140737347678208,
           "uuid": "6633B038-EA73-D1A6-FF9A-7D0C0EDF733D-95FEA2CC"
         }
       ]

diff  --git a/lldb/test/API/commands/trace/intelpt-trace/trace.json b/lldb/test/API/commands/trace/intelpt-trace/trace.json
index 9104c572e3fb7..d284817591094 100644
--- a/lldb/test/API/commands/trace/intelpt-trace/trace.json
+++ b/lldb/test/API/commands/trace/intelpt-trace/trace.json
@@ -1,12 +1,10 @@
 {
-  "trace": {
-    "type": "intel-pt",
-    "cpuInfo": {
-      "vendor": "intel",
-      "family": 6,
-      "model": 79,
-      "stepping": 1
-    }
+  "type": "intel-pt",
+  "cpuInfo": {
+    "vendor": "GenuineIntel",
+    "family": 6,
+    "model": 79,
+    "stepping": 1
   },
   "processes": [
     {
@@ -15,14 +13,14 @@
       "threads": [
         {
           "tid": 3842849,
-          "traceFile": "3842849.trace"
+          "traceBuffer": "3842849.trace"
         }
       ],
       "modules": [
         {
           "file": "a.out",
           "systemPath": "a.out",
-          "loadAddress": "0x0000000000400000",
+          "loadAddress": 4194304,
           "uuid": "6AA9A4E2-6F28-2F33-377D-59FECE874C71-5B41261A"
         }
       ]

diff  --git a/lldb/test/API/commands/trace/intelpt-trace/trace_2threads.json b/lldb/test/API/commands/trace/intelpt-trace/trace_2threads.json
index f73944d1fa186..5bd3d35ec08b6 100644
--- a/lldb/test/API/commands/trace/intelpt-trace/trace_2threads.json
+++ b/lldb/test/API/commands/trace/intelpt-trace/trace_2threads.json
@@ -1,12 +1,10 @@
 {
-  "trace": {
-    "type": "intel-pt",
-    "cpuInfo": {
-      "vendor": "intel",
-      "family": 6,
-      "model": 79,
-      "stepping": 1
-    }
+  "type": "intel-pt",
+  "cpuInfo": {
+    "vendor": "GenuineIntel",
+    "family": 6,
+    "model": 79,
+    "stepping": 1
   },
   "processes": [
     {
@@ -15,18 +13,18 @@
       "threads": [
         {
           "tid": 3842849,
-          "traceFile": "3842849.trace"
+          "traceBuffer": "3842849.trace"
         },
         {
           "tid": 3842850,
-          "traceFile": "3842849.trace"
+          "traceBuffer": "3842849.trace"
         }
       ],
       "modules": [
         {
           "file": "a.out",
           "systemPath": "a.out",
-          "loadAddress": "0x0000000000400000",
+          "loadAddress": 4194304,
           "uuid": "6AA9A4E2-6F28-2F33-377D-59FECE874C71-5B41261A"
         }
       ]

diff  --git a/lldb/test/API/commands/trace/intelpt-trace/trace_bad.json b/lldb/test/API/commands/trace/intelpt-trace/trace_bad.json
index 0232f9ffb80d4..5da88abc6ec1c 100644
--- a/lldb/test/API/commands/trace/intelpt-trace/trace_bad.json
+++ b/lldb/test/API/commands/trace/intelpt-trace/trace_bad.json
@@ -1,12 +1,10 @@
 {
-  "trace": {
-    "type": "intel-pt",
-    "cpuInfo": {
-      "vendor": "intel",
-      "family": 6,
-      "model": 79,
-      "stepping": 1
-    }
+  "type": "intel-pt",
+  "cpuInfo": {
+    "vendor": "GenuineIntel",
+    "family": 6,
+    "model": 79,
+    "stepping": 1
   },
   "processes": [
     123

diff  --git a/lldb/test/API/commands/trace/intelpt-trace/trace_bad2.json b/lldb/test/API/commands/trace/intelpt-trace/trace_bad2.json
index f944c7362d048..46fa01abf45b9 100644
--- a/lldb/test/API/commands/trace/intelpt-trace/trace_bad2.json
+++ b/lldb/test/API/commands/trace/intelpt-trace/trace_bad2.json
@@ -1,12 +1,10 @@
 {
-  "trace": {
-    "type": "intel-pt",
-    "cpuInfo": {
-      "vendor": "intel",
-      "family": 6,
-      "model": 79,
-      "stepping": 1
-    }
+  "type": "intel-pt",
+  "cpuInfo": {
+    "vendor": "GenuineIntel",
+    "family": 6,
+    "model": 79,
+    "stepping": 1
   },
   "processes": [
     {
@@ -15,14 +13,14 @@
       "threads": [
         {
           "tid": 5678,
-          "traceFile": "3842849.trace"
+          "traceBuffer": "3842849.trace"
         }
       ],
       "modules": [
         {
           "file": "a.out",
           "systemPath": "a.out",
-          "loadAddress": "0x0000000000400000",
+          "loadAddress": 4194304,
           "uuid": "6AA9A4E2-6F28-2F33-377D-59FECE874C71-5B41261A"
         }
       ]
@@ -32,7 +30,7 @@
       "threads": [
         {
           "tid": 56789,
-          "traceFile": "3842849.trace"
+          "traceBuffer": "3842849.trace"
         }
       ],
       "modules": []

diff  --git a/lldb/test/API/commands/trace/intelpt-trace/trace_bad3.json b/lldb/test/API/commands/trace/intelpt-trace/trace_bad3.json
index 63eafbc13efbf..ae33077c8ff09 100644
--- a/lldb/test/API/commands/trace/intelpt-trace/trace_bad3.json
+++ b/lldb/test/API/commands/trace/intelpt-trace/trace_bad3.json
@@ -1,12 +1,10 @@
 {
-  "trace": {
-    "type": "intel-pt",
-    "cpuInfo": {
-      "vendor": "intel",
-      "family": 6,
-      "model": 79,
-      "stepping": 1
-    }
+  "type": "intel-pt",
+  "cpuInfo": {
+    "vendor": "GenuineIntel",
+    "family": 6,
+    "model": 79,
+    "stepping": 1
   },
   "processes": [
     {
@@ -15,14 +13,14 @@
       "threads": [
         {
           "tid": 5678,
-          "traceFile": "3842849.trace"
+          "traceBuffer": "3842849.trace"
         }
       ],
       "modules": [
         {
           "file": "a.out",
           "systemPath": "a.out",
-          "loadAddress": "0x0000000000400000",
+          "loadAddress": 4194304,
           "uuid": "6AA9A4E2-6F28-2F33-377D-59FECE874C71-5B41261A"
         }
       ]

diff  --git a/lldb/test/API/commands/trace/intelpt-trace/trace_bad4.json b/lldb/test/API/commands/trace/intelpt-trace/trace_bad4.json
index 7224ee5f1f3c2..89245edb9aad8 100644
--- a/lldb/test/API/commands/trace/intelpt-trace/trace_bad4.json
+++ b/lldb/test/API/commands/trace/intelpt-trace/trace_bad4.json
@@ -1,11 +1,9 @@
 {
-  "trace": {
-    "type": "intel-pt",
-    "cpuInfo": {
-      "vendor": "intel",
-      "model": 79,
-      "stepping": 1
-    }
+  "type": "intel-pt",
+  "cpuInfo": {
+    "vendor": "GenuineIntel",
+    "model": 79,
+    "stepping": 1
   },
   "processes": [
   ]

diff  --git a/lldb/test/API/commands/trace/intelpt-trace/trace_bad5.json b/lldb/test/API/commands/trace/intelpt-trace/trace_bad5.json
index 1664072aa3b8d..d70b176af878b 100644
--- a/lldb/test/API/commands/trace/intelpt-trace/trace_bad5.json
+++ b/lldb/test/API/commands/trace/intelpt-trace/trace_bad5.json
@@ -1,12 +1,10 @@
 {
-  "trace": {
-    "type": "intel-pt",
-    "cpuInfo": {
-      "vendor": "intel",
-      "family": 6,
-      "model": 79,
-      "stepping": 1
-    }
+  "type": "intel-pt",
+  "cpuInfo": {
+    "vendor": "GenuineIntel",
+    "family": 6,
+    "model": 79,
+    "stepping": 1
   },
   "processes": [
     {
@@ -15,7 +13,7 @@
       "threads": [
         {
           "tid": 5678,
-          "traceFile": "3842849.trace"
+          "traceBuffer": "3842849.trace"
         }
       ],
       "modules": [

diff  --git a/lldb/test/API/commands/trace/intelpt-trace/trace_bad_image.json b/lldb/test/API/commands/trace/intelpt-trace/trace_bad_image.json
index 901f41901842c..84e4bf783f021 100644
--- a/lldb/test/API/commands/trace/intelpt-trace/trace_bad_image.json
+++ b/lldb/test/API/commands/trace/intelpt-trace/trace_bad_image.json
@@ -1,12 +1,10 @@
 {
-  "trace": {
-    "type": "intel-pt",
-    "cpuInfo": {
-      "vendor": "intel",
-      "family": 6,
-      "model": 79,
-      "stepping": 1
-    }
+  "type": "intel-pt",
+  "cpuInfo": {
+    "vendor": "GenuineIntel",
+    "family": 6,
+    "model": 79,
+    "stepping": 1
   },
   "processes": [
     {
@@ -15,14 +13,14 @@
       "threads": [
         {
           "tid": 3842849,
-          "traceFile": "3842849.trace"
+          "traceBuffer": "3842849.trace"
         }
       ],
       "modules": [
         {
           "file": "a.out",
           "systemPath": "a.out",
-          "loadAddress": "0x0000000000FFFFF0",
+          "loadAddress": 16777200,
           "uuid": "6AA9A4E2-6F28-2F33-377D-59FECE874C71-5B41261A"
         }
       ]

diff  --git a/lldb/test/API/commands/trace/intelpt-trace/trace_wrong_cpu.json b/lldb/test/API/commands/trace/intelpt-trace/trace_wrong_cpu.json
index 318aaba8154ff..e55af9f1cacc4 100644
--- a/lldb/test/API/commands/trace/intelpt-trace/trace_wrong_cpu.json
+++ b/lldb/test/API/commands/trace/intelpt-trace/trace_wrong_cpu.json
@@ -1,12 +1,10 @@
 {
-  "trace": {
-    "type": "intel-pt",
-    "cpuInfo": {
-      "vendor": "intel",
-      "family": 2123123,
-      "model": 12123123,
-      "stepping": 1231231
-    }
+  "type": "intel-pt",
+  "cpuInfo": {
+    "vendor": "GenuineIntel",
+    "family": 2123123,
+    "model": 12123123,
+    "stepping": 1231231
   },
   "processes": [
     {
@@ -15,14 +13,14 @@
       "threads": [
         {
           "tid": 3842849,
-          "traceFile": "3842849.trace"
+          "traceBuffer": "3842849.trace"
         }
       ],
       "modules": [
         {
           "file": "a.out",
           "systemPath": "a.out",
-          "loadAddress": "0x0000000000400000",
+          "loadAddress": 4194304,
           "uuid": "6AA9A4E2-6F28-2F33-377D-59FECE874C71-5B41261A"
         }
       ]

diff  --git a/lldb/unittests/Process/Linux/PerfTests.cpp b/lldb/unittests/Process/Linux/PerfTests.cpp
index 39bbc43c1da53..c12aa097dd3ca 100644
--- a/lldb/unittests/Process/Linux/PerfTests.cpp
+++ b/lldb/unittests/Process/Linux/PerfTests.cpp
@@ -6,8 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifdef __x86_64__
-
 #include "Perf.h"
 
 #include "llvm/Support/Error.h"
@@ -85,136 +83,3 @@ TEST(Perf, TscConversion) {
   ASSERT_LT(converted_tsc_
diff .count(),
             (SLEEP_NANOS + acceptable_overhead).count());
 }
-
-size_t ReadCylicBufferWrapper(void *buf, size_t buf_size, void *cyc_buf,
-                              size_t cyc_buf_size, size_t cyc_start,
-                              size_t offset) {
-  llvm::MutableArrayRef<uint8_t> dst(reinterpret_cast<uint8_t *>(buf),
-                                     buf_size);
-  llvm::ArrayRef<uint8_t> src(reinterpret_cast<uint8_t *>(cyc_buf),
-                              cyc_buf_size);
-  ReadCyclicBuffer(dst, src, cyc_start, offset);
-  return dst.size();
-}
-
-TEST(CyclicBuffer, EdgeCases) {
-  size_t bytes_read;
-  uint8_t cyclic_buffer[6] = {'l', 'i', 'c', 'c', 'y', 'c'};
-
-  // We will always leave the last bytes untouched
-  // so that string comparisons work.
-  char smaller_buffer[4] = {};
-
-  // empty buffer to read into
-  bytes_read = ReadCylicBufferWrapper(smaller_buffer, 0, cyclic_buffer,
-                                      sizeof(cyclic_buffer), 3, 0);
-  ASSERT_EQ(0u, bytes_read);
-
-  // empty cyclic buffer
-  bytes_read = ReadCylicBufferWrapper(smaller_buffer, sizeof(smaller_buffer),
-                                      cyclic_buffer, 0, 3, 0);
-  ASSERT_EQ(0u, bytes_read);
-
-  // bigger offset
-  bytes_read =
-      ReadCylicBufferWrapper(smaller_buffer, sizeof(smaller_buffer),
-                             cyclic_buffer, sizeof(cyclic_buffer), 3, 6);
-  ASSERT_EQ(0u, bytes_read);
-
-  // wrong offset
-  bytes_read =
-      ReadCylicBufferWrapper(smaller_buffer, sizeof(smaller_buffer),
-                             cyclic_buffer, sizeof(cyclic_buffer), 3, 7);
-  ASSERT_EQ(0u, bytes_read);
-
-  // wrong start
-  bytes_read =
-      ReadCylicBufferWrapper(smaller_buffer, sizeof(smaller_buffer),
-                             cyclic_buffer, sizeof(cyclic_buffer), 3, 7);
-  ASSERT_EQ(0u, bytes_read);
-}
-
-TEST(CyclicBuffer, EqualSizeBuffer) {
-  size_t bytes_read = 0;
-  uint8_t cyclic_buffer[6] = {'l', 'i', 'c', 'c', 'y', 'c'};
-
-  char cyclic[] = "cyclic";
-  for (size_t i = 0; i < sizeof(cyclic); i++) {
-    // We will always leave the last bytes untouched
-    // so that string comparisons work.
-    char equal_size_buffer[7] = {};
-    bytes_read =
-        ReadCylicBufferWrapper(equal_size_buffer, sizeof(cyclic_buffer),
-                               cyclic_buffer, sizeof(cyclic_buffer), 3, i);
-    ASSERT_EQ((sizeof(cyclic) - i - 1), bytes_read);
-    ASSERT_STREQ(equal_size_buffer, (cyclic + i));
-  }
-}
-
-TEST(CyclicBuffer, SmallerSizeBuffer) {
-  size_t bytes_read;
-  uint8_t cyclic_buffer[6] = {'l', 'i', 'c', 'c', 'y', 'c'};
-
-  // We will always leave the last bytes untouched
-  // so that string comparisons work.
-  char smaller_buffer[4] = {};
-  bytes_read =
-      ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1),
-                             cyclic_buffer, sizeof(cyclic_buffer), 3, 0);
-  ASSERT_EQ(3u, bytes_read);
-  ASSERT_STREQ(smaller_buffer, "cyc");
-
-  bytes_read =
-      ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1),
-                             cyclic_buffer, sizeof(cyclic_buffer), 3, 1);
-  ASSERT_EQ(3u, bytes_read);
-  ASSERT_STREQ(smaller_buffer, "ycl");
-
-  bytes_read =
-      ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1),
-                             cyclic_buffer, sizeof(cyclic_buffer), 3, 2);
-  ASSERT_EQ(3u, bytes_read);
-  ASSERT_STREQ(smaller_buffer, "cli");
-
-  bytes_read =
-      ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1),
-                             cyclic_buffer, sizeof(cyclic_buffer), 3, 3);
-  ASSERT_EQ(3u, bytes_read);
-  ASSERT_STREQ(smaller_buffer, "lic");
-
-  {
-    char smaller_buffer[4] = {};
-    bytes_read =
-        ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1),
-                               cyclic_buffer, sizeof(cyclic_buffer), 3, 4);
-    ASSERT_EQ(2u, bytes_read);
-    ASSERT_STREQ(smaller_buffer, "ic");
-  }
-  {
-    char smaller_buffer[4] = {};
-    bytes_read =
-        ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1),
-                               cyclic_buffer, sizeof(cyclic_buffer), 3, 5);
-    ASSERT_EQ(1u, bytes_read);
-    ASSERT_STREQ(smaller_buffer, "c");
-  }
-}
-
-TEST(CyclicBuffer, BiggerSizeBuffer) {
-  size_t bytes_read = 0;
-  uint8_t cyclic_buffer[6] = {'l', 'i', 'c', 'c', 'y', 'c'};
-
-  char cyclic[] = "cyclic";
-  for (size_t i = 0; i < sizeof(cyclic); i++) {
-    // We will always leave the last bytes untouched
-    // so that string comparisons work.
-    char bigger_buffer[10] = {};
-    bytes_read =
-        ReadCylicBufferWrapper(bigger_buffer, (sizeof(bigger_buffer) - 1),
-                               cyclic_buffer, sizeof(cyclic_buffer), 3, i);
-    ASSERT_EQ((sizeof(cyclic) - i - 1), bytes_read);
-    ASSERT_STREQ(bigger_buffer, (cyclic + i));
-  }
-}
-
-#endif // __x86_64__

diff  --git a/lldb/unittests/Utility/TraceGDBRemotePacketsTest.cpp b/lldb/unittests/Utility/TraceGDBRemotePacketsTest.cpp
index 94b6ec14ef840..b3f8538ee8b68 100644
--- a/lldb/unittests/Utility/TraceGDBRemotePacketsTest.cpp
+++ b/lldb/unittests/Utility/TraceGDBRemotePacketsTest.cpp
@@ -38,8 +38,7 @@ TEST(TraceGDBRemotePacketsTest, IntelPTGetStateResponse) {
 
   // Create TraceIntelPTGetStateResponse.
   TraceIntelPTGetStateResponse response;
-  response.tsc_conversion = std::make_unique<LinuxPerfZeroTscConversion>(
-      test_time_mult, test_time_shift, test_time_zero);
+  response.tsc_perf_zero_conversion = LinuxPerfZeroTscConversion{test_time_mult, test_time_shift, test_time_zero};
 
   // Serialize then deserialize.
   Expected<TraceIntelPTGetStateResponse> deserialized_response =
@@ -57,9 +56,9 @@ TEST(TraceGDBRemotePacketsTest, IntelPTGetStateResponse) {
   const uint64_t EXPECTED_NANOS = 9223372039007304983u;
 
   uint64_t pre_serialization_conversion =
-      response.tsc_conversion->Convert(TSC).count();
+      response.tsc_perf_zero_conversion->ToNanos(TSC).count();
   uint64_t post_serialization_conversion =
-      deserialized_response->tsc_conversion->Convert(TSC).count();
+      deserialized_response->tsc_perf_zero_conversion->ToNanos(TSC).count();
 
   // Check equality:
   // Ensure that both the TraceGetStateResponse and TraceIntelPTGetStateResponse
@@ -95,7 +94,7 @@ TEST(TraceGDBRemotePacketsTest, IntelPTGetStateResponseEmpty) {
   // portions of the JSON representation are unchanged.
   ASSERT_EQ(toJSON(response), toJSON(*deserialized_response));
   // Ensure that the tsc_conversion's are nullptr.
-  ASSERT_EQ(response.tsc_conversion.get(), nullptr);
-  ASSERT_EQ(response.tsc_conversion.get(),
-            deserialized_response->tsc_conversion.get());
+  ASSERT_EQ(response.tsc_perf_zero_conversion, None);
+  ASSERT_EQ(response.tsc_perf_zero_conversion,
+            deserialized_response->tsc_perf_zero_conversion);
 }


        


More information about the lldb-commits mailing list