Author: Walter Erquinigo
Date: 2020-09-21T17:13:18-07:00
New Revision: 74c93956e1c1f1054dfb040ce26830016e0f3095

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

LOG: Add a "Trace" plug-in to LLDB to add process trace support in stages.

This is the first in a series of patches that will adds a new processor trace 
plug-in to LLDB.

The idea for this first patch to to add the plug-in interface with simple 
commands for the trace files that can "load" and "dump" the trace information. 
We can test the functionality and ensure people are happy with the way things 
are done and how things are organized before moving on to adding more 
functionality.

Processor trace information can be view in a few different ways:
- post mortem where a trace is saved off that can be viewed later in the 
debugger
- gathered while a process is running and allow the user to step back in time 
(with no variables, memory or registers) to see how each thread arrived at 
where it is currently stopped.

This patch attempts to start with the first solution of loading a trace file 
after the fact. The idea is that we will use a JSON file to load the trace 
information. JSON allows us to specify information about the trace like:
- plug-in name in LLDB
- path to trace file
- shared library load information so we can re-create a target and symbolicate 
the information in the trace
- any other info that the trace plug-in will need to be able to successfully 
parse the trace information
  - cpu type
  - version info
  - ???

A new "trace" command was added at the top level of the LLDB commmands:
- "trace load"
- "trace dump"

I did this because if we load trace information we don't need to have a process 
and we might end up creating a new target for the trace information that will 
become active. If anyone has any input on where this would be better suited, 
please let me know. Walter Erquinigo will end up filling in the Intel PT 
specific plug-in so that it works and is tested once we can agree that the 
direction of this patch is the correct one, so please feel free to chime in 
with ideas on comments!

Reviewed By: clayborg

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

Added: 
    lldb/include/lldb/Target/Trace.h
    lldb/include/lldb/Target/TraceSettingsParser.h
    lldb/source/Commands/CommandObjectTrace.cpp
    lldb/source/Commands/CommandObjectTrace.h
    lldb/source/Plugins/Trace/CMakeLists.txt
    lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt
    lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
    lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
    lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSettingsParser.cpp
    lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSettingsParser.h
    lldb/source/Target/Trace.cpp
    lldb/source/Target/TraceSettingsParser.cpp
    lldb/test/API/commands/trace/TestTraceLoad.py
    lldb/test/API/commands/trace/TestTraceSchema.py
    lldb/test/API/commands/trace/intelpt-trace/3842849.trace
    lldb/test/API/commands/trace/intelpt-trace/a.out
    lldb/test/API/commands/trace/intelpt-trace/main.cpp
    lldb/test/API/commands/trace/intelpt-trace/trace.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

Modified: 
    lldb/include/lldb/Core/PluginManager.h
    lldb/include/lldb/lldb-forward.h
    lldb/include/lldb/lldb-private-interfaces.h
    lldb/source/Commands/CMakeLists.txt
    lldb/source/Commands/Options.td
    lldb/source/Core/PluginManager.cpp
    lldb/source/Interpreter/CommandInterpreter.cpp
    lldb/source/Plugins/CMakeLists.txt
    lldb/source/Target/CMakeLists.txt
    lldb/source/Utility/StructuredData.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Core/PluginManager.h 
