wallace created this revision.
wallace added a reviewer: clayborg.
Herald added subscribers: lldb-commits, dang.
Herald added a project: LLDB.
wallace requested review of this revision.
Herald added a subscriber: JDevlieghere.

This implements the interactive trace start and stop methods.

There's a lot of boilerplate code due to the gdb-remote protocol, but the main 
changes are:

- Implementation of the jLLDBTraceStop packet, which is quite simple.
- Implementation of the jLLDBTraceStart packet, which accepts plug-in specific 
params.
- Implementation of the jLLDBTraceQueryData, used for querying in json format 
various data, in a generic way so that it can be resused by trace plug-ins. I'm 
using it for querying the trace buffers and cpu config needed to decode.
- Created a set of well-defined structures that represent all packets and 
params send across the gdb-remote protocol, so that serialization and 
deserialization with JSON is as strongly-typed as possible.
- Created an IntelPTDecoder for live threads, that use the debugger's stop id 
as checkpoint for its internal cache.
- Added tests

Depends on D90729 <https://reviews.llvm.org/D90729>.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D91679

Files:
  lldb/docs/lldb-gdb-remote.txt
  lldb/include/lldb/Core/PluginManager.h
  lldb/include/lldb/Host/common/NativeProcessProtocol.h
  lldb/include/lldb/Target/Process.h
  lldb/include/lldb/Target/Trace.h
  lldb/include/lldb/Utility/StringExtractorGDBRemote.h
  lldb/include/lldb/Utility/TraceOptions.h
  lldb/include/lldb/lldb-private-interfaces.h
  lldb/source/Commands/CommandObjectThreadUtil.cpp
  lldb/source/Commands/CommandObjectThreadUtil.h
  lldb/source/Core/PluginManager.cpp
  lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
  lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
  lldb/source/Plugins/Process/Linux/ProcessorTrace.cpp
  lldb/source/Plugins/Process/Linux/ProcessorTrace.h
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
  lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
  lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
  lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.cpp
  lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.h
  lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
  lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h
  lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
  lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
  lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td
  lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
  lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
  lldb/source/Target/Trace.cpp
  lldb/source/Utility/StringExtractorGDBRemote.cpp
  lldb/source/Utility/TraceOptions.cpp
  lldb/test/API/commands/trace/TestTraceStartStop.py

Index: lldb/test/API/commands/trace/TestTraceStartStop.py
===================================================================
--- lldb/test/API/commands/trace/TestTraceStartStop.py
+++ lldb/test/API/commands/trace/TestTraceStartStop.py
@@ -3,6 +3,8 @@
 from lldbsuite.test import lldbutil
 from lldbsuite.test.decorators import *
 
+ADDRESS_REGEX = '0x[0-9a-fA-F]*'
+
 class TestTraceLoad(TestBase):
 
     mydir = TestBase.compute_mydir(__file__)
@@ -49,20 +51,57 @@
 
         self.expect("r")
 
+        # This fails because "trace start" hasn't been called yet
+        self.expect("thread trace stop", error=True,
+            substrs=["error: Process is not being traced"])
+
+
         # the help command should be the intel-pt one now
         self.expect("help thread trace start",
             substrs=["Start tracing one or more threads with intel-pt.",
                      "Syntax: thread trace start [<thread-index> <thread-index> ...] [<intel-pt-options>]"])
 
-        self.expect("thread trace start",
-            patterns=["would trace tid .* with size_in_kb 4 and custom_config 0"])
-
-        self.expect("thread trace start --size 20 --custom-config 1",
-            patterns=["would trace tid .* with size_in_kb 20 and custom_config 1"])
-
-        # This fails because "trace stop" is not yet implemented.
+        # We start tracing with a small buffer size
+        self.expect("thread trace start --size 1")
+        
+        # We fail if we try to trace again
+        self.expect("thread trace start", error=True, 
+            substrs=["error: tracing already active on this thread"])
+
+        # We can reconstruct the single instruction executed in the first line
+        self.expect("n")
+        self.expect("thread trace dump instructions", 
+            patterns=[f'''thread #1: tid = .*, total instructions = 1
+  a.out`main \+ 4 at main.cpp:2
+    \[0\] {ADDRESS_REGEX}    movl'''])
+
+        # We can reconstruct the instructions up to the second line
+        self.expect("n")
+        self.expect("thread trace dump instructions", 
+            patterns=[f'''thread #1: tid = .*, total instructions = 5
+  a.out`main \+ 4 at main.cpp:2
+    \[0\] {ADDRESS_REGEX}    movl .*
+  a.out`main \+ 11 at main.cpp:4
+    \[1\] {ADDRESS_REGEX}    movl .*
+    \[2\] {ADDRESS_REGEX}    jmp  .* ; <\+28> at main.cpp:4
+  a.out`main \+ 28 at main.cpp:4
+    \[3\] {ADDRESS_REGEX}    cmpl .*
+    \[4\] {ADDRESS_REGEX}    jle  .* ; <\+20> at main.cpp:5'''])
+
+        # We stop tracing
+        self.expect("thread trace stop")
+
+        # We can't stop twice
         self.expect("thread trace stop", error=True,
-            substrs=["error: Process is not being traced"])
+            substrs=["error: Process is not being traced."])
+
+        # We trace again from scratch
+        self.expect("thread trace start")
+        self.expect("n")
+        self.expect("thread trace dump instructions", 
+            patterns=[f'''thread #1: tid = .*, total instructions = 1
+  a.out`main \+ 20 at main.cpp:5
+    \[0\] {ADDRESS_REGEX}    xorl'''])
 
         self.expect("c")
         # Now the process has finished, so the commands should fail
Index: lldb/source/Utility/TraceOptions.cpp
===================================================================
--- lldb/source/Utility/TraceOptions.cpp
+++ lldb/source/Utility/TraceOptions.cpp
@@ -8,11 +8,45 @@
 
 #include "lldb/Utility/TraceOptions.h"
 
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/StringExtractor.h"
+
 using namespace lldb_private;
 
 namespace llvm {
 namespace json {
 
+bool fromJSON(const Value &value, TraceStopPacket &packet, Path path) {
+  ObjectMapper o(value, path);
+  return o && o.map("type", packet.type) && o.map("tid", packet.tid);
+}
+
+Value toJSON(const TraceStopPacket &packet) {
+  return Object{{"type", packet.type}, {"tid", packet.tid}};
+}
+
+Value toJSON(const TraceQueryDataSimplePacket &packet) {
+  return Object{{"query", packet.query}, {"type", packet.type}};
+}
+
+bool fromJSON(const Value &value, TraceIntelPTStartPacketParams &packet,
+              Path path) {
+  ObjectMapper o(value, path);
+  return o && o.map("bufferSizeInKB", packet.buffer_size_in_kb) &&
+         o.map("perfConfig", packet.perf_config);
+}
+
+bool fromJSON(const Value &value, TraceStartSimplePacket &packet, Path path) {
+  ObjectMapper o(value, path);
+  return o && o.map("type", packet.type) && o.map("tid", packet.tid);
+}
+
+bool fromJSON(const Value &value, TraceQueryDataSimplePacket &packet,
+              Path path) {
+  ObjectMapper o(value, path);
+  return o && o.map("type", packet.type) && o.map("query", packet.query);
+}
+
 bool fromJSON(const Value &value, TraceTypeInfo &info, Path path) {
   ObjectMapper o(value, path);
   if (!o)
@@ -21,5 +55,55 @@
   return o.map("name", info.name);
 }
 
+bool fromJSON(const Value &value, TraceIntelPTCPUConfig &pt_cpu, Path path) {
+  ObjectMapper o(value, path);
+  return o && o.map("vendor", pt_cpu.vendor) &&
+         o.map("family", pt_cpu.family) && o.map("model", pt_cpu.model) &&
+         o.map("stepping", pt_cpu.stepping);
+}
+
+bool fromJSON(const Value &value, TraceIntelPTBuffer &response, Path path) {
+  ObjectMapper o(value, path);
+  std::string string_data;
+  if (!o || !o.map("data", string_data))
+    return false;
+
+  StringExtractor extractor(string_data);
+  response.data.resize(string_data.size() / 2);
+  MutableArrayRef<uint8_t> data_ref(response.data);
+  extractor.GetHexBytesAvail(data_ref);
+  return true;
+}
+
+bool fromJSON(const Value &value, TraceIntelPTQueryBufferParams &params,
+              Path path) {
+  ObjectMapper o(value, path);
+  return o && o.map("tid", params.tid);
+}
+
+Value toJSON(const TraceIntelPTStartPacketParams &packet) {
+  return Object{{"bufferSizeInKB", packet.buffer_size_in_kb},
+                {"perfConfig", packet.perf_config}};
+}
+
+Value toJSON(const TraceIntelPTCPUConfig &cpu_config) {
+  return Object{{"family", cpu_config.family},
+                {"model", cpu_config.model},
+                {"stepping", cpu_config.stepping},
+                {"vendor", cpu_config.vendor}};
+}
+
+Value toJSON(const TraceIntelPTBuffer &buffer) {
+  StreamString stream;
+  for (const uint8_t &datum : buffer.data)
+    stream.PutHex8(datum);
+
+  return json::Object{{"data", stream.GetString().str()}};
+}
+
+Value toJSON(const TraceIntelPTQueryBufferParams &params) {
+  return json::Object{{"tid", params.tid}};
+}
+
 } // namespace json
 } // namespace llvm
Index: lldb/source/Utility/StringExtractorGDBRemote.cpp
===================================================================
--- lldb/source/Utility/StringExtractorGDBRemote.cpp
+++ lldb/source/Utility/StringExtractorGDBRemote.cpp
@@ -310,8 +310,15 @@
       return eServerPacketType_jTraceStart;
     if (PACKET_STARTS_WITH("jTraceStop:"))
       return eServerPacketType_jTraceStop;
+
     if (PACKET_MATCHES("jLLDBTraceSupportedType"))
       return eServerPacketType_jLLDBTraceSupportedType;
+    if (PACKET_STARTS_WITH("jLLDBTraceStop:"))
+      return eServerPacketType_jLLDBTraceStop;
+    if (PACKET_STARTS_WITH("jLLDBTraceStart:"))
+      return eServerPacketType_jLLDBTraceStart;
+    if (PACKET_STARTS_WITH("jLLDBTraceQueryData:"))
+      return eServerPacketType_jLLDBTraceQueryData;
     break;
 
   case 'v':
Index: lldb/source/Target/Trace.cpp
===================================================================
--- lldb/source/Target/Trace.cpp
+++ lldb/source/Target/Trace.cpp
@@ -72,6 +72,19 @@
   return createInvalidPlugInError(json_session.trace.type);
 }
 
+Expected<lldb::TraceSP> Trace::FindPlugin(llvm::StringRef plugin_name,
+                                          Process &process) {
+  if (!process.IsLiveDebugSession())
+    return createStringError(inconvertibleErrorCode(),
+                             "Can't trace non-live processes");
+
+  ConstString name(plugin_name);
+  if (auto create_callback =
+          PluginManager::GetTraceCreateCallbackForLiveProcess(name))
+    return create_callback(process);
+  return createInvalidPlugInError(plugin_name);
+}
+
 Expected<StringRef> Trace::FindPluginSchema(StringRef name) {
   ConstString plugin_name(name);
   StringRef schema = PluginManager::GetTraceSchema(plugin_name);
@@ -266,3 +279,13 @@
         return index < end_position;
       });
 }