b/lldb/include/lldb/Core/PluginManager.h
index 5e0c9395dae0..cd962b668163 100644
--- a/lldb/include/lldb/Core/PluginManager.h
+++ b/lldb/include/lldb/Core/PluginManager.h
@@ -330,6 +330,14 @@ class PluginManager {
   static SymbolVendorCreateInstance
   GetSymbolVendorCreateCallbackAtIndex(uint32_t idx);
 
+  // Trace
+  static bool RegisterPlugin(ConstString name, const char *description,
+                             TraceCreateInstance create_callback);
+
+  static bool UnregisterPlugin(TraceCreateInstance create_callback);
+
+  static TraceCreateInstance GetTraceCreateCallback(ConstString plugin_name);
+
   // UnwindAssembly
   static bool RegisterPlugin(ConstString name, const char *description,
                              UnwindAssemblyCreateInstance create_callback);

diff  --git a/lldb/include/lldb/Target/Trace.h 
b/lldb/include/lldb/Target/Trace.h
new file mode 100644
index 000000000000..2328fba822a2
--- /dev/null
+++ b/lldb/include/lldb/Target/Trace.h
@@ -0,0 +1,145 @@
+//===-- Trace.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_TRACE_H
+#define LLDB_TARGET_TRACE_H
+
+#include "llvm/Support/JSON.h"
+
+#include "lldb/Core/PluginInterface.h"
+#include "lldb/Target/TraceSettingsParser.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+/// \class Trace Trace.h "lldb/Target/Trace.h"
+/// A plug-in interface definition class for trace information.
+///
+/// Trace plug-ins allow processor trace information to be loaded into LLDB so
+/// that the data can be dumped, used for reverse and forward stepping to allow
+/// introspection into the reason your process crashed or found its way to its
+/// current state.
+///
+/// Trace information can be loaded into a target without a process to allow
+/// introspection of the trace information during post mortem analysis, such as
+/// when loading core files.
+///
+/// Processor trace information can also be fetched through the process
+/// interfaces during a live debug session if your process supports gathering
+/// this information.
+class Trace : public PluginInterface {
+public:
+  ~Trace() override = default;
+
+  /// Dump the trace data that this plug-in has access to.
+  ///
+  /// This function will dump all of the trace data for all threads in a user
+  /// readable format. Options for dumping can be added as this API is iterated
+  /// on.
+  ///
+  /// \param[in] s
+  ///     A stream object to dump the information to.
+  virtual void Dump(Stream *s) const = 0;
+
+  /// Find a trace plug-in using JSON data.
+  ///
+  /// When loading trace data from disk, the information for the trace data
+  /// can be contained in multiple files and require plug-in specific
+  /// information about the CPU. Using data like JSON provides an
+  /// easy way to specify all of the settings and information that we will need
+  /// to load trace data into LLDB. This structured data can include:
+  ///   - The plug-in name (this allows a specific plug-in to be selected)
+  ///   - Architecture or target triple
+  ///   - one or more paths to the trace data file on disk
+  ///     - core trace data
+  ///     - thread events or related information
+  ///   - shared library load information to use for this trace data that
+  ///     allows a target to be created so the trace information can be
+  ///     symbolicated so that the trace information can be displayed to the
+  ///     user
+  ///     - shared library path
+  ///     - load address
+  ///     - information on how to fetch the shared library
+  ///       - path to locally cached file on disk
+  ///       - URL to download the file
+  ///   - Any information needed to load the trace file
+  ///     - CPU information
+  ///     - Custom plug-in information needed to decode the trace information
+  ///       correctly.
+  ///
+  /// \param[in] debugger
+  ///     The debugger instance were new Target will be created as part of the
+  ///     JSON data parsing.
+  ///
+  /// \param[in] settings
+  ///     JSON object describing a trace.
+  ///
+  /// \param[in] settings_dir
+  ///     Path to a directory used to resolve relative paths in the JSON data.
+  ///     If the JSON data is defined in a file, this should be the
+  ///     folder containing it.
+  static llvm::Expected<lldb::TraceSP>
+  FindPlugin(Debugger &debugger, const llvm::json::Value &settings,
+             llvm::StringRef settings_dir);
+
+  /// Create an instance of trace plug-in by name.
+  ///
+  /// \param[in] plugin_name
+  ///     Name of the trace plugin.
+  static llvm::Expected<lldb::TraceSP> FindPlugin(llvm::StringRef plugin_name);
+
+  /// Parse the JSON settings and create the corresponding \a Target
+  /// objects. In case of an error, no targets are created.
+  ///
+  /// \param[in] debugger
+  ///   The debugger instance where the targets are created.
+  ///
+  /// \param[in] settings
+  ///     JSON object describing a trace.
+  ///
+  /// \param[in] settings_dir
+  ///     Path to a directory used to resolve relative paths in the JSON data.
+  ///     If the JSON data is defined in a file, this should be the
+  ///     folder containing it.
+  ///
+  /// \return
+  ///   An error object containing the reason if there is a failure.
+  llvm::Error ParseSettings(Debugger &debugger,
+                            const llvm::json::Object &settings,
+                            llvm::StringRef settings_dir);
+
+  /// Get the JSON schema of the settings for the trace plug-in.
+  llvm::StringRef GetSchema();
+
+protected:
+  Trace() {}
+
+  /// The actual plug-in should define its own implementation of \a
+  /// TraceSettingsParser for doing any custom parsing.
+  virtual std::unique_ptr<lldb_private::TraceSettingsParser> CreateParser() = 
0;
+
+private:
+  Trace(const Trace &) = delete;
+  const Trace &operator=(const Trace &) = delete;
+
+protected:
+  friend class TraceSettingsParser;
+  /// JSON object that holds all settings for this trace session.
+  llvm::json::Object m_settings;
+  /// The directory that contains the settings file.
+  std::string m_settings_dir;
+
+  std::map<lldb::pid_t, std::map<lldb::tid_t, lldb_private::FileSpec>>
+      m_thread_to_trace_file_map;
+  std::vector<lldb::TargetSP> m_targets;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_TARGET_TRACE_H

diff  --git a/lldb/include/lldb/Target/TraceSettingsParser.h 
b/lldb/include/lldb/Target/TraceSettingsParser.h
new file mode 100644
index 000000000000..bc18c107ed83
--- /dev/null
+++ b/lldb/include/lldb/Target/TraceSettingsParser.h
@@ -0,0 +1,136 @@
+//===-- TraceSettingsParser.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_TRACE_SETTINGS_PARSER_H
+#define LLDB_TARGET_TRACE_SETTINGS_PARSER_H
+
+#include "llvm/ADT/Optional.h"
+
+#include "lldb/Target/Trace.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+/// \class TraceSettingsParser TraceSettingsParser.h
+/// A plug-in interface definition class for parsing \a Trace settings.
+///
+/// As \a Trace plug-ins support plug-in specific settings, this class should 
be
+/// overriden and implement the plug-in specific parsing logic.
+class TraceSettingsParser {
+public:
+  TraceSettingsParser(Trace &trace) : m_trace(trace) {}
+
+  virtual ~TraceSettingsParser() = default;
+
+  /// Get the JSON schema of the settings for the trace plug-in.
+  llvm::StringRef GetSchema();
+
+  /// Parse the structured data settings and create the corresponding \a Target
+  /// objects. In case of and error, no targets are created.
+  ///
+  /// \param[in] debugger
+  ///   The debugger instance where the targets are created.
+  ///
+  /// \param[in] settings
+  ///   The settings to parse.
+  ///
+  /// \param[in] settings_dir
+  ///   The directory that contains the settings file used to resolve relative
+  ///   paths.
+  ///
+  /// \return
+  ///   An error object containing the reason if there is a failure.
+  llvm::Error ParseSettings(Debugger &debugger,
+                            const llvm::json::Object &settings,
+                            llvm::StringRef settings_dir);
+
+protected:
+  /// Method that should be overriden by implementations of this class to
+  /// provide the specific plug-in schema inside the "trace" section of the
+  /// global schema.
+  virtual llvm::StringRef GetPluginSchema() = 0;
+
+  /// Method that should be overriden to parse the plug-in specific settings.
+  ///
+  /// \return
+  ///   An error object containing the reason if there is a failure.
+  virtual llvm::Error ParsePluginSettings() = 0;
+
+private:
+  /// Resolve non-absolute paths relativejto the settings folder
+  void NormalizePath(lldb_private::FileSpec &file_spec);
+  llvm::Error ParseProcess(lldb_private::Debugger &debugger,
+                           const llvm::json::Object &process);
+  llvm::Error ParseProcesses(lldb_private::Debugger &debugger);
+  llvm::Error ParseThread(lldb::ProcessSP &process_sp,
+                          const llvm::json::Object &thread);
+  llvm::Error ParseThreads(lldb::ProcessSP &process_sp,
+                           const llvm::json::Object &process);
+  llvm::Error ParseModule(lldb::TargetSP &target_sp,
+                          const llvm::json::Object &module);
+  llvm::Error ParseModules(lldb::TargetSP &target_sp,
+                           const llvm::json::Object &process);
+  llvm::Error ParseSettingsImpl(lldb_private::Debugger &debugger);
+
+  Trace &m_trace;
+
+protected:
+  /// Objects created as product of the parsing
+  /// \{
+  /// JSON object that holds all settings for this trace session.
+  llvm::json::Object m_settings;
+  /// The directory that contains the settings file.
+  std::string m_settings_dir;
+
+  std::map<lldb::pid_t, std::map<lldb::tid_t, lldb_private::FileSpec>>
+      m_thread_to_trace_file_map;
+  std::vector<lldb::TargetSP> m_targets;
+  /// \}
+};
+
+} // namespace lldb_private
+
+namespace json_helpers {
+/// JSON parsing helpers based on \a llvm::Expected.
+/// \{
+llvm::Error CreateWrongTypeError(const llvm::json::Value &value,
+                                 llvm::StringRef type);
+
+llvm::Expected<int64_t> ToIntegerOrError(const llvm::json::Value &value);
+
+llvm::Expected<llvm::StringRef> ToStringOrError(const llvm::json::Value 
&value);
+
+llvm::Expected<const llvm::json::Array &>
+ToArrayOrError(const llvm::json::Value &value);
+
+llvm::Expected<const llvm::json::Object &>
+ToObjectOrError(const llvm::json::Value &value);
+
+llvm::Error CreateMissingKeyError(llvm::json::Object obj, llvm::StringRef key);
+
+llvm::Expected<const llvm::json::Value &>
+GetValueOrError(const llvm::json::Object &obj, llvm::StringRef key);
+
+llvm::Expected<int64_t> GetIntegerOrError(const llvm::json::Object &obj,
+                                          llvm::StringRef key);
+
+llvm::Expected<llvm::StringRef> GetStringOrError(const llvm::json::Object &obj,
+                                                 llvm::StringRef key);
+
+llvm::Expected<const llvm::json::Array &>
+GetArrayOrError(const llvm::json::Object &obj, llvm::StringRef key);
+
+llvm::Expected<const llvm::json::Object &>
+GetObjectOrError(const llvm::json::Object &obj, llvm::StringRef key);
+
+llvm::Expected<llvm::Optional<llvm::StringRef>>
+GetOptionalStringOrError(const llvm::json::Object &obj, llvm::StringRef key);
+/// \}
+} // namespace json_helpers
+
+#endif // LLDB_TARGET_TRACE_SETTINGS_PARSER_H

diff  --git a/lldb/include/lldb/lldb-forward.h 
b/lldb/include/lldb/lldb-forward.h
index 6682413820d6..0a7f08c95571 100644
--- a/lldb/include/lldb/lldb-forward.h
+++ b/lldb/include/lldb/lldb-forward.h
@@ -226,6 +226,8 @@ class ThreadPlanStepRange;
 class ThreadPlanStepThrough;
 class ThreadPlanTracer;
 class ThreadSpec;
+class Trace;
+class TraceSettingsParser;
 class TraceOptions;
 class Type;
 class TypeAndOrName;
@@ -432,6 +434,7 @@ typedef std::shared_ptr<lldb_private::ThreadCollection> 
ThreadCollectionSP;
 typedef std::shared_ptr<lldb_private::ThreadPlan> ThreadPlanSP;
 typedef std::weak_ptr<lldb_private::ThreadPlan> ThreadPlanWP;
 typedef std::shared_ptr<lldb_private::ThreadPlanTracer> ThreadPlanTracerSP;
+typedef std::shared_ptr<lldb_private::Trace> TraceSP;
 typedef std::shared_ptr<lldb_private::TraceOptions> TraceOptionsSP;
 typedef std::shared_ptr<lldb_private::Type> TypeSP;
 typedef std::weak_ptr<lldb_private::Type> TypeWP;

diff  --git a/lldb/include/lldb/lldb-private-interfaces.h 
b/lldb/include/lldb/lldb-private-interfaces.h
index 1568e7a3cb51..9b47b73b772c 100644
--- a/lldb/include/lldb/lldb-private-interfaces.h
+++ b/lldb/include/lldb/lldb-private-interfaces.h
@@ -18,6 +18,12 @@
 #include <memory>
 #include <set>
 
+namespace llvm {
+namespace json {
+class Object;
+}
+} // namespace llvm
+
 namespace lldb_private {
 typedef lldb::ABISP (*ABICreateInstance)(lldb::ProcessSP process_sp,
                                          const ArchSpec &arch);
@@ -104,6 +110,7 @@ typedef lldb::REPLSP (*REPLCreateInstance)(Status &error,
                                            const char *repl_options);
 typedef int (*ComparisonFunction)(const void *, const void *);
 typedef void (*DebuggerInitializeCallback)(Debugger &debugger);
+typedef lldb::TraceSP (*TraceCreateInstance)();
 
 } // namespace lldb_private
 

diff  --git a/lldb/source/Commands/CMakeLists.txt 
b/lldb/source/Commands/CMakeLists.txt
index 3e57670fd040..e29a2414d8ff 100644
--- a/lldb/source/Commands/CMakeLists.txt
+++ b/lldb/source/Commands/CMakeLists.txt
@@ -31,6 +31,7 @@ add_lldb_library(lldbCommands
   CommandObjectStats.cpp
   CommandObjectTarget.cpp
   CommandObjectThread.cpp
+  CommandObjectTrace.cpp
   CommandObjectType.cpp
   CommandObjectVersion.cpp
   CommandObjectWatchpoint.cpp

diff  --git a/lldb/source/Commands/CommandObjectTrace.cpp 
b/lldb/source/Commands/CommandObjectTrace.cpp
new file mode 100644
index 000000000000..c622914e0d79
--- /dev/null
+++ b/lldb/source/Commands/CommandObjectTrace.cpp
@@ -0,0 +1,292 @@
+//===-- CommandObjectTrace.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 "CommandObjectTrace.h"
+
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/MemoryBuffer.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionGroupFormat.h"
+#include "lldb/Interpreter/OptionValueBoolean.h"
+#include "lldb/Interpreter/OptionValueLanguage.h"
+#include "lldb/Interpreter/OptionValueString.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Target/Trace.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace llvm;
+
+// CommandObjectTraceLoad
+#define LLDB_OPTIONS_trace_load
+#include "CommandOptions.inc"
+
+#pragma mark CommandObjectTraceLoad
+
+class CommandObjectTraceLoad : public CommandObjectParsed {
+public:
+  class CommandOptions : public Options {
+  public:
+    CommandOptions() : Options() { OptionParsingStarting(nullptr); }
+
+    ~CommandOptions() override = default;
+
+    Status SetOptionValue(uint32_t option_idx, StringRef option_arg,
+                          ExecutionContext *execution_context) override {
+      Status error;
+      const int short_option = m_getopt_table[option_idx].val;
+
+      switch (short_option) {
+      case 'v': {
+        m_verbose = true;
+        break;
+      }
+      default:
+        llvm_unreachable("Unimplemented option");
+      }
+      return error;
+    }
+
+    void OptionParsingStarting(ExecutionContext *execution_context) override {
+      m_verbose = false;
+    }
+
+    ArrayRef<OptionDefinition> GetDefinitions() override {
+      return makeArrayRef(g_trace_load_options);
+    }
+
+    bool m_verbose; // Enable verbose logging for debugging purposes.
+  };
+
+  CommandObjectTraceLoad(CommandInterpreter &interpreter)
+      : CommandObjectParsed(interpreter, "trace load",
+                            "Load processor trace data from a JSON file.",
+                            "trace load"),
+        m_options() {}
+
+  ~CommandObjectTraceLoad() override = default;
+
+  Options *GetOptions() override { return &m_options; }
+
+protected:
+  bool DoExecute(Args &command, CommandReturnObject &result) override {
+    if (command.size() != 1) {
+      result.AppendError("a single path to a JSON file containing trace "
+                         "information is required");
+      result.SetStatus(eReturnStatusFailed);
+      return false;
+    }
+
+    auto end_with_failure = [&result](llvm::Error err) -> bool {
+      result.AppendErrorWithFormat("%s\n",
+                                   llvm::toString(std::move(err)).c_str());
+      result.SetStatus(eReturnStatusFailed);
+      return false;
+    };
+
+    FileSpec json_file(command[0].ref());
+
+    auto buffer_or_error = llvm::MemoryBuffer::getFile(json_file.GetPath());
+    if (!buffer_or_error) {
+      return end_with_failure(llvm::createStringError(
+          std::errc::invalid_argument, "could not open input file: %s - %s.",
+          json_file.GetPath().c_str(),
+          buffer_or_error.getError().message().c_str()));
+    }
+
+    llvm::Expected<json::Value> settings =
+        json::parse(buffer_or_error.get()->getBuffer().str());
+    if (!settings)
+      return end_with_failure(settings.takeError());
+
+    if (Expected<lldb::TraceSP> traceOrErr = Trace::FindPlugin(
+            GetDebugger(), *settings, json_file.GetDirectory().AsCString())) {
+      lldb::TraceSP trace_sp = traceOrErr.get();
+      if (m_options.m_verbose)
+        result.AppendMessageWithFormat("loading trace with plugin %s\n",
+                                       trace_sp->GetPluginName().AsCString());
+    } else
+      return end_with_failure(traceOrErr.takeError());
+
+    result.SetStatus(eReturnStatusSuccessFinishResult);
+    return true;
+  }
+
+  CommandOptions m_options;
+};
+
+// CommandObjectTraceDump
+#define LLDB_OPTIONS_trace_dump
+#include "CommandOptions.inc"
+
+#pragma mark CommandObjectTraceDump
+
+class CommandObjectTraceDump : public CommandObjectParsed {
+public:
+  class CommandOptions : public Options {
+  public:
+    CommandOptions() : Options() { OptionParsingStarting(nullptr); }
+
+    ~CommandOptions() override = default;
+
+    Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+                          ExecutionContext *execution_context) override {
+      Status error;
+      const int short_option = m_getopt_table[option_idx].val;
+
+      switch (short_option) {
+      case 'v': {
+        m_verbose = true;
+        break;
+      }
+      default:
+        llvm_unreachable("Unimplemented option");
+      }
+      return error;
+    }
+
+    void OptionParsingStarting(ExecutionContext *execution_context) override {
+      m_verbose = false;
+    }
+
+    llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+      return llvm::makeArrayRef(g_trace_dump_options);
+    }
+
+    bool m_verbose; // Enable verbose logging for debugging purposes.
+  };
+
+  CommandObjectTraceDump(CommandInterpreter &interpreter)
+      : CommandObjectParsed(interpreter, "trace dump",
+                            "Dump the loaded processor trace data.",
+                            "trace dump"),
+        m_options() {}
+
+  ~CommandObjectTraceDump() override = default;
+
+  Options *GetOptions() override { return &m_options; }
+
+protected:
+  bool DoExecute(Args &command, CommandReturnObject &result) override {
+    Status error;
+    // TODO: fill in the dumping code here!
+    if (error.Success()) {
+      result.SetStatus(eReturnStatusSuccessFinishResult);
+    } else {
+      result.AppendErrorWithFormat("%s\n", error.AsCString());
+      result.SetStatus(eReturnStatusFailed);
+    }
+    return result.Succeeded();
+  }
+
+  CommandOptions m_options;
+};
+
+// CommandObjectTraceSchema
+#define LLDB_OPTIONS_trace_schema
+#include "CommandOptions.inc"
+
+#pragma mark CommandObjectTraceSchema
+
+class CommandObjectTraceSchema : public CommandObjectParsed {
+public:
+  class CommandOptions : public Options {
+  public:
+    CommandOptions() : Options() { OptionParsingStarting(nullptr); }
+
+    ~CommandOptions() override = default;
+
+    Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+                          ExecutionContext *execution_context) override {
+      Status error;
+      const int short_option = m_getopt_table[option_idx].val;
+
+      switch (short_option) {
+      case 'v': {
+        m_verbose = true;
+        break;
+      }
+      default:
+        llvm_unreachable("Unimplemented option");
+      }
+      return error;
+    }
+
+    void OptionParsingStarting(ExecutionContext *execution_context) override {
+      m_verbose = false;
+    }
+
+    llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+      return llvm::makeArrayRef(g_trace_schema_options);
+    }
+
+    bool m_verbose; // Enable verbose logging for debugging purposes.
+  };
+
+  CommandObjectTraceSchema(CommandInterpreter &interpreter)
+      : CommandObjectParsed(interpreter, "trace schema",
+                            "Show the schema of the given trace plugin.",
+                            "trace schema <plug-in>"),
+        m_options() {}
+
+  ~CommandObjectTraceSchema() override = default;
+
+  Options *GetOptions() override { return &m_options; }
+
+protected:
+  bool DoExecute(Args &command, CommandReturnObject &result) override {
+    Status error;
+    if (command.empty()) {
+      result.SetError(
+          "trace schema cannot be invoked without a plug-in as argument");
+      return false;
+    }
+
+    StringRef plugin_name(command[0].c_str());
+
+    if (Expected<lldb::TraceSP> traceOrErr = Trace::FindPlugin(plugin_name)) {
+      lldb::TraceSP trace_sp = traceOrErr.get();
+      result.AppendMessage(trace_sp->GetSchema());
+    } else {
+      error.SetErrorString(llvm::toString(traceOrErr.takeError()));
+    }
+
+    if (error.Success()) {
+      result.SetStatus(eReturnStatusSuccessFinishResult);
+    } else {
+      result.AppendErrorWithFormat("%s\n", error.AsCString());
+      result.SetStatus(eReturnStatusFailed);
+    }
+    return result.Succeeded();
+  }
+
+  CommandOptions m_options;
+};
+
+// CommandObjectTrace
+
+CommandObjectTrace::CommandObjectTrace(CommandInterpreter &interpreter)
+    : CommandObjectMultiword(interpreter, "trace",
+                             "Commands for loading and using processor "
+                             "trace information.",
+                             "trace [<sub-command-options>]") {
+  LoadSubCommand("load",
+                 CommandObjectSP(new CommandObjectTraceLoad(interpreter)));
+  LoadSubCommand("dump",
+                 CommandObjectSP(new CommandObjectTraceDump(interpreter)));
+  LoadSubCommand("schema",
+                 CommandObjectSP(new CommandObjectTraceSchema(interpreter)));
+}
+
+CommandObjectTrace::~CommandObjectTrace() = default;

diff  --git a/lldb/source/Commands/CommandObjectTrace.h 
b/lldb/source/Commands/CommandObjectTrace.h
new file mode 100644
index 000000000000..2dca0e26b243
--- /dev/null
+++ b/lldb/source/Commands/CommandObjectTrace.h
@@ -0,0 +1,25 @@
+//===-- CommandObjectTrace.h ------------------------------------*- C++ 
-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTTRACE_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTTRACE_H
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+class CommandObjectTrace : public CommandObjectMultiword {
+public:
+  CommandObjectTrace(CommandInterpreter &interpreter);
+
+  ~CommandObjectTrace() override;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTTRACE_H

diff  --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td
index b41b1871ad81..f2401dc5f326 100644
--- a/lldb/source/Commands/Options.td
+++ b/lldb/source/Commands/Options.td
@@ -1175,3 +1175,19 @@ let Command = "watchpoint delete" in {
   def watchpoint_delete_force : Option<"force", "f">, Group<1>,
     Desc<"Delete all watchpoints without querying for confirmation.">;
 }
+
+let Command = "trace load" in {
+  def trace_load_verbose : Option<"verbose", "v">, Group<1>,
+    Desc<"Show verbose trace load logging for debugging the plug-in "
+    "implementation.">;
+}
+
+let Command = "trace dump" in {
+  def trace_dump_verbose : Option<"verbose", "v">, Group<1>,
+    Desc<"Show verbose trace information.">;
+}
+
+let Command = "trace schema" in {
+  def trace_schema_verbose : Option<"verbose", "v">, Group<1>,
+    Desc<"Show verbose trace schema logging for debugging the plug-in.">;
+}

diff  --git a/lldb/source/Core/PluginManager.cpp 
b/lldb/source/Core/PluginManager.cpp
index 3545ef66cc38..e025aa1d3cdb 100644
--- a/lldb/source/Core/PluginManager.cpp
+++ b/lldb/source/Core/PluginManager.cpp
@@ -1005,6 +1005,30 @@ 
PluginManager::GetSymbolVendorCreateCallbackAtIndex(uint32_t idx) {
   return GetSymbolVendorInstances().GetCallbackAtIndex(idx);
 }
 
+#pragma mark Trace
+
+typedef PluginInstance<TraceCreateInstance> TraceInstance;
+typedef PluginInstances<TraceInstance> TraceInstances;
+
+static TraceInstances &GetTraceInstances() {
+  static TraceInstances g_instances;
+  return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(ConstString name, const char *description,
+                                   TraceCreateInstance create_callback) {
+  return GetTraceInstances().RegisterPlugin(name, description, 
create_callback);
+}
+
+bool PluginManager::UnregisterPlugin(TraceCreateInstance create_callback) {
+  return GetTraceInstances().UnregisterPlugin(create_callback);
+}
+
+TraceCreateInstance
+PluginManager::GetTraceCreateCallback(ConstString plugin_name) {
+  return GetTraceInstances().GetCallbackForName(plugin_name);
+}
+
 #pragma mark UnwindAssembly
 
 typedef PluginInstance<UnwindAssemblyCreateInstance> UnwindAssemblyInstance;

diff  --git a/lldb/source/Interpreter/CommandInterpreter.cpp 
b/lldb/source/Interpreter/CommandInterpreter.cpp
index 1f6746800097..88b07d5e3a0a 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -37,6 +37,7 @@
 #include "Commands/CommandObjectStats.h"
 #include "Commands/CommandObjectTarget.h"
 #include "Commands/CommandObjectThread.h"
+#include "Commands/CommandObjectTrace.h"
 #include "Commands/CommandObjectType.h"
 #include "Commands/CommandObjectVersion.h"
 #include "Commands/CommandObjectWatchpoint.h"
@@ -512,6 +513,7 @@ void CommandInterpreter::LoadCommandDictionary() {
   REGISTER_COMMAND_OBJECT("statistics", CommandObjectStats);
   REGISTER_COMMAND_OBJECT("target", CommandObjectMultiwordTarget);
   REGISTER_COMMAND_OBJECT("thread", CommandObjectMultiwordThread);
+  REGISTER_COMMAND_OBJECT("trace", CommandObjectTrace);
   REGISTER_COMMAND_OBJECT("type", CommandObjectType);
   REGISTER_COMMAND_OBJECT("version", CommandObjectVersion);
   REGISTER_COMMAND_OBJECT("watchpoint", CommandObjectMultiwordWatchpoint);

diff  --git a/lldb/source/Plugins/CMakeLists.txt 
b/lldb/source/Plugins/CMakeLists.txt
index d91ba749f86c..3da23eca0773 100644
--- a/lldb/source/Plugins/CMakeLists.txt
+++ b/lldb/source/Plugins/CMakeLists.txt
@@ -19,6 +19,7 @@ add_subdirectory(StructuredData)
 add_subdirectory(SymbolFile)
 add_subdirectory(SystemRuntime)
 add_subdirectory(SymbolVendor)
+add_subdirectory(Trace)
 add_subdirectory(TypeSystem)
 add_subdirectory(UnwindAssembly)
 

diff  --git a/lldb/source/Plugins/Trace/CMakeLists.txt 
b/lldb/source/Plugins/Trace/CMakeLists.txt
new file mode 100644
index 000000000000..edbb5f14b4e6
--- /dev/null
+++ b/lldb/source/Plugins/Trace/CMakeLists.txt
@@ -0,0 +1,5 @@
+option(LLDB_BUILD_INTEL_PT "Enable Building of Intel(R) Processor Trace Tool" 
OFF)
+
+if (LLDB_BUILD_INTEL_PT)
+  add_subdirectory(intel-pt)
+endif()

diff  --git a/lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt 
b/lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt
new file mode 100644
index 000000000000..aeaba0cdeb20
--- /dev/null
+++ b/lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt
@@ -0,0 +1,23 @@
+if (NOT LIBIPT_INCLUDE_PATH)
+  message (FATAL_ERROR "libipt include path not provided")
+endif()
+
+if (NOT EXISTS "${LIBIPT_INCLUDE_PATH}")
+  message (FATAL_ERROR "invalid libipt include path provided")
+endif()
+include_directories(${LIBIPT_INCLUDE_PATH})
+
+find_library(LIBIPT_LIBRARY ipt PATHS ${LIBIPT_LIBRARY_PATH} REQUIRED)
+
+add_lldb_library(lldbPluginTraceIntelPT PLUGIN
+  TraceIntelPT.cpp
+  TraceIntelPTSettingsParser.cpp
+
+  LINK_LIBS
+    lldbCore
+    lldbSymbol
+    lldbTarget
+    ${LIBIPT_LIBRARY}
+  LINK_COMPONENTS
+    Support
+  )

diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp 
b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
new file mode 100644
index 000000000000..13a990470626
--- /dev/null
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
@@ -0,0 +1,51 @@
+//===-- TraceIntelPT.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 "TraceIntelPT.h"
+
+#include "TraceIntelPTSettingsParser.h"
+#include "lldb/Core/PluginManager.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace llvm;
+
+LLDB_PLUGIN_DEFINE_ADV(TraceIntelPT, TraceIntelPT)
+
+void TraceIntelPT::Initialize() {
+  PluginManager::RegisterPlugin(GetPluginNameStatic(), "Intel Processor Trace",
+                                CreateInstance);
+}
+
+void TraceIntelPT::Terminate() {
+  PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+ConstString TraceIntelPT::GetPluginNameStatic() {
+  static ConstString g_name("intel-pt");
+  return g_name;
+}
+
+std::unique_ptr<lldb_private::TraceSettingsParser>
+TraceIntelPT::CreateParser() {
+  return std::make_unique<TraceIntelPTSettingsParser>(*this);
+}
+
+//------------------------------------------------------------------
+// PluginInterface protocol
+//------------------------------------------------------------------
+
+ConstString TraceIntelPT::GetPluginName() { return GetPluginNameStatic(); }
+
+uint32_t TraceIntelPT::GetPluginVersion() { return 1; }
+
+void TraceIntelPT::Dump(lldb_private::Stream *s) const {}
+
+lldb::TraceSP TraceIntelPT::CreateInstance() {
+  return lldb::TraceSP(new TraceIntelPT());
+}

diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h 
b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
new file mode 100644
index 000000000000..edc781e08ad4
--- /dev/null
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
@@ -0,0 +1,48 @@
+//===-- TraceIntelPT.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 liblldb_TraceIntelPT_h_
+#define liblldb_TraceIntelPT_h_
+
+#include "intel-pt.h"
+#include "llvm/ADT/Optional.h"
+
+#include "TraceIntelPTSettingsParser.h"
+#include "lldb/Target/Trace.h"
+#include "lldb/lldb-private.h"
+
+class TraceIntelPT : public lldb_private::Trace {
+public:
+  void Dump(lldb_private::Stream *s) const override;
+
+  /// PluginInterface protocol
+  /// \{
+  lldb_private::ConstString GetPluginName() override;
+
+  static void Initialize();
+
+  static void Terminate();
+
+  static lldb::TraceSP CreateInstance();
+
+  static lldb_private::ConstString GetPluginNameStatic();
+
+  uint32_t GetPluginVersion() override;
+  /// \}
+
+protected:
+  TraceIntelPT() : Trace() {}
+
+  std::unique_ptr<lldb_private::TraceSettingsParser> CreateParser() override;
+
+private:
+  friend class TraceIntelPTSettingsParser;
+  pt_cpu m_pt_cpu;
+};
+
+#endif // liblldb_TraceIntelPT_h_

diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSettingsParser.cpp 
b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSettingsParser.cpp
new file mode 100644
index 000000000000..c8f90c9a978d
--- /dev/null
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSettingsParser.cpp
@@ -0,0 +1,69 @@
+//===-- TraceIntelPTSettingsParser.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 "TraceIntelPTSettingsParser.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace llvm;
+
+StringRef TraceIntelPTSettingsParser::GetPluginSchema() {
+  return R"({
+  "type": "intel-pt",
+  "pt_cpu": {
+    "vendor": "intel" | "unknown",
+    "family": integer,
+    "model": integer,
+    "stepping": integer
+  }
+})";
+}
+
+llvm::Error TraceIntelPTSettingsParser::ParsePTCPU(const json::Object &trace) {
+  llvm::Expected<const json::Object &> pt_cpu =
+      json_helpers::GetObjectOrError(trace, "pt_cpu");
+  if (!pt_cpu)
+    return pt_cpu.takeError();
+
+  llvm::Expected<llvm::StringRef> vendor =
+      json_helpers::GetStringOrError(*pt_cpu, "vendor");
+  if (!vendor)
+    return vendor.takeError();
+
+  llvm::Expected<int64_t> family =
+      json_helpers::GetIntegerOrError(*pt_cpu, "family");
+  if (!family)
+    return family.takeError();
+
+  llvm::Expected<int64_t> model =
+      json_helpers::GetIntegerOrError(*pt_cpu, "model");
+  if (!model)
+    return model.takeError();
+
+  llvm::Expected<int64_t> stepping =
+      json_helpers::GetIntegerOrError(*pt_cpu, "stepping");
+  if (!stepping)
+    return stepping.takeError();
+
+  m_pt_cpu = {vendor->compare("intel") == 0 ? pcv_intel : pcv_unknown,
+              static_cast<uint16_t>(*family), static_cast<uint8_t>(*model),
+              static_cast<uint8_t>(*stepping)};
+  return llvm::Error::success();
+}
+
+llvm::Error TraceIntelPTSettingsParser::ParsePluginSettings() {
+  llvm::Expected<const json::Object &> trace =
+      json_helpers::GetObjectOrError(m_settings, "trace");
+  if (!trace)
+    return trace.takeError();
+  if (llvm::Error err = ParsePTCPU(*trace))
+    return err;
+
+  m_trace.m_pt_cpu = m_pt_cpu;
+  return llvm::Error::success();
+}

diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSettingsParser.h 
b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSettingsParser.h
new file mode 100644
index 000000000000..b8aaa106b10d
--- /dev/null
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSettingsParser.h
@@ -0,0 +1,39 @@
+//===-- TraceIntelPTSettingsParser.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 liblldb_TraceIntelPTSettingsParser_h_
+#define liblldb_TraceIntelPTSettingsParser_h_
+
+#include "intel-pt.h"
+
+#include "TraceIntelPT.h"
+#include "lldb/Target/TraceSettingsParser.h"
+#include "lldb/Utility/StructuredData.h"
+
+class TraceIntelPT;
+
+class TraceIntelPTSettingsParser : public lldb_private::TraceSettingsParser {
+
+public:
+  TraceIntelPTSettingsParser(TraceIntelPT &trace)
+      : lldb_private::TraceSettingsParser((lldb_private::Trace &)trace),
+        m_trace(trace) {}
+
+protected:
+  llvm::StringRef GetPluginSchema() override;
+
+  llvm::Error ParsePluginSettings() override;
+
+private:
+  llvm::Error ParsePTCPU(const llvm::json::Object &trace);
+
+  TraceIntelPT &m_trace;
+  pt_cpu m_pt_cpu;
+};
+
+#endif // liblldb_TraceIntelPTSettingsParser_h_

diff  --git a/lldb/source/Target/CMakeLists.txt 
b/lldb/source/Target/CMakeLists.txt
index ca80b5b64778..c3a09c6c0098 100644
--- a/lldb/source/Target/CMakeLists.txt
+++ b/lldb/source/Target/CMakeLists.txt
@@ -65,6 +65,8 @@ add_lldb_library(lldbTarget
   ThreadPlanTracer.cpp
   ThreadPlanStack.cpp
   ThreadSpec.cpp
+  Trace.cpp
+  TraceSettingsParser.cpp
   UnixSignals.cpp
   UnwindAssembly.cpp
   UnwindLLDB.cpp

diff  --git a/lldb/source/Target/Trace.cpp b/lldb/source/Target/Trace.cpp
new file mode 100644
index 000000000000..6a1f8eadee1b
--- /dev/null
+++ b/lldb/source/Target/Trace.cpp
@@ -0,0 +1,78 @@
+//===-- Trace.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 "lldb/Target/Trace.h"
+
+#include <regex>
+#include <sstream>
+
+#include "llvm/Support/Format.h"
+
+#include "lldb/Core/PluginManager.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace llvm;
+
+llvm::Expected<lldb::TraceSP> Trace::FindPlugin(Debugger &debugger,
+                                                const json::Value &settings,
+                                                StringRef info_dir) {
+  llvm::Expected<const json::Object &> settings_obj =
+      json_helpers::ToObjectOrError(settings);
+  if (!settings_obj)
+    return settings_obj.takeError();
+
+  llvm::Expected<const json::Object &> trace =
+      json_helpers::GetObjectOrError(*settings_obj, "trace");
+  if (!trace)
+    return trace.takeError();
+
+  llvm::Expected<StringRef> type =
+      json_helpers::GetStringOrError(*trace, "type");
+  if (!type)
+    return type.takeError();
+
+  ConstString plugin_name(*type);
+  auto create_callback = PluginManager::GetTraceCreateCallback(plugin_name);
+  if (create_callback) {
+    TraceSP instance = create_callback();
+    if (llvm::Error err =
+            instance->ParseSettings(debugger, *settings_obj, info_dir))
+      return std::move(err);
+    return instance;
+  }
+
+  return createStringError(
+      std::errc::invalid_argument,
+      "no trace plug-in matches the specified type: \"%s\"",
+      plugin_name.AsCString());
+}
+
+llvm::Expected<lldb::TraceSP> Trace::FindPlugin(StringRef name) {
+  ConstString plugin_name(name);
+  auto create_callback = PluginManager::GetTraceCreateCallback(plugin_name);
+  if (create_callback)
+    return create_callback();
+
+  return createStringError(
+      std::errc::invalid_argument,
+      "no trace plug-in matches the specified type: \"%s\"",
+      plugin_name.AsCString());
+}
+
+llvm::Error Trace::ParseSettings(Debugger &debugger,
+                                 const llvm::json::Object &settings,
+                                 llvm::StringRef settings_dir) {
+  if (llvm::Error err =
+          CreateParser()->ParseSettings(debugger, settings, settings_dir))
+    return err;
+
+  return llvm::Error::success();
+}
+
+llvm::StringRef Trace::GetSchema() { return CreateParser()->GetSchema(); }

diff  --git a/lldb/source/Target/TraceSettingsParser.cpp 
b/lldb/source/Target/TraceSettingsParser.cpp
new file mode 100644
index 000000000000..09e60bb35a4a
--- /dev/null
+++ b/lldb/source/Target/TraceSettingsParser.cpp
@@ -0,0 +1,351 @@
+//===-- TraceSettingParser.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 "lldb/Target/TraceSettingsParser.h"
+
+#include <regex>
+
+#include "Plugins/Process/Utility/HistoryThread.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Target/Process.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace llvm;
+
+namespace json_helpers {
+
+llvm::Error CreateWrongTypeError(const json::Value &value,
+                                 llvm::StringRef type) {
+  std::string s;
+  llvm::raw_string_ostream os(s);
+  os << llvm::formatv("JSON value is expected to be \"{0}\".\nValue:\n{1:2}",
+                      type, value);
+  os.flush();
+
+  return llvm::createStringError(std::errc::invalid_argument, 
os.str().c_str());
+}
+
+llvm::Expected<int64_t> ToIntegerOrError(const json::Value &value) {
+  llvm::Optional<int64_t> v = value.getAsInteger();
+  if (v.hasValue())
+    return *v;
+  return CreateWrongTypeError(value, "integer");
+}
+
+llvm::Expected<StringRef> ToStringOrError(const json::Value &value) {
+  llvm::Optional<StringRef> v = value.getAsString();
+  if (v.hasValue())
+    return *v;
+  return CreateWrongTypeError(value, "string");
+}
+
+llvm::Expected<const json::Array &> ToArrayOrError(const json::Value &value) {
+  if (const json::Array *v = value.getAsArray())
+    return *v;
+  return CreateWrongTypeError(value, "array");
+}
+
+llvm::Expected<const json::Object &> ToObjectOrError(const json::Value &value) 
{
+  if (const json::Object *v = value.getAsObject())
+    return *v;
+  return CreateWrongTypeError(value, "object");
+}
+
+llvm::Error CreateMissingKeyError(json::Object obj, llvm::StringRef key) {
+  std::string str;
+  llvm::raw_string_ostream os(str);
+  os << llvm::formatv(
+      "JSON object is missing the \"{0}\" field.\nValue:\n{1:2}", key,
+      json::Value(std::move(obj)));
+  os.flush();
+
+  return llvm::createStringError(std::errc::invalid_argument, 
os.str().c_str());
+}
+
+llvm::Expected<const json::Value &> GetValueOrError(const json::Object &obj,
+                                                    StringRef key) {
+  if (const json::Value *v = obj.get(key))
+    return *v;
+  else
+    return CreateMissingKeyError(obj, key);
+}
+
+llvm::Expected<int64_t> GetIntegerOrError(const json::Object &obj,
+                                          StringRef key) {
+  if (llvm::Expected<const json::Value &> v = GetValueOrError(obj, key))
+    return ToIntegerOrError(*v);
+  else
+    return v.takeError();
+}
+
+llvm::Expected<StringRef> GetStringOrError(const json::Object &obj,
+                                           StringRef key) {
+  if (llvm::Expected<const json::Value &> v = GetValueOrError(obj, key))
+    return ToStringOrError(*v);
+  else
+    return v.takeError();
+}
+
+llvm::Expected<const json::Array &> GetArrayOrError(const json::Object &obj,
+                                                    StringRef key) {
+  if (llvm::Expected<const json::Value &> v = GetValueOrError(obj, key))
+    return ToArrayOrError(*v);
+  else
+    return v.takeError();
+}
+
+llvm::Expected<const json::Object &> GetObjectOrError(const json::Object &obj,
+                                                      StringRef key) {
+  if (llvm::Expected<const json::Value &> v = GetValueOrError(obj, key))
+    return ToObjectOrError(*v);
+  else
+    return v.takeError();
+}
+
+llvm::Expected<llvm::Optional<StringRef>>
+GetOptionalStringOrError(const json::Object &obj, StringRef key) {
+  if (const json::Value *v = obj.get(key))
+    return ToStringOrError(*v);
+  return llvm::None;
+}
+
+} // namespace json_helpers
+
+StringRef TraceSettingsParser::GetSchema() {
+  static std::string schema;
+  if (schema.empty()) {
+    std::ostringstream schema_builder;
+    schema_builder << "{\n \"trace\": ";
+
+    // We need to add spaces to indent correctly the plugin schema
+    std::string plugin_schema(GetPluginSchema());
+    plugin_schema = std::regex_replace(plugin_schema, std::regex("\n"), "\n  
");
+    schema_builder << plugin_schema << ",\n";
+
+    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 settings file.)";
+    schema = schema_builder.str();
+  }
+  return schema;
+}
+
+void TraceSettingsParser::NormalizePath(FileSpec &file_spec) {
+  if (file_spec.IsRelative())
+    file_spec.PrependPathComponent(m_settings_dir);
+}
+
+llvm::Error TraceSettingsParser::ParseThread(ProcessSP &process_sp,
+                                             const json::Object &thread) {
+  llvm::Expected<lldb::tid_t> raw_tid =
+      json_helpers::GetIntegerOrError(thread, "tid");
+  if (!raw_tid)
+    return raw_tid.takeError();
+  lldb::tid_t tid = static_cast<lldb::tid_t>(*raw_tid);
+
+  if (llvm::Expected<StringRef> trace_file =
+          json_helpers::GetStringOrError(thread, "traceFile")) {
+    FileSpec spec(*trace_file);
+    NormalizePath(spec);
+    m_thread_to_trace_file_map[process_sp->GetID()][tid] = spec;
+  } else
+    return trace_file.takeError();
+
+  ThreadSP thread_sp(new HistoryThread(*process_sp, tid, /*callstack*/ {}));
+  process_sp->GetThreadList().AddThread(thread_sp);
+  return llvm::Error::success();
+}
+
+llvm::Error TraceSettingsParser::ParseThreads(ProcessSP &process_sp,
+                                              const json::Object &process) {
+  llvm::Expected<const json::Array &> threads =
+      json_helpers::GetArrayOrError(process, "threads");
+  if (!threads)
+    return threads.takeError();
+
+  for (const json::Value &thread_val : *threads) {
+    llvm::Expected<const json::Object &> thread =
+        json_helpers::ToObjectOrError(thread_val);
+    if (!thread)
+      return thread.takeError();
+    if (llvm::Error err = ParseThread(process_sp, *thread))
+      return err;
+  }
+  return llvm::Error::success();
+}
+
+static llvm::Expected<addr_t> ParseAddress(StringRef address_str) {
+  addr_t address;
+  if (address_str.getAsInteger(0, address))
+    return createStringError(std::errc::invalid_argument,
+                             "\"%s\" does not represent an integer",
+                             address_str.data());
+  return address;
+}
+
+llvm::Error TraceSettingsParser::ParseModule(TargetSP &target_sp,
+                                             const json::Object &module) {
+  llvm::Expected<StringRef> load_address_str =
+      json_helpers::GetStringOrError(module, "loadAddress");
+  if (!load_address_str)
+    return load_address_str.takeError();
+  llvm::Expected<addr_t> load_address = ParseAddress(*load_address_str);
+  if (!load_address)
+    return load_address.takeError();
+
+  llvm::Expected<StringRef> system_path =
+      json_helpers::GetStringOrError(module, "systemPath");
+  if (!system_path)
+    return system_path.takeError();
+  FileSpec system_file_spec(*system_path);
+  NormalizePath(system_file_spec);
+
+  llvm::Expected<llvm::Optional<StringRef>> file =
+      json_helpers::GetOptionalStringOrError(module, "file");
+  if (!file)
+    return file.takeError();
+  FileSpec local_file_spec(file->hasValue() ? **file : *system_path);
+  NormalizePath(local_file_spec);
+
+  ModuleSpec module_spec;
+  module_spec.GetFileSpec() = local_file_spec;
+  module_spec.GetPlatformFileSpec() = system_file_spec;
+  module_spec.SetObjectOffset(*load_address);
+
+  llvm::Expected<llvm::Optional<StringRef>> uuid_str =
+      json_helpers::GetOptionalStringOrError(module, "uuid");
+  if (!uuid_str)
+    return uuid_str.takeError();
+  if (uuid_str->hasValue())
+    module_spec.GetUUID().SetFromStringRef(**uuid_str);
+
+  Status error;
+  ModuleSP module_sp =
+      target_sp->GetOrCreateModule(module_spec, /*notify*/ false, &error);
+  return error.ToError();
+}
+
+llvm::Error TraceSettingsParser::ParseModules(TargetSP &target_sp,
+                                              const json::Object &process) {
+  llvm::Expected<const json::Array &> modules =
+      json_helpers::GetArrayOrError(process, "modules");
+  if (!modules)
+    return modules.takeError();
+
+  for (const json::Value &module_val : *modules) {
+    llvm::Expected<const json::Object &> module =
+        json_helpers::ToObjectOrError(module_val);
+    if (!module)
+      return module.takeError();
+    if (llvm::Error err = ParseModule(target_sp, *module))
+      return err;
+  }
+  return llvm::Error::success();
+}
+
+llvm::Error TraceSettingsParser::ParseProcess(Debugger &debugger,
+                                              const json::Object &process) {
+  llvm::Expected<int64_t> pid = json_helpers::GetIntegerOrError(process, 
"pid");
+  if (!pid)
+    return pid.takeError();
+
+  llvm::Expected<StringRef> triple =
+      json_helpers::GetStringOrError(process, "triple");
+  if (!triple)
+    return triple.takeError();
+
+  TargetSP target_sp;
+  Status error = debugger.GetTargetList().CreateTarget(
+      debugger, /*user_exe_path*/ llvm::StringRef(), *triple, 
eLoadDependentsNo,
+      /*platform_options*/ nullptr, target_sp);
+
+  if (!target_sp)
+    return error.ToError();
+
+  m_targets.push_back(target_sp);
+  debugger.GetTargetList().SetSelectedTarget(target_sp.get());
+
+  ProcessSP process_sp(target_sp->CreateProcess(
+      /*listener*/ nullptr, /*plugin_name*/ llvm::StringRef(),
+      /*crash_file*/ nullptr));
+  process_sp->SetID(static_cast<lldb::pid_t>(*pid));
+
+  if (llvm::Error err = ParseThreads(process_sp, process))
+    return err;
+
+  return ParseModules(target_sp, process);
+}
+
+llvm::Error TraceSettingsParser::ParseProcesses(Debugger &debugger) {
+  llvm::Expected<const json::Array &> processes =
+      json_helpers::GetArrayOrError(m_settings, "processes");
+  if (!processes)
+    return processes.takeError();
+
+  for (const json::Value &process_val : *processes) {
+    llvm::Expected<const json::Object &> process =
+        json_helpers::ToObjectOrError(process_val);
+    if (!process)
+      return process.takeError();
+    if (llvm::Error err = ParseProcess(debugger, *process))
+      return err;
+  }
+  return llvm::Error::success();
+}
+
+llvm::Error TraceSettingsParser::ParseSettingsImpl(Debugger &debugger) {
+  if (llvm::Error err = ParseProcesses(debugger))
+    return err;
+  return ParsePluginSettings();
+}
+
+llvm::Error
+TraceSettingsParser::ParseSettings(Debugger &debugger,
+                                   const llvm::json::Object &settings,
+                                   llvm::StringRef settings_dir) {
+  m_settings = settings;
+  m_settings_dir = settings_dir.str();
+  if (llvm::Error err = ParseSettingsImpl(debugger)) {
+    // We clean all the targets that were created internally, which should 
leave
+    // the debugger unchanged
+    for (auto target_sp : m_targets)
+      debugger.GetTargetList().DeleteTarget(target_sp);
+
+    return createStringError(std::errc::invalid_argument, "%s\nSchema:\n%s",
+                             llvm::toString(std::move(err)).c_str(),
+                             GetSchema().data());
+  }
+
+  m_trace.m_settings = m_settings;
+  m_trace.m_settings_dir = m_settings_dir;
+  m_trace.m_thread_to_trace_file_map = m_thread_to_trace_file_map;
+  m_trace.m_targets = m_targets;
+
+  return llvm::Error::success();
+}

diff  --git a/lldb/source/Utility/StructuredData.cpp 
b/lldb/source/Utility/StructuredData.cpp
index 359c49ae254b..cb26329dc726 100644
--- a/lldb/source/Utility/StructuredData.cpp
+++ b/lldb/source/Utility/StructuredData.cpp
@@ -42,7 +42,12 @@ StructuredData::ParseJSONFromFile(const FileSpec 
&input_spec, Status &error) {
                                     buffer_or_error.getError().message());
     return return_sp;
   }
-  return ParseJSON(buffer_or_error.get()->getBuffer().str());
+  llvm::Expected<json::Value> value =
+      json::parse(buffer_or_error.get()->getBuffer().str());
+  if (value)
+    return ParseJSONValue(*value);
+  error.SetErrorString(toString(value.takeError()));
+  return StructuredData::ObjectSP();
 }
 
 static StructuredData::ObjectSP ParseJSONValue(json::Value &value) {

diff  --git a/lldb/test/API/commands/trace/TestTraceLoad.py 
b/lldb/test/API/commands/trace/TestTraceLoad.py
new file mode 100644
index 000000000000..f418b8d5a822
--- /dev/null
+++ b/lldb/test/API/commands/trace/TestTraceLoad.py
@@ -0,0 +1,57 @@
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+from lldbsuite.test.decorators import *
+
+class TestTraceLoad(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+    NO_DEBUG_INFO_TESTCASE = True
+
+    def setUp(self):
+        TestBase.setUp(self)
+        if 'intel-pt' not in configuration.enabled_plugins:
+            self.skipTest("The intel-pt test plugin is not enabled")
+
+
+    def testLoadTrace(self):
+        src_dir = self.getSourceDir()
+        trace_definition_file = os.path.join(src_dir, "intelpt-trace", 
"trace.json")
+        self.expect("trace load -v " + trace_definition_file, 
substrs=["intel-pt"])
+
+        target = self.dbg.GetSelectedTarget()
+        process = target.GetProcess()
+        self.assertEqual(process.GetProcessID(), 1234)
+
+        self.assertEqual(process.GetNumThreads(), 1)
+        self.assertEqual(process.GetThreadAtIndex(0).GetThreadID(), 3842849)
+
+        self.assertEqual(target.GetNumModules(), 1)
+        module = target.GetModuleAtIndex(0)
+        path = module.GetFileSpec()
+        self.assertEqual(path.fullpath, os.path.join(src_dir, "intelpt-trace", 
"a.out"))
+        self.assertGreater(module.GetNumSections(), 0)
+        self.assertEqual(module.GetSectionAtIndex(0).GetFileAddress(), 
0x400000)
+
+        self.assertEqual("6AA9A4E2-6F28-2F33-377D-59FECE874C71-5B41261A", 
module.GetUUIDString())
+
+
+    def testLoadInvalidTraces(self):
+        src_dir = self.getSourceDir()
+        # We test first an invalid type
+        trace_definition_file = os.path.join(src_dir, "intelpt-trace", 
"trace_bad.json")
+        self.expect("trace load -v " + trace_definition_file, error=True,
+          substrs=['error: JSON value is expected to be "object"', "Value", 
"123", "Schema"])
+
+        # Now we test a missing field
+        trace_definition_file2 = os.path.join(src_dir, "intelpt-trace", 
"trace_bad2.json")
+        self.expect("trace load -v " + trace_definition_file2, error=True,
+            substrs=['error: JSON object is missing the "triple" field.', 
"Value", "pid", "12345", "Schema"])
+
+        # The following wrong schema will have a valid target and an invalid 
one. In the case of failure,
+        # no targets should be created.
+        self.assertEqual(self.dbg.GetNumTargets(), 0)
+        trace_definition_file2 = os.path.join(src_dir, "intelpt-trace", 
"trace_bad3.json")
+        self.expect("trace load -v " + trace_definition_file2, error=True,
+            substrs=['error: JSON object is missing the "pid" field.'])
+        self.assertEqual(self.dbg.GetNumTargets(), 0)

diff  --git a/lldb/test/API/commands/trace/TestTraceSchema.py 
b/lldb/test/API/commands/trace/TestTraceSchema.py
new file mode 100644
index 000000000000..1549325962ae
--- /dev/null
+++ b/lldb/test/API/commands/trace/TestTraceSchema.py
@@ -0,0 +1,22 @@
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+from lldbsuite.test.decorators import *
+
+class TestTraceLoad(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+    NO_DEBUG_INFO_TESTCASE = True
+
+    def setUp(self):
+        TestBase.setUp(self)
+        if 'intel-pt' not in configuration.enabled_plugins:
+            self.skipTest("The intel-pt test plugin is not enabled")
+
+
+    def testSchema(self):
+        self.expect("trace schema intel-pt", substrs=["trace", "triple", 
"threads", "traceFile"])
+
+    def testInvalidPluginSchema(self):
+        self.expect("trace schema invalid-plugin", error=True,
+            substrs=['error: no trace plug-in matches the specified type: 
"invalid-plugin"'])

diff  --git a/lldb/test/API/commands/trace/intelpt-trace/3842849.trace 
b/lldb/test/API/commands/trace/intelpt-trace/3842849.trace
new file mode 100644
index 000000000000..14f7b5ef4aed
Binary files /dev/null and 
b/lldb/test/API/commands/trace/intelpt-trace/3842849.trace 
diff er

diff  --git a/lldb/test/API/commands/trace/intelpt-trace/a.out 
b/lldb/test/API/commands/trace/intelpt-trace/a.out
new file mode 100755
index 000000000000..98a27fb532d6
Binary files /dev/null and b/lldb/test/API/commands/trace/intelpt-trace/a.out 
diff er

diff  --git a/lldb/test/API/commands/trace/intelpt-trace/main.cpp 
b/lldb/test/API/commands/trace/intelpt-trace/main.cpp
new file mode 100644
index 000000000000..3e4ad1c0b843
--- /dev/null
+++ b/lldb/test/API/commands/trace/intelpt-trace/main.cpp
@@ -0,0 +1,8 @@
+int main() {
+  int ret = 0;
+
+  for (int i = 0; i < 4; i++)
+    ret ^= 1;
+
+  return ret;
+}

diff  --git a/lldb/test/API/commands/trace/intelpt-trace/trace.json 
b/lldb/test/API/commands/trace/intelpt-trace/trace.json
new file mode 100644
index 000000000000..543b71d0cefe
--- /dev/null
+++ b/lldb/test/API/commands/trace/intelpt-trace/trace.json
@@ -0,0 +1,31 @@
+{
+  "trace": {
+    "type": "intel-pt",
+    "pt_cpu": {
+      "vendor": "intel",
+      "family": 6,
+      "model": 79,
+      "stepping": 1
+    }
+  },
+  "processes": [
+    {
+      "pid": 1234,
+      "triple": "x86_64-*-linux",
+      "threads": [
+        {
+          "tid": 3842849,
+          "traceFile": "3842849.trace"
+        }
+      ],
+      "modules": [
+        {
+          "file": "a.out",
+          "systemPath": "a.out",
+          "loadAddress": "0x0000000000400000",
+          "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
new file mode 100644
index 000000000000..9d00a82ebcf7
--- /dev/null
+++ b/lldb/test/API/commands/trace/intelpt-trace/trace_bad.json
@@ -0,0 +1,14 @@
+{
+  "trace": {
+    "type": "intel-pt",
+    "pt_cpu": {
+      "vendor": "intel",
+      "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
new file mode 100644
index 000000000000..16ece129c8d8
--- /dev/null
+++ b/lldb/test/API/commands/trace/intelpt-trace/trace_bad2.json
@@ -0,0 +1,41 @@
+{
+  "trace": {
+    "type": "intel-pt",
+    "pt_cpu": {
+      "vendor": "intel",
+      "family": 6,
+      "model": 79,
+      "stepping": 1
+    }
+  },
+  "processes": [
+    {
+      "pid": 1234,
+      "triple": "x86_64-*-linux",
+      "threads": [
+        {
+          "tid": 5678,
+          "traceFile": "3842849.trace"
+        }
+      ],
+      "modules": [
+        {
+          "file": "a.out",
+          "systemPath": "a.out",
+          "loadAddress": "0x0000000000400000",
+          "uuid": "6AA9A4E2-6F28-2F33-377D-59FECE874C71-5B41261A"
+        }
+      ]
+    },
+    {
+      "pid": 12345,
+      "threads": [
+        {
+          "tid": 56789,
+          "traceFile": "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
new file mode 100644
index 000000000000..dc587a8a4c52
--- /dev/null
+++ b/lldb/test/API/commands/trace/intelpt-trace/trace_bad3.json
@@ -0,0 +1,32 @@
+{
+  "trace": {
+    "type": "intel-pt",
+    "pt_cpu": {
+      "vendor": "intel",
+      "family": 6,
+      "model": 79,
+      "stepping": 1
+    }
+  },
+  "processes": [
+    {
+      "pid": 1234,
+      "triple": "x86_64-*-linux",
+      "threads": [
+        {
+          "tid": 5678,
+          "traceFile": "3842849.trace"
+        }
+      ],
+      "modules": [
+        {
+          "file": "a.out",
+          "systemPath": "a.out",
+          "loadAddress": "0x0000000000400000",
+          "uuid": "6AA9A4E2-6F28-2F33-377D-59FECE874C71-5B41261A"
+        }
+      ]
+    },
+    {}
+  ]
+}


        
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to