+
+Expected<std::string> Trace::DoQueryData(Process &process,
+                                         const json::Value &packet) {
+  return process.TraceQueryData(packet);
+}
+
+Error Trace::DoStartTracingThread(Thread &thread,
+                                  const llvm::json::Value &packet) {
+  return thread.GetProcess()->StartTracingThread(packet);
+}
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
@@ -19,16 +19,9 @@
 
 class TraceIntelPTSessionFileParser : public TraceSessionFileParser {
 public:
-  struct JSONPTCPU {
-    std::string vendor;
-    int64_t family;
-    int64_t model;
-    int64_t stepping;
-  };
-
   struct JSONTraceIntelPTSettings
       : TraceSessionFileParser::JSONTracePluginSettings {
-    JSONPTCPU pt_cpu;
+    TraceIntelPTCPUConfig pt_cpu;
   };
 
   /// See \a TraceSessionFileParser::TraceSessionFileParser for the description
@@ -55,9 +48,9 @@
   CreateTraceIntelPTInstance(const pt_cpu &pt_cpu,
                              std::vector<ParsedProcess> &parsed_processes);
 
-private:
-  pt_cpu ParsePTCPU(const JSONPTCPU &pt_cpu);
+  static pt_cpu ParsePTCPU(const TraceIntelPTCPUConfig &pt_cpu);
 
+private:
   const llvm::json::Value &m_trace_session_file;
 };
 
@@ -67,12 +60,6 @@
 namespace llvm {
 namespace json {
 
-bool fromJSON(
-    const Value &value,
-    lldb_private::trace_intel_pt::TraceIntelPTSessionFileParser::JSONPTCPU
-        &pt_cpu,
-    Path path);
-
 bool fromJSON(const Value &value,
               lldb_private::trace_intel_pt::TraceIntelPTSessionFileParser::
                   JSONTraceIntelPTSettings &plugin_settings,
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
@@ -13,6 +13,7 @@
 #include "lldb/Target/Target.h"
 #include "lldb/Target/ThreadList.h"
 #include "lldb/Target/ThreadTrace.h"
+#include "lldb/Utility/TraceOptions.h"
 
 using namespace lldb;
 using namespace lldb_private;
@@ -35,7 +36,8 @@
   return schema;
 }
 
-pt_cpu TraceIntelPTSessionFileParser::ParsePTCPU(const JSONPTCPU &pt_cpu) {
+pt_cpu
+TraceIntelPTSessionFileParser::ParsePTCPU(const TraceIntelPTCPUConfig &pt_cpu) {
   return {pt_cpu.vendor.compare("intel") == 0 ? pcv_intel : pcv_unknown,
           static_cast<uint16_t>(pt_cpu.family),
           static_cast<uint8_t>(pt_cpu.model),
@@ -73,14 +75,6 @@
 namespace llvm {
 namespace json {
 
-bool fromJSON(const Value &value,
-              TraceIntelPTSessionFileParser::JSONPTCPU &pt_cpu, Path path) {
-  ObjectMapper o(value, path);
-  return o && o.map("vendor", pt_cpu.vendor) &&
-         o.map("family", pt_cpu.family) && o.map("model", pt_cpu.model) &&
-         o.map("stepping", pt_cpu.stepping);
-}
-
 bool fromJSON(
     const Value &value,
     TraceIntelPTSessionFileParser::JSONTraceIntelPTSettings &plugin_settings,
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td
@@ -4,13 +4,14 @@
   def thread_trace_start_intel_pt_size: Option<"size", "s">,
     Group<1>,
     Arg<"Value">,
-    Desc<"The size of the trace in KB. The kernel rounds it down to the nearest"
-         " multiple of 4. Defaults to 4.">;
-  def thread_trace_start_intel_pt_custom_config: Option<"custom-config", "c">,
+    Desc<"The size of the trace in KB. It is rounded up to the nearest pwoer of"
+         " 2. Defaults to 4.">;
+  def thread_trace_start_intel_pt_custom_config: Option<"perf-config", "c">,
     Group<1>,
     Arg<"Value">,
-    Desc<"Low level bitmask configuration for the kernel based on the values "
-         "in `grep -H  /sys/bus/event_source/devices/intel_pt/format/*`. "
+    Desc<"Custom low level perf event bitmask configuration for the kernel "
+         "based on the values in "
+         "`grep -H  /sys/bus/event_source/devices/intel_pt/format/*`. "
          "See https://github.com/torvalds/linux/blob/master/tools/perf/Documentation/perf-intel-pt.txt";
          " for more information. Defaults to 0.">;
 }
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
@@ -45,8 +45,12 @@
   /// \return
   ///     A trace instance or an error in case of failures.
   static llvm::Expected<lldb::TraceSP>
-  CreateInstance(const llvm::json::Value &trace_session_file,
-                 llvm::StringRef session_file_dir, Debugger &debugger);
+  CreateInstanceForSessionFile(const llvm::json::Value &trace_session_file,
+                               llvm::StringRef session_file_dir,
+                               Debugger &debugger);
+
+  static llvm::Expected<lldb::TraceSP>
+  CreateInstanceForLiveProcess(Process &process);
 
   static ConstString GetPluginNameStatic();
 
@@ -64,6 +68,15 @@
 
   size_t GetCursorPosition(const Thread &thread) override;
 
+  llvm::Error StopTracingThread(const Thread &thread) override;
+
+  llvm::Error StartTracingThread(Thread &thread,
+                                 const TraceIntelPTStartPacketParams &params);
+
+  static llvm::Expected<TraceIntelPTBuffer> QueryTraceBuffer(Thread &thread);
+
+  static llvm::Expected<pt_cpu> QueryCPUConfig(Process &process);
+
 private:
   friend class TraceIntelPTSessionFileParser;
 
@@ -73,6 +86,8 @@
   TraceIntelPT(const pt_cpu &pt_cpu,
                const std::vector<std::shared_ptr<ThreadTrace>> &traced_threads);
 
+  TraceIntelPT(const pt_cpu &pt_cpu) : m_pt_cpu(pt_cpu), m_thread_decoders(){};
+
   /// Decode the trace of the given thread that, i.e. recontruct the traced
   /// instructions. That trace must be managed by this class.
   ///
@@ -86,8 +101,7 @@
   const DecodedThread *Decode(const Thread &thread);
 
   pt_cpu m_pt_cpu;
-  std::map<std::pair<lldb::pid_t, lldb::tid_t>, ThreadTraceDecoder>
-      m_trace_threads;
+  std::map<const Thread *, std::unique_ptr<ThreadDecoder>> m_thread_decoders;
 };
 
 } // namespace trace_intel_pt
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
@@ -28,12 +28,13 @@
 
 void TraceIntelPT::Initialize() {
   PluginManager::RegisterPlugin(
-      GetPluginNameStatic(), "Intel Processor Trace", CreateInstance,
+      GetPluginNameStatic(), "Intel Processor Trace",
+      CreateInstanceForSessionFile, CreateInstanceForLiveProcess,
       TraceIntelPTSessionFileParser::GetSchema(), GetStartCommand);
 }
 
 void TraceIntelPT::Terminate() {
-  PluginManager::UnregisterPlugin(CreateInstance);
+  PluginManager::UnregisterPlugin(CreateInstanceForSessionFile);
 }
 
 ConstString TraceIntelPT::GetPluginNameStatic() {
@@ -55,31 +56,78 @@
 
 void TraceIntelPT::Dump(Stream *s) const {}
 
-Expected<TraceSP>
-TraceIntelPT::CreateInstance(const json::Value &trace_session_file,
-                             StringRef session_file_dir, Debugger &debugger) {
+Expected<TraceSP> TraceIntelPT::CreateInstanceForSessionFile(
+    const json::Value &trace_session_file, StringRef session_file_dir,
+    Debugger &debugger) {
   return TraceIntelPTSessionFileParser(debugger, trace_session_file,
                                        session_file_dir)
       .Parse();
 }
 
+Error TraceIntelPT::StopTracingThread(const Thread &thread) {
+  auto it = m_thread_decoders.find(&thread);
+  if (it == m_thread_decoders.end())
+    return createStringError(inconvertibleErrorCode(),
+                             "thread is not being traced");
+  if (Error err =
+          thread.GetProcess()->StopTracingThread(thread.GetID(), "intel-pt"))
+    return err;
+
+  m_thread_decoders.erase(it);
+
+  // We clear the trace instance for the target we are not tracing any threads.
+  if (m_thread_decoders.empty())
+    thread.GetProcess()->GetTarget().SetTrace(nullptr);
+  return Error::success();
+}
+
+Error TraceIntelPT::StartTracingThread(
+    Thread &thread, const TraceIntelPTStartPacketParams &params) {
+  if (Error err = Trace::StartTracingThread(thread, params))
+    return err;
+  m_thread_decoders.emplace(
+      &thread, std::make_unique<LiveThreadDecoder>(thread, m_pt_cpu));
+  return Error::success();
+}
+
+Expected<TraceIntelPTBuffer> TraceIntelPT::QueryTraceBuffer(Thread &thread) {
+  TraceIntelPTQueryBufferParams params = {static_cast<int64_t>(thread.GetID())};
+  return QueryData<TraceIntelPTBuffer>(*thread.GetProcess(), "intel-pt",
+                                       "buffer", params);
+}
+
+Expected<pt_cpu> TraceIntelPT::QueryCPUConfig(Process &process) {
+  if (Expected<TraceIntelPTCPUConfig> cpu_config =
+          QueryData<TraceIntelPTCPUConfig>(process, "intel-pt", "pt_cpu"))
+    return TraceIntelPTSessionFileParser::ParsePTCPU(*cpu_config);
+  else
+    return cpu_config.takeError();
+}
+
+Expected<TraceSP> TraceIntelPT::CreateInstanceForLiveProcess(Process &process) {
+  Expected<pt_cpu> pt_cpu = QueryCPUConfig(process);
+  if (!pt_cpu)
+    return pt_cpu.takeError();
+
+  TraceSP instance(new TraceIntelPT(*pt_cpu));
+  process.GetTarget().SetTrace(instance);
+  return instance;
+}
+
 TraceIntelPT::TraceIntelPT(
     const pt_cpu &pt_cpu,
     const std::vector<std::shared_ptr<ThreadTrace>> &traced_threads)
     : m_pt_cpu(pt_cpu) {
   for (const std::shared_ptr<ThreadTrace> &thread : traced_threads)
-    m_trace_threads.emplace(
-        std::piecewise_construct,
-        std::forward_as_tuple(thread->GetProcess()->GetID(), thread->GetID()),
-        std::forward_as_tuple(thread, pt_cpu));
+    m_thread_decoders.emplace(
+        thread.get(), std::make_unique<ThreadTraceDecoder>(thread, pt_cpu));
 }
 
 const DecodedThread *TraceIntelPT::Decode(const Thread &thread) {
-  auto it = m_trace_threads.find(
-      std::make_pair(thread.GetProcess()->GetID(), thread.GetID()));
-  if (it == m_trace_threads.end())
+  auto it = m_thread_decoders.find(&thread);
+  if (it == m_thread_decoders.end())
     return nullptr;
-  return &it->second.Decode();
+  return &it->second->Decode();
 }
 
 size_t TraceIntelPT::GetCursorPosition(const Thread &thread) {
Index: lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h
+++ lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h
@@ -18,24 +18,33 @@
 namespace lldb_private {
 namespace trace_intel_pt {
 
-/// \a lldb_private::ThreadTrace decoder that stores the output from decoding,
-/// avoiding recomputations, as decoding is expensive.
-class ThreadTraceDecoder {
+/// Base class that handles the decoding of a thread and its trace buffer/ file.
+class ThreadDecoder {
+public:
+  virtual ~ThreadDecoder() = default;
+
+  /// Decode the thread and store the result internally, to avoid
+  /// recomputations.
+  ///
+  /// \return
+  ///     A \a DecodedThread instance.
+  virtual const DecodedThread &Decode() = 0;
+};
+
+/// Decoder implementation for \a lldb_private::ThreadTrace, which are non-live
+/// processes that come trace session files.
+class ThreadTraceDecoder : public ThreadDecoder {
 public:
   /// \param[in] trace_thread
   ///     The thread whose trace file will be decoded.
   ///
   /// \param[in] pt_cpu
-  ///     The libipt cpu used when recording the trace.
+  ///     The libipt cpu used when decoding the trace.
   ThreadTraceDecoder(const std::shared_ptr<ThreadTrace> &trace_thread,
                      const pt_cpu &pt_cpu)
       : m_trace_thread(trace_thread), m_pt_cpu(pt_cpu), m_decoded_thread() {}
 
-  /// Decode the thread and store the result internally.
-  ///
-  /// \return
-  ///     A \a DecodedThread instance.
-  const DecodedThread &Decode();
+  const DecodedThread &Decode() override;
 
 private:
   ThreadTraceDecoder(const ThreadTraceDecoder &other) = delete;
@@ -46,6 +55,31 @@
   llvm::Optional<DecodedThread> m_decoded_thread;
 };
 
+class LiveThreadDecoder : public ThreadDecoder {
+public:
+  /// \param[in] thread
+  ///     The thread whose traces will be decoded.
+  ///
+  /// \param[in] pt_cpu
+  ///     The libipt cpu used when decoding the trace.
+  LiveThreadDecoder(Thread &thread, const pt_cpu &pt_cpu);
+
+  /// Decode the current trace for the thread.
+  ///
+  /// Internally the result is saved to avoid recomputations. If the debugger's
+  /// stop id has changed between calls, the cached result is discarded.
+  const DecodedThread &Decode() override;
+
+private:
+  LiveThreadDecoder(const LiveThreadDecoder &other) = delete;
+  LiveThreadDecoder &operator=(const LiveThreadDecoder &other) = delete;
+
+  lldb::ThreadSP m_thread_sp;
+  pt_cpu m_pt_cpu;
+  llvm::Optional<DecodedThread> m_decoded_thread;
+  uint32_t m_stop_id;
+};
+
 } // namespace trace_intel_pt
 } // namespace lldb_private
 
Index: lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
+++ lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
@@ -10,10 +10,12 @@
 
 #include "llvm/Support/MemoryBuffer.h"
 
+#include "TraceIntelPT.h"
 #include "lldb/Core/Module.h"
 #include "lldb/Core/Section.h"
 #include "lldb/Target/Target.h"
 #include "lldb/Target/ThreadTrace.h"
+#include "lldb/Utility/StringExtractor.h"
 
 using namespace lldb;
 using namespace lldb_private;
@@ -166,14 +168,7 @@
 
 static std::vector<IntelPTInstruction>
 CreateDecoderAndDecode(Process &process, const pt_cpu &pt_cpu,
-                       const FileSpec &trace_file) {
-  ErrorOr<std::unique_ptr<MemoryBuffer>> trace_or_error =
-      MemoryBuffer::getFile(trace_file.GetPath());
-  if (std::error_code err = trace_or_error.getError())
-    return makeInstructionListFromError(errorCodeToError(err));
-
-  MemoryBuffer &trace = **trace_or_error;
-
+                       MutableArrayRef<uint8_t> buffer) {
   pt_config config;
   pt_config_init(&config);
   config.cpu = pt_cpu;
@@ -181,12 +176,8 @@
   if (int errcode = pt_cpu_errata(&config.errata, &config.cpu))
     return makeInstructionListFromError(make_error<IntelPTError>(errcode));
 
-  // The libipt library does not modify the trace buffer, hence the following
-  // cast is safe.
-  config.begin =
-      reinterpret_cast<uint8_t *>(const_cast<char *>(trace.getBufferStart()));
-  config.end =
-      reinterpret_cast<uint8_t *>(const_cast<char *>(trace.getBufferEnd()));
+  config.begin = buffer.data();
+  config.end = buffer.data() + buffer.size();
 
   pt_insn_decoder *decoder = pt_insn_alloc_decoder(&config);
   if (!decoder)
@@ -204,6 +195,33 @@
   return instructions;
 }
 
+static std::vector<IntelPTInstruction>
+CreateDecoderAndDecode(Process &process, const pt_cpu &pt_cpu,
+                       const FileSpec &trace_file) {
+  ErrorOr<std::unique_ptr<MemoryBuffer>> trace_or_error =
+      MemoryBuffer::getFile(trace_file.GetPath());
+  if (std::error_code err = trace_or_error.getError())
+    return makeInstructionListFromError(errorCodeToError(err));
+
+  MemoryBuffer &trace = **trace_or_error;
+  MutableArrayRef<uint8_t> trace_data(
+      // The libipt library does not modify the trace buffer, hence the
+      // following cast is safe.
+      reinterpret_cast<uint8_t *>(const_cast<char *>(trace.getBufferStart())),
+      trace.getBufferSize());
+  return CreateDecoderAndDecode(process, pt_cpu, trace_data);
+}
+
+static std::vector<IntelPTInstruction>
+CreateDecoderAndDecode(Thread &thread, const pt_cpu &pt_cpu) {
+  if (Expected<TraceIntelPTBuffer> buffer =
+          TraceIntelPT::QueryTraceBuffer(thread))
+    return CreateDecoderAndDecode(*thread.GetProcess(), pt_cpu,
+                                  MutableArrayRef<uint8_t>(buffer->data));
+  else
+    return makeInstructionListFromError(buffer.takeError());
+}
+
 const DecodedThread &ThreadTraceDecoder::Decode() {
   if (!m_decoded_thread.hasValue()) {
     m_decoded_thread = DecodedThread(
@@ -213,3 +231,21 @@
 
   return *m_decoded_thread;
 }
+
+LiveThreadDecoder::LiveThreadDecoder(Thread &thread, const pt_cpu &pt_cpu)
+    : m_thread_sp(thread.shared_from_this()), m_pt_cpu(pt_cpu),
+      m_decoded_thread(), m_stop_id(thread.GetProcess()->GetStopID()) {}
+
+const DecodedThread &LiveThreadDecoder::Decode() {
+  uint32_t new_stop_id = m_thread_sp->GetProcess()->GetStopID();
+  if (new_stop_id != m_stop_id) {
+    m_stop_id = new_stop_id;
+    m_decoded_thread = None;
+  }
+
+  if (!m_decoded_thread.hasValue())
+    m_decoded_thread =
+        DecodedThread(CreateDecoderAndDecode(*m_thread_sp, m_pt_cpu));
+
+  return *m_decoded_thread;
+}
Index: lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.h
+++ lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.h
@@ -16,7 +16,7 @@
 namespace lldb_private {
 namespace trace_intel_pt {
 
-class CommandObjectTraceStartIntelPT : public CommandObjectIterateOverThreads {
+class CommandObjectTraceStartIntelPT : public CommandObjecThreadTraceStart {
 public:
   class CommandOptions : public Options {
   public:
@@ -31,13 +31,13 @@
 
     llvm::ArrayRef<OptionDefinition> GetDefinitions() override;
 
-    size_t m_size_in_kb;
-    uint32_t m_custom_config;
+    size_t m_buffer_size_in_kb;
+    uint32_t m_perf_config;
   };
 
   CommandObjectTraceStartIntelPT(CommandInterpreter &interpreter)
-      : CommandObjectIterateOverThreads(
-            interpreter, "thread trace start",
+      : CommandObjecThreadTraceStart(
+            "intel-pt", interpreter, "thread trace start",
             "Start tracing one or more threads with intel-pt. "
             "Defaults to the current thread. Thread indices can be "
             "specified as arguments.\n Use the thread-index \"all\" to trace "
Index: lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.cpp
+++ lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.cpp
@@ -8,7 +8,9 @@
 
 #include "CommandObjectTraceStartIntelPT.h"
 
+#include "TraceIntelPT.h"
 #include "lldb/Host/OptionParser.h"
+#include "lldb/Target/Process.h"
 #include "lldb/Target/Trace.h"
 
 using namespace lldb;
@@ -33,17 +35,17 @@
       error.SetErrorStringWithFormat("invalid integer value for option '%s'",
                                      option_arg.str().c_str());
     else
-      m_size_in_kb = size_in_kb;
+      m_buffer_size_in_kb = size_in_kb;
     break;
   }
   case 'c': {
-    int32_t custom_config;
-    if (option_arg.empty() || option_arg.getAsInteger(0, custom_config) ||
-        custom_config < 0)
+    int32_t perf_config;
+    if (option_arg.empty() || option_arg.getAsInteger(0, perf_config) ||
+        perf_config < 0)
       error.SetErrorStringWithFormat("invalid integer value for option '%s'",
                                      option_arg.str().c_str());
     else
-      m_custom_config = custom_config;
+      m_perf_config = perf_config;
     break;
   }
   default:
@@ -54,8 +56,8 @@
 
 void CommandObjectTraceStartIntelPT::CommandOptions::OptionParsingStarting(
     ExecutionContext *execution_context) {
-  m_size_in_kb = 4;
-  m_custom_config = 0;
+  m_buffer_size_in_kb = 4;
+  m_perf_config = 0;
 }
 
 llvm::ArrayRef<OptionDefinition>
@@ -65,9 +67,19 @@
 
 bool CommandObjectTraceStartIntelPT::HandleOneThread(
     lldb::tid_t tid, CommandReturnObject &result) {
-  result.AppendMessageWithFormat(
-      "would trace tid %" PRIu64 " with size_in_kb %zu and custom_config %d\n",
-      tid, m_options.m_size_in_kb, m_options.m_custom_config);
-  result.SetStatus(eReturnStatusSuccessFinishResult);
+
+  TraceIntelPTStartPacketParams options;
+  options.buffer_size_in_kb = m_options.m_buffer_size_in_kb;
+  options.perf_config = m_options.m_perf_config;
+
+  Thread &thread =
+      *m_exe_ctx.GetProcessRef().GetThreadList().FindThreadByID(tid);
+  TraceIntelPT *trace =
+      (TraceIntelPT *)m_exe_ctx.GetTargetRef().GetTrace().get();
+
+  if (Error err = trace->StartTracingThread(thread, options))
+    result.SetError(toString(std::move(err)));
+  else
+    result.SetStatus(eReturnStatusSuccessFinishResult);
   return result.Succeeded();
 }
Index: lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -177,6 +177,14 @@
 
   llvm::Expected<TraceTypeInfo> GetSupportedTraceType() override;
 
+  llvm::Error StopTracingThread(lldb::tid_t tid,
+                                llvm::StringRef trace_type) override;
+
+  llvm::Error StartTracingThread(const llvm::json::Value &options) override;
+
+  llvm::Expected<std::string>
+  TraceQueryData(const llvm::json::Value &query) override;
+
   Status GetTraceConfig(lldb::user_id_t uid, TraceOptions &options) override;
 
   Status GetWatchpointSupportInfo(uint32_t &num, bool &after) override;
Index: lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -1228,6 +1228,21 @@
   return m_gdb_comm.SendGetSupportedTraceType();
 }
 
+llvm::Error ProcessGDBRemote::StopTracingThread(lldb::tid_t tid,
+                                                llvm::StringRef trace_type) {
+  return m_gdb_comm.SendTraceStop(tid, trace_type);
+}
+
+llvm::Error
+ProcessGDBRemote::StartTracingThread(const llvm::json::Value &options) {
+  return m_gdb_comm.SendTraceStart(options);
+}
+
+llvm::Expected<std::string>
+ProcessGDBRemote::TraceQueryData(const llvm::json::Value &query) {
+  return m_gdb_comm.SendTraceQueryData(query);
+}
+
 void ProcessGDBRemote::DidExit() {
   // When we exit, disconnect from the GDB server communications
   m_gdb_comm.Disconnect();
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
@@ -166,6 +166,12 @@
 
   PacketResult Handle_jLLDBTraceSupportedType(StringExtractorGDBRemote &packet);
 
+  PacketResult Handle_jLLDBTraceStart(StringExtractorGDBRemote &packet);
+
+  PacketResult Handle_jLLDBTraceStop(StringExtractorGDBRemote &packet);
+
+  PacketResult Handle_jLLDBTraceQueryData(StringExtractorGDBRemote &packet);
+
   PacketResult Handle_QRestoreRegisterState(StringExtractorGDBRemote &packet);
 
   PacketResult Handle_vAttach(StringExtractorGDBRemote &packet);
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -194,6 +194,15 @@
   RegisterMemberFunctionHandler(
       StringExtractorGDBRemote::eServerPacketType_jLLDBTraceSupportedType,
       &GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceSupportedType);
+  RegisterMemberFunctionHandler(
+      StringExtractorGDBRemote::eServerPacketType_jLLDBTraceStart,
+      &GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceStart);
+  RegisterMemberFunctionHandler(
+      StringExtractorGDBRemote::eServerPacketType_jLLDBTraceStop,
+      &GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceStop);
+  RegisterMemberFunctionHandler(
+      StringExtractorGDBRemote::eServerPacketType_jLLDBTraceQueryData,
+      &GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceQueryData);
 
   RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_g,
                                 &GDBRemoteCommunicationServerLLGS::Handle_g);
@@ -1256,6 +1265,111 @@
   return SendPacketNoLock(escaped_response.GetString());
 }
 
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceStop(
+    StringExtractorGDBRemote &packet) {
+  // Fail if we don't have a current process.
+  if (!m_debugged_process_up ||
+      (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID))
+    return SendErrorResponse(Status("Process not running."));
+
+  packet.ConsumeFront("jLLDBTraceStop:");
+  Expected<TraceStopPacket> stop_packet =
+      json::parse<TraceStopPacket>(packet.Peek());
+  if (!stop_packet)
+    return SendErrorResponse(stop_packet.takeError());
+
+  if (stop_packet->type != "intel-pt")
+    return SendErrorResponse(
+        Status("Unsupported tracing type '%s'", stop_packet->type.c_str()));
+
+  if (Error err = m_debugged_process_up->StopIntelPTTrace(stop_packet->tid))
+    return SendErrorResponse(std::move(err));
+
+  return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceStart(
+    StringExtractorGDBRemote &packet) {
+
+  // Fail if we don't have a current process.
+  if (!m_debugged_process_up ||
+      (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID))
+    return SendErrorResponse(Status("Process not running."));
+
+  packet.ConsumeFront("jLLDBTraceStart:");
+  Expected<TraceStartSimplePacket> start_packet =
+      json::parse<TraceStartSimplePacket>(packet.Peek());
+  if (!start_packet)
+    return SendErrorResponse(start_packet.takeError());
+
+  if (start_packet->type != "intel-pt")
+    return SendErrorResponse(
+        Status("Unsupported tracing type '%s'", start_packet->type.c_str()));
+
+  auto intel_pt_start_packet =
+      json::parse<TraceIntelPTStartPacket>(packet.Peek());
+  if (!intel_pt_start_packet)
+    return SendErrorResponse(intel_pt_start_packet.takeError());
+
+  if (Error err = m_debugged_process_up->StartIntelPTTrace(
+          intel_pt_start_packet->tid,
+          static_cast<size_t>(intel_pt_start_packet->params.buffer_size_in_kb),
+          static_cast<uint32_t>(intel_pt_start_packet->params.perf_config)))
+    return SendErrorResponse(std::move(err));
+
+  return SendOKResponse();
+}
+
+template <typename T> static Expected<json::Value> toJSON(Expected<T> obj) {
+  if (obj)
+    return json::toJSON(*obj);
+  else
+    return obj.takeError();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceQueryData(
+    StringExtractorGDBRemote &packet) {
+
+  // Fail if we don't have a current process.
+  if (!m_debugged_process_up ||
+      (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID))
+    return SendErrorResponse(Status("Process not running."));
+
+  packet.ConsumeFront("jLLDBTraceQueryData:");
+  llvm::Expected<TraceQueryDataSimplePacket> query_data_packet =
+      llvm::json::parse<TraceQueryDataSimplePacket>(packet.Peek());
+  if (!query_data_packet)
+    return SendErrorResponse(Status(query_data_packet.takeError()));
+
+  auto handle_intelpt_buffer_query = [&] {
+    if (auto buffer_packet =
+            json::parse<TraceIntelPTQueryBufferPacket>(packet.Peek()))
+      return SendJSONResponse(toJSON(
+          m_debugged_process_up->GetIntelPTBuffer(buffer_packet->params.tid)));
+    else
+      return SendErrorResponse(buffer_packet.takeError());
+  };
+
+  auto handle_intelpt_cpu_config_query = [&] {
+    return SendJSONResponse(
+        toJSON(m_debugged_process_up->GetIntelPTCPUConfig()));
+  };
+
+  if (query_data_packet->type == "intel-pt") {
+    if (query_data_packet->query == "buffer")
+      return handle_intelpt_buffer_query();
+    else if (query_data_packet->query == "pt_cpu")
+      return handle_intelpt_cpu_config_query();
+  }
+
+  return SendErrorResponse(Status(
+      "Unsupported query '%s' for tracing type '%s'",
+      query_data_packet->query.c_str(), query_data_packet->type.c_str()));
+}
+
 GDBRemoteCommunication::PacketResult
 GDBRemoteCommunicationServerLLGS::Handle_jTraceConfigRead(
     StringExtractorGDBRemote &packet) {
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h
@@ -73,6 +73,13 @@
 
   PacketResult SendOKResponse();
 
+  /// Serialize and send a JSON object response.
+  PacketResult SendJSONResponse(const llvm::json::Value &value);
+
+  /// Serialize and send a JSON object response, or respond with an error if the
+  /// input object is an \a llvm::Error.
+  PacketResult SendJSONResponse(llvm::Expected<llvm::json::Value> value);
+
 private:
   GDBRemoteCommunicationServer(const GDBRemoteCommunicationServer &) = delete;
   const GDBRemoteCommunicationServer &
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp
@@ -16,11 +16,13 @@
 #include "lldb/Utility/StreamString.h"
 #include "lldb/Utility/StringExtractorGDBRemote.h"
 #include "lldb/Utility/UnimplementedError.h"
+#include "llvm/Support/JSON.h"
 #include <cstring>
 
 using namespace lldb;
 using namespace lldb_private;
 using namespace lldb_private::process_gdb_remote;
+using namespace llvm;
 
 GDBRemoteCommunicationServer::GDBRemoteCommunicationServer(
     const char *comm_name, const char *listener_name)
@@ -151,3 +153,21 @@
 bool GDBRemoteCommunicationServer::HandshakeWithClient() {
   return GetAck() == PacketResult::Success;
 }
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServer::SendJSONResponse(const json::Value &value) {
+  std::string json_string;
+  raw_string_ostream os(json_string);
+  os << value;
+  os.flush();
+  StreamGDBRemote escaped_response;
+  escaped_response.PutEscapedBytes(json_string.c_str(), json_string.size());
+  return SendPacketNoLock(escaped_response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServer::SendJSONResponse(Expected<json::Value> value) {
+  if (!value)
+    return SendErrorResponse(value.takeError());
+  return SendJSONResponse(*value);
+}
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -522,6 +522,13 @@
 
   llvm::Expected<TraceTypeInfo> SendGetSupportedTraceType();
 
+  llvm::Error SendTraceStart(const llvm::json::Value &options);
+
+  llvm::Error SendTraceStop(lldb::tid_t tid, llvm::StringRef trace_type);
+
+  llvm::Expected<std::string>
+  SendTraceQueryData(const llvm::json::Value &query);
+
 protected:
   LazyBool m_supports_not_sending_acks;
   LazyBool m_supports_thread_suffix;
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -3480,6 +3480,94 @@
       "failed to send packet: jLLDBTraceSupportedType");
 }
 
+llvm::Error
+GDBRemoteCommunicationClient::SendTraceStop(lldb::tid_t tid,
+                                            llvm::StringRef trace_type) {
+  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+
+  StreamGDBRemote escaped_packet;
+  escaped_packet.PutCString("jLLDBTraceStop:");
+
+  TraceStopPacket packet(trace_type, tid);
+
+  std::string json_string;
+  llvm::raw_string_ostream os(json_string);
+  os << llvm::json::toJSON(packet);
+  os.flush();
+
+  escaped_packet.PutEscapedBytes(json_string.c_str(), json_string.size());
+
+  StringExtractorGDBRemote response;
+  if (SendPacketAndWaitForResponse(escaped_packet.GetString(), response,
+                                   true) ==
+      GDBRemoteCommunication::PacketResult::Success) {
+    if (response.IsOKResponse())
+      return llvm::Error::success();
+    return response.GetStatus().ToError();
+  }
+  LLDB_LOG(log, "failed to send packet: jLLDBTraceStop");
+  return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                 "failed to send packet: jLLDBTraceStop '%s'",
+                                 escaped_packet.GetData());
+}
+
+llvm::Error
+GDBRemoteCommunicationClient::SendTraceStart(const llvm::json::Value &options) {
+  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+
+  StreamGDBRemote escaped_packet;
+  escaped_packet.PutCString("jLLDBTraceStart:");
+
+  std::string json_string;
+  llvm::raw_string_ostream os(json_string);
+  os << options;
+  os.flush();
+
+  escaped_packet.PutEscapedBytes(json_string.c_str(), json_string.size());
+
+  StringExtractorGDBRemote response;
+  if (SendPacketAndWaitForResponse(escaped_packet.GetString(), response,
+                                   true) ==
+      GDBRemoteCommunication::PacketResult::Success) {
+    if (response.IsOKResponse())
+      return llvm::Error::success();
+    return response.GetStatus().ToError();
+  }
+  LLDB_LOG(log, "failed to send packet: jLLDBTraceStart");
+  return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                 "failed to send packet: jLLDBTraceStart '%s'",
+                                 escaped_packet.GetData());
+}
+
+llvm::Expected<std::string> GDBRemoteCommunicationClient::SendTraceQueryData(
+    const llvm::json::Value &query) {
+  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+
+  StreamGDBRemote escaped_packet;
+  escaped_packet.PutCString("jLLDBTraceQueryData:");
+
+  std::string json_string;
+  llvm::raw_string_ostream os(json_string);
+  os << query;
+  os.flush();
+
+  escaped_packet.PutEscapedBytes(json_string.c_str(), json_string.size());
+
+  StringExtractorGDBRemote response;
+  if (SendPacketAndWaitForResponse(escaped_packet.GetString(), response,
+                                   true) ==
+      GDBRemoteCommunication::PacketResult::Success) {
+    if (!response.IsNormalResponse())
+      return response.GetStatus().ToError();
+    return std::string(response.Peek());
+  }
+  LLDB_LOG(log, "failed to send packet: jLLDBTraceQueryData");
+  return llvm::createStringError(
+      llvm::inconvertibleErrorCode(),
+      "failed to send packet: jLLDBTraceQueryData '%s'",
+      escaped_packet.GetData());
+}
+
 Status
 GDBRemoteCommunicationClient::SendGetTraceConfigPacket(lldb::user_id_t uid,
                                                        TraceOptions &options) {
Index: lldb/source/Plugins/Process/Linux/ProcessorTrace.h
===================================================================
--- lldb/source/Plugins/Process/Linux/ProcessorTrace.h
+++ lldb/source/Plugins/Process/Linux/ProcessorTrace.h
@@ -79,8 +79,25 @@
 
   void SetTraceID(lldb::user_id_t traceid) { m_traceid = traceid; }
 
-  Status StartTrace(lldb::pid_t pid, lldb::tid_t tid,
-                    const TraceOptions &config);
+  /// Start tracing a thread
+  ///
+  /// \param[in] pid
+  ///     The pid of the process whose thread will be traced.
+  ///
+  /// \param[in] tid
+  ///     The tid of the thread to trace.
+  ///
+  /// \param[in] buffer_size_in_kb
+  ///     See \a NativeProcessProtocol::StartIntelPTTrace.
+  ///
+  /// \param[in] perf_config
+  ///     See \a NativeProcessProtocol::StartIntelPTTrace.
+  ///
+  /// \return
+  ///     \a llvm::Error::success if tracing was successful, or an
+  ///     \a llvm::Error otherwise.
+  llvm::Error StartTrace(lldb::pid_t pid, lldb::tid_t tid,
+                         size_t buffer_size_in_kb, uint32_t perf_config = 0);
 
   llvm::MutableArrayRef<uint8_t> GetAuxBuffer();
   llvm::MutableArrayRef<uint8_t> GetDataBuffer();
@@ -99,10 +116,27 @@
 
   static Status GetCPUType(TraceOptions &config);
 
+  /// See \a NativeProcessProtocol::GetIntelPTCPUConfig.
+  static llvm::Expected<TraceIntelPTCPUConfig> GetIntelPTCPUConfig();
+
   static llvm::Expected<ProcessorTraceMonitorUP>
   Create(lldb::pid_t pid, lldb::tid_t tid, const TraceOptions &config,
          bool useProcessSettings);
 
+  /// Start tracing a thread.
+  ///
+  /// See \a StartTrace.
+  ///
+  /// \return
+  ///   A \a ProcessorTraceMonitorUP instance if tracing was successful, or
+  ///   an \a llvm::Error otherwise.
+  static llvm::Expected<ProcessorTraceMonitorUP>
+  Create(lldb::pid_t pid, lldb::tid_t tid, size_t buffer_size_in_kb,
+         uint32_t perf_config);
+
+  /// See \a NativeProcessProtocol::GetIntelPTBuffer.
+  llvm::Expected<TraceIntelPTBuffer> GetIntelPTBuffer();
+
   Status ReadPerfTraceAux(llvm::MutableArrayRef<uint8_t> &buffer,
                           size_t offset = 0);
 
Index: lldb/source/Plugins/Process/Linux/ProcessorTrace.cpp
===================================================================
--- lldb/source/Plugins/Process/Linux/ProcessorTrace.cpp
+++ lldb/source/Plugins/Process/Linux/ProcessorTrace.cpp
@@ -16,6 +16,7 @@
 #include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
 #include "ProcessorTrace.h"
 #include "lldb/Host/linux/Support.h"
+#include "lldb/Utility/StreamString.h"
 
 #include <sys/ioctl.h>
 #include <sys/syscall.h>
@@ -67,27 +68,22 @@
 
 bool ProcessorTraceMonitor::IsSupported() { return (bool)GetOSEventType(); }
 
-Status ProcessorTraceMonitor::StartTrace(lldb::pid_t pid, lldb::tid_t tid,
-                                         const TraceOptions &config) {
+Error ProcessorTraceMonitor::StartTrace(lldb::pid_t pid, lldb::tid_t tid,
+                                        size_t buffer_size_in_kb,
+                                        uint32_t perf_config) {
 #ifndef PERF_ATTR_SIZE_VER5
   llvm_unreachable("perf event not supported");
 #else
-  Status error;
   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
 
   LLDB_LOG(log, "called thread id {0}", tid);
   uint64_t page_size = getpagesize();
-  uint64_t bufsize = config.getTraceBufferSize();
-  uint64_t metabufsize = config.getMetaDataBufferSize();
 
+  uint64_t buffer_size = buffer_size_in_kb * 1024;
   uint64_t numpages = static_cast<uint64_t>(
-      llvm::PowerOf2Floor((bufsize + page_size - 1) / page_size));
+      llvm::PowerOf2Floor((buffer_size + page_size - 1) / page_size));
   numpages = std::max<uint64_t>(1, numpages);
-  bufsize = page_size * numpages;
-
-  numpages = static_cast<uint64_t>(
-      llvm::PowerOf2Floor((metabufsize + page_size - 1) / page_size));
-  metabufsize = page_size * numpages;
+  buffer_size = page_size * numpages;
 
   perf_event_attr attr;
   memset(&attr, 0, sizeof(attr));
@@ -98,66 +94,58 @@
   attr.exclude_hv = 1;
   attr.exclude_idle = 1;
   attr.mmap = 1;
+  attr.config = perf_config;
 
   Expected<uint32_t> intel_pt_type = GetOSEventType();
 
-  if (!intel_pt_type) {
-    error = intel_pt_type.takeError();
-    return error;
-  }
+  if (!intel_pt_type)
+    return intel_pt_type.takeError();
 
   LLDB_LOG(log, "intel pt type {0}", *intel_pt_type);
   attr.type = *intel_pt_type;
 
-  LLDB_LOG(log, "meta buffer size {0}", metabufsize);
-  LLDB_LOG(log, "buffer size {0} ", bufsize);
-
-  if (error.Fail()) {
-    LLDB_LOG(log, "Status in custom config");
-
-    return error;
-  }
+  LLDB_LOG(log, "buffer size {0} ", buffer_size);
 
   errno = 0;
   auto fd =
       syscall(SYS_perf_event_open, &attr, static_cast<::tid_t>(tid), -1, -1, 0);
   if (fd == -1) {
     LLDB_LOG(log, "syscall error {0}", errno);
-    error.SetErrorString("perf event syscall Failed");
-    return error;
+    return createStringError(inconvertibleErrorCode(),
+                             "perf event syscall failed");
   }
 
   m_fd = std::unique_ptr<int, file_close>(new int(fd), file_close());
 
   errno = 0;
   auto base =
-      mmap(nullptr, (metabufsize + page_size), PROT_WRITE, MAP_SHARED, fd, 0);
+      mmap(nullptr, (buffer_size + page_size), PROT_WRITE, MAP_SHARED, fd, 0);
 
   if (base == MAP_FAILED) {
     LLDB_LOG(log, "mmap base error {0}", errno);
-    error.SetErrorString("Meta buffer allocation failed");
-    return error;
+    return createStringError(inconvertibleErrorCode(),
+                             "Meta buffer allocation failed");
   }
 
   m_mmap_meta = std::unique_ptr<perf_event_mmap_page, munmap_delete>(
       reinterpret_cast<perf_event_mmap_page *>(base),
-      munmap_delete(metabufsize + page_size));
+      munmap_delete(buffer_size + page_size));
 
   m_mmap_meta->aux_offset = m_mmap_meta->data_offset + m_mmap_meta->data_size;
-  m_mmap_meta->aux_size = bufsize;
+  m_mmap_meta->aux_size = buffer_size;
 
   errno = 0;
-  auto mmap_aux = mmap(nullptr, bufsize, PROT_READ, MAP_SHARED, fd,
+  auto mmap_aux = mmap(nullptr, buffer_size, PROT_READ, MAP_SHARED, fd,
                        static_cast<long int>(m_mmap_meta->aux_offset));
 
   if (mmap_aux == MAP_FAILED) {
     LLDB_LOG(log, "second mmap done {0}", errno);
-    error.SetErrorString("Trace buffer allocation failed");
-    return error;
+    return createStringError(inconvertibleErrorCode(),
+                             "Trace buffer allocation failed");
   }
   m_mmap_aux = std::unique_ptr<uint8_t, munmap_delete>(
-      reinterpret_cast<uint8_t *>(mmap_aux), munmap_delete(bufsize));
-  return error;
+      reinterpret_cast<uint8_t *>(mmap_aux), munmap_delete(buffer_size));
+  return Error::success();
 #endif
 }
 
@@ -180,19 +168,17 @@
 #endif
 }
 
-Status ProcessorTraceMonitor::GetCPUType(TraceOptions &config) {
-
-  Status error;
-  uint64_t cpu_family = -1;
-  uint64_t model = -1;
-  uint64_t stepping = -1;
+Expected<TraceIntelPTCPUConfig> ProcessorTraceMonitor::GetIntelPTCPUConfig() {
+  int64_t cpu_family = -1;
+  int64_t model = -1;
+  int64_t stepping = -1;
   std::string vendor_id;
 
   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
 
   auto BufferOrError = getProcFile("cpuinfo");
   if (!BufferOrError)
-    return BufferOrError.getError();
+    return Status(BufferOrError.getError()).ToError();
 
   LLDB_LOG(log, "GetCPUType Function");
 
@@ -226,29 +212,51 @@
     }
     LLDB_LOG(log, "{0}:{1}:{2}:{3}", cpu_family, model, stepping, vendor_id);
 
-    if ((cpu_family != static_cast<uint64_t>(-1)) &&
-        (model != static_cast<uint64_t>(-1)) &&
-        (stepping != static_cast<uint64_t>(-1)) && (!vendor_id.empty())) {
-      auto params_dict = std::make_shared<StructuredData::Dictionary>();
-      params_dict->AddIntegerItem("cpu_family", cpu_family);
-      params_dict->AddIntegerItem("cpu_model", model);
-      params_dict->AddIntegerItem("cpu_stepping", stepping);
-      params_dict->AddStringItem("cpu_vendor", vendor_id);
+    if ((cpu_family != -1) && (model != -1) && (stepping != -1) &&
+        (!vendor_id.empty())) {
+      return TraceIntelPTCPUConfig{cpu_family, model, stepping,
+                                   vendor_id == "GenuineIntel" ? "intel"
+                                                               : "unknown"};
+    }
+  }
+  return createStringError(inconvertibleErrorCode(), "cpu info not found");
+}
 
-      llvm::StringRef intel_custom_params_key("intel-pt");
+Status ProcessorTraceMonitor::GetCPUType(TraceOptions &config) {
+  Expected<TraceIntelPTCPUConfig> cpu_info_or_err = GetIntelPTCPUConfig();
+  if (!cpu_info_or_err)
+    return Status(cpu_info_or_err.takeError());
+  TraceIntelPTCPUConfig &cpu_info = *cpu_info_or_err;
+
+  auto params_dict = std::make_shared<StructuredData::Dictionary>();
+  params_dict->AddIntegerItem("cpu_family", cpu_info.family);
+  params_dict->AddIntegerItem("cpu_model", cpu_info.model);
+  params_dict->AddIntegerItem("cpu_stepping", cpu_info.stepping);
+  params_dict->AddStringItem("cpu_vendor", cpu_info.vendor);
+
+  llvm::StringRef intel_custom_params_key("intel-pt");
+
+  auto intel_custom_params = std::make_shared<StructuredData::Dictionary>();
+  intel_custom_params->AddItem(
+      intel_custom_params_key,
+      StructuredData::ObjectSP(std::move(params_dict)));
+
+  config.setTraceParams(intel_custom_params);
+  return Status();
+}
 
-      auto intel_custom_params = std::make_shared<StructuredData::Dictionary>();
-      intel_custom_params->AddItem(
-          intel_custom_params_key,
-          StructuredData::ObjectSP(std::move(params_dict)));
+llvm::Expected<ProcessorTraceMonitorUP>
+ProcessorTraceMonitor::Create(lldb::pid_t pid, lldb::tid_t tid,
+                              size_t buffer_size_in_kb, uint32_t config) {
+  ProcessorTraceMonitorUP pt_monitor_up(new ProcessorTraceMonitor);
 
-      config.setTraceParams(intel_custom_params);
-      return error; // we are done
-    }
-  }
+  if (llvm::Error err =
+          pt_monitor_up->StartTrace(pid, tid, buffer_size_in_kb, config))
+    return std::move(err);
 
-  error.SetErrorString("cpu info not found");
-  return error;
+  pt_monitor_up->SetThreadID(tid);
+  pt_monitor_up->SetTraceID(m_trace_num++);
+  return std::move(pt_monitor_up);
 }
 
 llvm::Expected<ProcessorTraceMonitorUP>
@@ -266,7 +274,7 @@
 
   ProcessorTraceMonitorUP pt_monitor_up(new ProcessorTraceMonitor);
 
-  error = pt_monitor_up->StartTrace(pid, tid, config);
+  error = pt_monitor_up->StartTrace(pid, tid, config.getTraceBufferSize());
   if (error.Fail())
     return error.ToError();
 
@@ -281,6 +289,15 @@
   return std::move(pt_monitor_up);
 }
 
+Expected<TraceIntelPTBuffer> ProcessorTraceMonitor::GetIntelPTBuffer() {
+  TraceIntelPTBuffer buffer(m_mmap_meta->aux_size);
+  MutableArrayRef<uint8_t> buffer_ref(buffer.data);
+  Status error = ReadPerfTraceAux(buffer_ref, 0);
+  if (error.Fail())
+    return error.ToError();
+  return std::move(buffer);
+}
+
 Status
 ProcessorTraceMonitor::ReadPerfTraceAux(llvm::MutableArrayRef<uint8_t> &buffer,
                                         size_t offset) {
Index: lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
===================================================================
--- lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
+++ lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
@@ -101,6 +101,15 @@
     return getProcFile(GetID(), "auxv");
   }
 
+  llvm::Error StopIntelPTTrace(lldb::tid_t tid) override;
+
+  llvm::Error StartIntelPTTrace(lldb::tid_t tid, size_t buffer_size_in_kb,
+                                uint32_t perf_config) override;
+
+  llvm::Expected<TraceIntelPTBuffer> GetIntelPTBuffer(lldb::tid_t tid) override;
+
+  llvm::Expected<TraceIntelPTCPUConfig> GetIntelPTCPUConfig() override;
+
   lldb::user_id_t StartTrace(const TraceOptions &config,
                              Status &error) override;
 
Index: lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
===================================================================
--- lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
+++ lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
@@ -2033,6 +2033,51 @@
   return m_pt_proces_trace_id;
 }
 
+Error NativeProcessLinux::StopIntelPTTrace(lldb::tid_t tid) {
+  const auto &trace_instance = m_processor_trace_monitor.find(tid);
+  if (trace_instance == m_processor_trace_monitor.end())
+    return createStringError(inconvertibleErrorCode(), "thread not traced");
+
+  return StopProcessorTracingOnThread(trace_instance->second->GetTraceID(), tid)
+      .ToError();
+}
+
+Error NativeProcessLinux::StartIntelPTTrace(lldb::tid_t tid,
+                                            size_t buffer_size_in_kb,
+                                            uint32_t perf_config) {
+  if (!GetThreadByID(tid))
+    return createStringError(inconvertibleErrorCode(), "invalid thread id");
+
+  const auto &trace_instance = m_processor_trace_monitor.find(tid);
+  if (trace_instance != m_processor_trace_monitor.end())
+    return createStringError(inconvertibleErrorCode(),
+                             "tracing already active on this thread");
+
+  Expected<ProcessorTraceMonitorUP> trace_monitor =
+      ProcessorTraceMonitor::Create(GetID(), tid, buffer_size_in_kb,
+                                    perf_config);
+  if (!trace_monitor)
+    return trace_monitor.takeError();
+
+  m_processor_trace_monitor.try_emplace(tid, std::move(*trace_monitor));
+  return Error::success();
+}
+
+Expected<TraceIntelPTBuffer>
+NativeProcessLinux::GetIntelPTBuffer(lldb::tid_t tid) {
+  const auto &trace_instance = m_processor_trace_monitor.find(tid);
+  if (trace_instance == m_processor_trace_monitor.end())
+    return createStringError(inconvertibleErrorCode(),
+                             "thread not currently traced");
+
+  return trace_instance->second->GetIntelPTBuffer();
+}
+
+llvm::Expected<TraceIntelPTCPUConfig>
+NativeProcessLinux::GetIntelPTCPUConfig() {
+  return ProcessorTraceMonitor::GetIntelPTCPUConfig();
+}
+
 lldb::user_id_t NativeProcessLinux::StartTrace(const TraceOptions &config,
                                                Status &error) {
   if (config.getType() != TraceType::eTraceTypeProcessorTrace)
Index: lldb/source/Core/PluginManager.cpp
===================================================================
--- lldb/source/Core/PluginManager.cpp
+++ lldb/source/Core/PluginManager.cpp
@@ -1008,16 +1008,21 @@
 
 #pragma mark Trace
 
-struct TraceInstance : public PluginInstance<TraceCreateInstance> {
-  TraceInstance(ConstString name, std::string description,
-                CallbackType create_callback, llvm::StringRef schema,
-                TraceGetStartCommand get_start_command)
-      : PluginInstance<TraceCreateInstance>(name, std::move(description),
-                                            create_callback),
-        schema(schema), get_start_command(get_start_command) {}
+struct TraceInstance
+    : public PluginInstance<TraceCreateInstanceForSessionFile> {
+  TraceInstance(
+      ConstString name, std::string description,
+      CallbackType create_callback_for_session_file,
+      TraceCreateInstanceForLiveProcess create_callback_for_live_process,
+      llvm::StringRef schema, TraceGetStartCommand get_start_command)
+      : PluginInstance<TraceCreateInstanceForSessionFile>(
+            name, std::move(description), create_callback_for_session_file),
+        schema(schema), get_start_command(get_start_command),
+        create_callback_for_live_process(create_callback_for_live_process) {}
 
   llvm::StringRef schema;
   TraceGetStartCommand get_start_command;
+  TraceCreateInstanceForLiveProcess create_callback_for_live_process;
 };
 
 typedef PluginInstances<TraceInstance> TraceInstances;
@@ -1027,23 +1032,35 @@
   return g_instances;
 }
 
-bool PluginManager::RegisterPlugin(ConstString name, const char *description,
-                                   TraceCreateInstance create_callback,
-                                   llvm::StringRef schema,
-                                   TraceGetStartCommand get_start_command) {
+bool PluginManager::RegisterPlugin(
+    ConstString name, const char *description,
+    TraceCreateInstanceForSessionFile create_callback_for_session_file,
+    TraceCreateInstanceForLiveProcess create_callback_for_live_process,
+    llvm::StringRef schema, TraceGetStartCommand get_start_command) {
   return GetTracePluginInstances().RegisterPlugin(
-      name, description, create_callback, schema, get_start_command);
+      name, description, create_callback_for_session_file,
+      create_callback_for_live_process, schema, get_start_command);
 }
 
-bool PluginManager::UnregisterPlugin(TraceCreateInstance create_callback) {
-  return GetTracePluginInstances().UnregisterPlugin(create_callback);
+bool PluginManager::UnregisterPlugin(
+    TraceCreateInstanceForSessionFile create_callback_for_session_file) {
+  return GetTracePluginInstances().UnregisterPlugin(
+      create_callback_for_session_file);
 }
 
-TraceCreateInstance
+TraceCreateInstanceForSessionFile
 PluginManager::GetTraceCreateCallback(ConstString plugin_name) {
   return GetTracePluginInstances().GetCallbackForName(plugin_name);
 }
 
+TraceCreateInstanceForLiveProcess
+PluginManager::GetTraceCreateCallbackForLiveProcess(ConstString plugin_name) {
+  for (const TraceInstance &instance : GetTracePluginInstances().GetInstances())
+    if (instance.name == plugin_name)
+      return instance.create_callback_for_live_process;
+  return nullptr;
+}
+
 llvm::StringRef PluginManager::GetTraceSchema(ConstString plugin_name) {
   for (const TraceInstance &instance : GetTracePluginInstances().GetInstances())
     if (instance.name == plugin_name)
Index: lldb/source/Commands/CommandObjectThreadUtil.h
===================================================================
--- lldb/source/Commands/CommandObjectThreadUtil.h
+++ lldb/source/Commands/CommandObjectThreadUtil.h
@@ -76,6 +76,23 @@
   bool m_add_return = true;
 };
 
+/// Base class that should be extended by Trace plug-ins to implement
+/// thread-level tracing.
+class CommandObjecThreadTraceStart : public CommandObjectIterateOverThreads {
+public:
+  CommandObjecThreadTraceStart(llvm::StringRef trace_plugin_name,
+                               CommandInterpreter &interpreter,
+                               const char *name, const char *help,
+                               const char *syntax, uint32_t flags)
+      : CommandObjectIterateOverThreads(interpreter, name, help, syntax, flags),
+        m_trace_plugin_name(trace_plugin_name.str()) {}
+
+  bool DoExecute(Args &command, CommandReturnObject &result) override;
+
+private:
+  std::string m_trace_plugin_name;
+};
+
 } // namespace lldb_private
 
 #endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTTHREADUTIL_H
Index: lldb/source/Commands/CommandObjectThreadUtil.cpp
===================================================================
--- lldb/source/Commands/CommandObjectThreadUtil.cpp
+++ lldb/source/Commands/CommandObjectThreadUtil.cpp
@@ -156,3 +156,20 @@
   }
   return true;
 }
+
+bool CommandObjecThreadTraceStart::DoExecute(Args &command,
+                                             CommandReturnObject &result) {
+  ProcessSP process_sp = m_exe_ctx.GetProcessSP();
+  /// We create a new Trace instance so that plug-ins can assume it exists.
+  if (!process_sp->GetTarget().GetTrace()) {
+    Expected<TraceSP> trace =
+        Trace::FindPlugin(m_trace_plugin_name, *process_sp);
+    if (!trace) {
+      result.SetStatus(eReturnStatusFailed);
+      result.AppendErrorWithFormat("couldn't start tracing the process. %s",
+                                   toString(trace.takeError()).c_str());
+      return false;
+    }
+  }
+  return CommandObjectIterateOverThreads::DoExecute(command, result);
+}
Index: lldb/include/lldb/lldb-private-interfaces.h
===================================================================
--- lldb/include/lldb/lldb-private-interfaces.h
+++ lldb/include/lldb/lldb-private-interfaces.h
@@ -111,9 +111,11 @@
                                            const char *repl_options);
 typedef int (*ComparisonFunction)(const void *, const void *);
 typedef void (*DebuggerInitializeCallback)(Debugger &debugger);
-typedef llvm::Expected<lldb::TraceSP> (*TraceCreateInstance)(
+typedef llvm::Expected<lldb::TraceSP> (*TraceCreateInstanceForSessionFile)(
     const llvm::json::Value &trace_session_file,
     llvm::StringRef session_file_dir, lldb_private::Debugger &debugger);
+typedef llvm::Expected<lldb::TraceSP> (*TraceCreateInstanceForLiveProcess)(
+    Process &process);
 typedef lldb::CommandObjectSP (*TraceGetStartCommand)(
     CommandInterpreter &interpreter);
 
Index: lldb/include/lldb/Utility/TraceOptions.h
===================================================================
--- lldb/include/lldb/Utility/TraceOptions.h
+++ lldb/include/lldb/Utility/TraceOptions.h
@@ -68,11 +68,175 @@
   /// the lldb-server.
   StructuredData::DictionarySP m_trace_params;
 };
-}
+
+/// jLLDBTraceStop gdb-remote packet structures
+/// \{
+struct TraceStopPacket {
+  TraceStopPacket() {}
+
+  TraceStopPacket(llvm::StringRef type, int64_t tid) : type(type), tid(tid) {}
+
+  std::string type;
+  int64_t tid;
+};
+///}
+
+/// jLLDBTraceStart gdb-remote packet structures
+/// \{
+struct TraceStartSimplePacket {
+  TraceStartSimplePacket() {}
+
+  TraceStartSimplePacket(llvm::StringRef type, int64_t tid)
+      : type(type), tid(tid) {}
+
+  std::string type;
+  int64_t tid;
+};
+
+template <typename TParams> struct TraceStartPacket : TraceStartSimplePacket {
+  TraceStartPacket() {}
+
+  TraceStartPacket(llvm::StringRef type, int64_t tid, const TParams &params)
+      : TraceStartSimplePacket(type, tid), params(params) {}
+
+  TParams params;
+};
+
+struct TraceIntelPTStartPacketParams {
+  int64_t buffer_size_in_kb;
+  int64_t perf_config;
+};
+
+using TraceIntelPTStartPacket = TraceStartPacket<TraceIntelPTStartPacketParams>;
+/// \}
+
+/// jTraceQueryData gdb-remote packet structures
+/// \{
+struct TraceQueryDataSimplePacket {
+  TraceQueryDataSimplePacket() {}
+
+  TraceQueryDataSimplePacket(llvm::StringRef type, llvm::StringRef query)
+      : type(type), query(query) {}
+  std::string type;
+  std::string query;
+};
+
+template <typename TParams>
+struct TraceQueryDataPacket : TraceQueryDataSimplePacket {
+  TraceQueryDataPacket() {}
+
+  TraceQueryDataPacket(llvm::StringRef type, llvm::StringRef query,
+                       const TParams &params)
+      : TraceQueryDataSimplePacket(type, query), params(params) {}
+
+  TParams params;
+};
+
+struct TraceIntelPTQueryBufferParams {
+  int64_t tid;
+};
+
+struct TraceIntelPTBuffer {
+  TraceIntelPTBuffer() : data() {}
+
+  TraceIntelPTBuffer(size_t size) : data(size) {}
+
+  std::vector<uint8_t> data;
+};
+
+struct TraceIntelPTCPUConfig {
+  int64_t family;
+  int64_t model;
+  int64_t stepping;
+  std::string vendor;
+};
+
+using TraceIntelPTQueryBufferPacket =
+    TraceQueryDataPacket<TraceIntelPTQueryBufferParams>;
+/// \}
+
+} // namespace lldb_private
 
 namespace llvm {
 namespace json {
 
+/// jLLDBTraceStop
+/// \{
+bool fromJSON(const Value &value, lldb_private::TraceStopPacket &packet,
+              Path path);
+
+Value toJSON(const lldb_private::TraceStopPacket &packet);
+///}
+
+/// jLLDBTraceStart
+/// \{
+bool fromJSON(const Value &value, lldb_private::TraceStartSimplePacket &packet,
+              Path path);
+
+bool fromJSON(const Value &value,
+              lldb_private::TraceIntelPTStartPacketParams &packet, Path path);
+
+Value toJSON(const lldb_private::TraceIntelPTStartPacketParams &packet);
+
+template <typename TParams>
+bool fromJSON(const Value &value,
+              lldb_private::TraceStartPacket<TParams> &packet, Path path) {
+  ObjectMapper o(value, path);
+  return o &&
+         fromJSON(value, (lldb_private::TraceStartSimplePacket &)packet,
+                  path) &&
+         o.map("params", packet.params);
+}
+
+template <typename TParams>
+Value toJSON(const lldb_private::TraceStartPacket<TParams> &packet) {
+  return Object{{"tid", packet.tid},
+                {"type", packet.type},
+                {"params", toJSON(packet.params)}};
+}
+/// \}
+
+/// jLLDBTraceQueryData
+/// \{
+bool fromJSON(const Value &value,
+              lldb_private::TraceQueryDataSimplePacket &packet, Path path);
+
+Value toJSON(const lldb_private::TraceQueryDataSimplePacket &packet);
+
+bool fromJSON(const Value &value,
+              lldb_private::TraceIntelPTQueryBufferParams &params, Path path);
+
+Value toJSON(const lldb_private::TraceIntelPTQueryBufferParams &buffer);
+
+template <typename TParams>
+bool fromJSON(const Value &value,
+              lldb_private::TraceQueryDataPacket<TParams> &packet, Path path) {
+  ObjectMapper o(value, path);
+  return o &&
+         fromJSON(value, (lldb_private::TraceQueryDataSimplePacket &)packet,
+                  path) &&
+         o.map("params", packet.params);
+}
+
+template <typename TParams>
+Value toJSON(const lldb_private::TraceQueryDataPacket<TParams> &packet) {
+  return llvm::json::Object{{"query", packet.query},
+                            {"type", packet.type},
+                            {"params", toJSON(packet.params)}};
+}
+
+bool fromJSON(const Value &value, lldb_private::TraceIntelPTBuffer &response,
+              Path path);
+
+Value toJSON(const lldb_private::TraceIntelPTBuffer &buffer);
+
+bool fromJSON(const Value &value, lldb_private::TraceIntelPTCPUConfig &pt_cpu,
+              Path path);
+
+Value toJSON(const lldb_private::TraceIntelPTCPUConfig &cpu_config);
+
+/// \}
+
 bool fromJSON(const Value &value, lldb_private::TraceTypeInfo &info, Path path);
 
 } // namespace json
Index: lldb/include/lldb/Utility/StringExtractorGDBRemote.h
===================================================================
--- lldb/include/lldb/Utility/StringExtractorGDBRemote.h
+++ lldb/include/lldb/Utility/StringExtractorGDBRemote.h
@@ -168,6 +168,9 @@
     eServerPacketType_jTraceStop,       // deprecated
     eServerPacketType_jTraceConfigRead, // deprecated
 
+    eServerPacketType_jLLDBTraceQueryData,
+    eServerPacketType_jLLDBTraceStart,
+    eServerPacketType_jLLDBTraceStop,
     eServerPacketType_jLLDBTraceSupportedType,
   };
 
Index: lldb/include/lldb/Target/Trace.h
===================================================================
--- lldb/include/lldb/Target/Trace.h
+++ lldb/include/lldb/Target/Trace.h
@@ -12,7 +12,9 @@
 #include "llvm/Support/JSON.h"
 
 #include "lldb/Core/PluginInterface.h"
+#include "lldb/Target/Thread.h"
 #include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/TraceOptions.h"
 #include "lldb/Utility/UnimplementedError.h"
 #include "lldb/lldb-private.h"
 
@@ -97,6 +99,21 @@
   FindPlugin(Debugger &debugger, const llvm::json::Value &trace_session_file,
              llvm::StringRef session_file_dir);
 
+  /// Find a trace plug-in to trace a live process.
+  ///
+  /// \param[in] plugin_name
+  ///     Plug-in name to search.
+  ///
+  /// \param[in] process
+  ///     Live process to trace.
+  ///
+  /// \return
+  ///     A \a TraceSP instance, or an \a llvm::Error if the plug-in name
+  ///     doesn't match any registered plug-ins or tracing couldn't be
+  ///     started.
+  static llvm::Expected<lldb::TraceSP> FindPlugin(llvm::StringRef plugin_name,
+                                                  Process &process);
+
   /// Get the schema of a Trace plug-in given its name.
   ///
   /// \param[in] plugin_name
@@ -196,8 +213,102 @@
   /// \return
   ///     The total number of instructions in the trace.
   virtual size_t GetInstructionCount(const Thread &thread) = 0;
+
+protected:
+  /// Start tracing a thread.
+  ///
+  /// Base implementation that should be invoked by plug-ins to start tracing.
+  ///
+  /// \param[in] thread
+  ///     The thread to trace.
+  ///
+  /// \param[in] params
+  ///     json-serializable params specific to the thread plug-in.
+  ///
+  /// \return
+  ///     \a llvm::Error::success if the operation was successful, or
+  ///     \a llvm::Error otherwise.
+  template <typename TParams>
+  llvm::Error StartTracingThread(Thread &thread, const TParams &params);
+
+  /// Query tracing-related data.
+  ///
+  /// Base implementation that should be invoked by plug-ins to query data.
+  ///
+  /// \param[in] process
+  ///     The process that owns the queried data.
+  ///
+  /// \param[in] trace_type
+  ///     The tracing technology, e.g. intel-pt, arm-coresight, etc.
+  ///
+  /// \param[in] query
+  ///     An identifier for the requested data.
+  ///
+  /// \param[in] params
+  ///     JSON-serializable params specific to the query.
+  ///
+  /// \return
+  ///     A JSON-serializable templated object holding the requested data, or
+  ///     an \a llvm::Error in case of failures.
+  template <typename TResponse, typename TParams>
+  static llvm::Expected<TResponse>
+  QueryData(Process &process, llvm::StringRef trace_type, llvm::StringRef query,
+            const TParams &params);
+
+  /// Version of \a Query Data that doesn't require parameters for the query.
+  template <typename TResponse>
+  static llvm::Expected<TResponse> QueryData(Process &process,
+                                             llvm::StringRef trace_type,
+                                             llvm::StringRef query);
+
+private:
+  /// Helper class to reduce the implementation of the templated \a
+  /// StartTracingThread.
+  llvm::Error DoStartTracingThread(Thread &thread,
+                                   const llvm::json::Value &packet);
+
+  /// Helper class to reduce the implementation of the templated \a QueryData.
+  static llvm::Expected<std::string>
+  DoQueryData(Process &process, const llvm::json::Value &packet);
 };
 
+template <typename TParams>
+llvm::Error Trace::StartTracingThread(Thread &thread, const TParams &params) {
+  TraceStartPacket<TParams> packet(GetPluginName().GetStringRef(),
+                                   thread.GetID(), params);
+  return DoStartTracingThread(thread, llvm::json::toJSON(packet));
+}
+
+template <typename TResponse, typename TParams>
+llvm::Expected<TResponse>
+Trace::QueryData(Process &process, llvm::StringRef trace_type,
+                 llvm::StringRef query, const TParams &params) {
+  TraceQueryDataPacket<TParams> packet(trace_type, query, params);
+
+  llvm::Expected<std::string> response =
+      DoQueryData(process, llvm::json::toJSON(packet));
+
+  if (response)
+    return llvm::json::parse<TResponse>(*response);
+  else
+    return response.takeError();
+}
+
+template <typename TResponse>
+llvm::Expected<TResponse> Trace::QueryData(Process &process,
+                                           llvm::StringRef trace_type,
+                                           llvm::StringRef query) {
+  TraceQueryDataSimplePacket packet(trace_type, query);
+
+  llvm::Expected<std::string> response =
+      DoQueryData(process, llvm::json::toJSON(packet));
+
+  if (response)
+    return llvm::json::parse<TResponse>(*response);
+  else
+    return response.takeError();
+}
+
 } // namespace lldb_private
 
 #endif // LLDB_TARGET_TRACE_H
Index: lldb/include/lldb/Target/Process.h
===================================================================
--- lldb/include/lldb/Target/Process.h
+++ lldb/include/lldb/Target/Process.h
@@ -2555,6 +2555,48 @@
   ///     not supported for the inferior.
   virtual llvm::Expected<TraceTypeInfo> GetSupportedTraceType();
 
+  /// Start tracing a thread. It fails if the thread is already being traced.
+  ///
+  /// \param[in] options
+  ///     JSON structure with the parameters to start the tracing.
+  ///
+  /// \return
+  ///     \a llvm::Error::success if the operation was successful, or an
+  ///     \a llvm::Error otherwise.
+  virtual llvm::Error StartTracingThread(const llvm::json::Value &options) {
+    return llvm::make_error<UnimplementedError>();
+  }
+
+  /// Stop tracing a thread.
+  ///
+  /// \param[in] tid
+  ///     The thread id to stop tracing.
+  ///
+  /// \param[in] trace_type
+  ///     The trace technology name to start tracing with, e.g. intel-pt,
+  ///     arm-coresight, etc.
+  ///
+  /// \return
+  ///     \a llvm::Error::success if stopping was successful, or an
+  ///     \a llvm::Error otherwise.
+  virtual llvm::Error StopTracingThread(lldb::tid_t tid,
+                                        llvm::StringRef trace_type) {
+    return llvm::make_error<UnimplementedError>();
+  }
+
+  /// Query tracing-related data
+  ///
+  /// \param[in] query
+  ///     JSON structure with the parameters that describe the query.
+  ///
+  /// \return
+  ///     An unparsed JSON string with the queried data, or an \a llvm::Error
+  ///     in case of failures.
+  virtual llvm::Expected<std::string>
+  TraceQueryData(const llvm::json::Value &query) {
+    return llvm::make_error<UnimplementedError>();
+  }
+
   // This calls a function of the form "void * (*)(void)".
   bool CallVoidArgVoidPtrReturn(const Address *address,
                                 lldb::addr_t &returned_func,
Index: lldb/include/lldb/Host/common/NativeProcessProtocol.h
===================================================================
--- lldb/include/lldb/Host/common/NativeProcessProtocol.h
+++ lldb/include/lldb/Host/common/NativeProcessProtocol.h
@@ -306,6 +306,67 @@
            MainLoop &mainloop) const = 0;
   };
 
+  /// Intel PT interface
+  /// \{
+
+  /// Stop tracing a thread.
+  ///
+  /// \param[in] tid
+  ///     The thread id to stop tracing.
+  ///
+  /// \return
+  ///     \a llvm::Error::success if stopping was successful, or an
+  ///     \a llvm::Error otherwise.
+  virtual llvm::Error StopIntelPTTrace(lldb::tid_t tid) {
+    return llvm::make_error<UnimplementedError>();
+  }
+
+  /// Start tracing a thread. It fails if the thread is already being traced.
+  ///
+  /// \param[in] tid
+  ///     The thread id to start tracing.
+  ///
+  /// \param[in] buffer_size_in_kb
+  ///     The size of the circular trace buffer. It is rounded up to the nearest
+  ///     power of 2.
+  ///
+  /// \param[in] perf_config
+  ///     Low level perf event config. See
+  ///     https://github.com/torvalds/linux/blob/master/tools/perf/Documentation/perf-intel-pt.txt
+  ///     for more information
+  ///
+  /// \return
+  ///     \a llvm::Error::success if the operation was successful, or an
+  ///     \a llvm::Error otherwise.
+  virtual llvm::Error StartIntelPTTrace(lldb::tid_t tid,
+                                        size_t buffer_size_in_kb,
+                                        uint32_t perf_config = 0) {
+    return llvm::make_error<UnimplementedError>();
+  }
+
+  /// Get the trace buffer for a thread.
+  ///
+  /// \param[in] tid
+  ///     The thread id of the thread whose trace buffer is requested.
+  ///
+  /// \return
+  ///     A \a TraceIntelPTBuffer object, or an \a llvm::Error in case of
+  ///     failures.
+  virtual llvm::Expected<TraceIntelPTBuffer> GetIntelPTBuffer(lldb::tid_t tid) {
+    return llvm::make_error<UnimplementedError>();
+  }
+
+  /// Get the Intel CPU config for the current host, which is used for decoding
+  /// trace buffers.
+  ///
+  /// \return
+  ///     A \a TraceIntelPTCPUConfig object, or an \a llvm::Error in case of
+  ///     failures.
+  virtual llvm::Expected<TraceIntelPTCPUConfig> GetIntelPTCPUConfig() {
+    return llvm::make_error<UnimplementedError>();
+  }
+  /// \}
+
   /// StartTracing API for starting a tracing instance with the
   /// TraceOptions on a specific thread or process.
   ///
Index: lldb/include/lldb/Core/PluginManager.h
===================================================================
--- lldb/include/lldb/Core/PluginManager.h
+++ lldb/include/lldb/Core/PluginManager.h
@@ -331,14 +331,20 @@
   GetSymbolVendorCreateCallbackAtIndex(uint32_t idx);
 
   // Trace
-  static bool RegisterPlugin(ConstString name, const char *description,
-                             TraceCreateInstance create_callback,
-                             llvm::StringRef schema,
-                             TraceGetStartCommand get_start_command);
+  static bool RegisterPlugin(
+      ConstString name, const char *description,
+      TraceCreateInstanceForSessionFile create_callback_for_session_file,
+      TraceCreateInstanceForLiveProcess create_callback_for_live_process,
+      llvm::StringRef schema, TraceGetStartCommand get_start_command);
+
+  static bool
+  UnregisterPlugin(TraceCreateInstanceForSessionFile create_callback);
 
-  static bool UnregisterPlugin(TraceCreateInstance create_callback);
+  static TraceCreateInstanceForSessionFile
+  GetTraceCreateCallback(ConstString plugin_name);
 
-  static TraceCreateInstance GetTraceCreateCallback(ConstString plugin_name);
+  static TraceCreateInstanceForLiveProcess
+  GetTraceCreateCallbackForLiveProcess(ConstString plugin_name);
 
   static lldb::CommandObjectSP
   GetTraceStartCommand(llvm::StringRef plugin_name,
Index: lldb/docs/lldb-gdb-remote.txt
===================================================================
--- lldb/docs/lldb-gdb-remote.txt
+++ lldb/docs/lldb-gdb-remote.txt
@@ -261,6 +261,110 @@
 send packet: jLLDBTraceSupportedType
 read packet: {"name": <name>, "description", <description>}/E<error code>;AAAAAAAAA
 
+//----------------------------------------------------------------------
+// jLLDBTraceStart
+//
+// BRIEF
+//  Start tracing a thread using a provided tracing technology. The input
+//  is specified as a JSON object. If tracing started succesfully, an OK
+//  response is returned, or an error otherwise.
+//
+// SCHEMA
+//  The schema for the input is
+//
+//  {
+//    "type": <tracing technology name, e.g. intel-pt, arm-coresight>
+//    "tid": <thread id trace in decimal>
+//    "params": {
+//      <JSON object with parameters specific to the selected tracing technology>
+//    }
+//  }
+//
+// intel-pt
+//  The schema for the "params" field for the intel-pt tracing technology is
+//
+//  {
+//    "bufferSizeInKB": <trace buffer size in KB in decimal>
+//    "perf_config": <custom low-level perf event intel-pt config in decimal>
+//  }
+//----------------------------------------------------------------------
+
+send packet: jLLDBTraceStart:{"type":<type>,"tid":<tid>,"params":<params>}]
+read packet: OK/E<error code>;AAAAAAAAA
+
+//----------------------------------------------------------------------
+// jLLDBTraceStop
+//
+// BRIEF
+//  Stop tracing a thread using a provided tracing technology. The input
+//  is specified as a JSON object. If tracing was stopped succesfully, an OK
+//  response is returned, or an error otherwise.
+//
+// SCHEMA
+//  The schema for the input is
+//
+//  {
+//    "type": <tracing technology name, e.g. intel-pt, arm-coresight>
+//    "tid": <thread id trace in decimal>
+//  }
+//----------------------------------------------------------------------
+
+send packet: jLLDBTraceStop:{"type":<type>,"tid":<tid>}]
+read packet: OK/E<error code>;AAAAAAAAA
+
+//----------------------------------------------------------------------
+// jLLDBTraceQueryData
+//
+// BRIEF
+//  Get custom data given a trace technology and a query identifier. The input
+//  is specified as a JSON object. The response is a JSON object that
+//  depends on the queried data, or an error in case of failures.
+//
+// SCHEMA
+//  The schema for the input is
+//
+//  {
+//    "type": <tracing technology name, e.g. intel-pt, arm-coresight>
+//    "query": <identifier for the data>
+//    "params": {
+//      <optional JSON object with parameters specific to the queried data>
+//    }
+//  }
+//
+// intel-pt query "pt_cpu"
+//
+//  This query doesn't not have any input params.
+//
+//  The schema for the return value is
+//
+//  {
+//    "vendor": "intel" | "unknown",
+//    "family": <decimal>,
+//    "model": <decimal>,
+//    "stepping": <decimal>
+//  }
+//
+// intel-pt query "buffer"
+//
+//  The schema for the "params" field in this query is
+//
+//  {
+//    "tid": <thread id trace in decimal>
+//  }
+//
+//  The schema for the return value is
+//
+//  {
+//    "data": <contents of the trace buffer in HEX8 format>
+//  }
+//----------------------------------------------------------------------
+
+send packet: jLLDBTraceQueryData:{"type":<type>,"query":<query>}]
+read packet: {return value}/E<error code>;AAAAAAAAA
+
+send packet: jLLDBTraceQueryData:{"type":<type>,"query":<query>,"params":<params>}]
+read packet: {return value}/E<error code>;AAAAAAAAA
+
 //----------------------------------------------------------------------
 // jTraceStart:
 //
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to