wallace updated this revision to Diff 288496.
wallace added a comment.

Addressed comments

- Now using StringRef correctly whenever possible.
- Revert back to using "traceFile" only inside the thread section. That's how 
intel-pt works at the moment and, as Greg suggested, we can change the schema 
in the future if needed.
- Now the global schema construction inlines the plug-in schema.
- Simplified the CMake logic
- Separated the parser from the plugin. Now new plugins need to derive their 
own Trace implementation along with their own Parser.
- I'm still using StructuredData for the parsing. There's a chance that once we 
release this feature users will want support for other formats besides JSON, so 
for now I prefer to ask for JSON input but parse in a format-agnostic way. If 
eventually no one needs any other format, we can switch to JSON-only parsing.
- Also, now the Targets created during parsing are preserved only if the 
parsing succeeded. Otherwise, the Debugger object is left unchanged.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D85705

Files:
  lldb/include/lldb/Core/PluginManager.h
  lldb/include/lldb/Target/Trace.h
  lldb/include/lldb/Target/TraceSettingsParser.h
  lldb/include/lldb/Utility/StructuredData.h
  lldb/include/lldb/lldb-forward.h
  lldb/include/lldb/lldb-private-interfaces.h
  lldb/source/Commands/CMakeLists.txt
  lldb/source/Commands/CommandObjectTrace.cpp
  lldb/source/Commands/CommandObjectTrace.h
  lldb/source/Commands/Options.td
  lldb/source/Core/PluginManager.cpp
  lldb/source/Interpreter/CommandInterpreter.cpp
  lldb/source/Plugins/CMakeLists.txt
  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/CMakeLists.txt
  lldb/source/Target/Trace.cpp
  lldb/source/Target/TraceSettingsParser.cpp
  lldb/source/Utility/StructuredData.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

Index: lldb/test/API/commands/trace/intelpt-trace/trace_bad3.json
===================================================================
--- /dev/null
+++ 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
+    }
+  },
+  "triple": "x86_64-*-linux",
+  "processes": [
+    {
+      "pid": 1234,
+      "threads": [
+        {
+          "tid": 5678,
+          "traceFile": "3842849.trace"
+        }
+      ],
+      "modules": [
+        {
+          "file": "a.out",
+          "systemPath": "a.out",
+          "loadAddress": "0x0000000000400000",
+          "uuid": "6AA9A4E2-6F28-2F33-377D-59FECE874C71-5B41261A"
+        }
+      ]
+    },
+    {}
+  ]
+}
Index: lldb/test/API/commands/trace/intelpt-trace/trace_bad2.json
===================================================================
--- /dev/null
+++ lldb/test/API/commands/trace/intelpt-trace/trace_bad2.json
@@ -0,0 +1,12 @@
+{
+  "trace": {
+    "type": "intel-pt",
+    "pt_cpu": {
+      "vendor": "intel",
+      "family": 6,
+      "model": 79,
+      "stepping": 1
+    }
+  },
+  "processes": []
+}
Index: lldb/test/API/commands/trace/intelpt-trace/trace_bad.json
===================================================================
--- /dev/null
+++ lldb/test/API/commands/trace/intelpt-trace/trace_bad.json
@@ -0,0 +1,15 @@
+{
+  "trace": {
+    "type": "intel-pt",
+    "pt_cpu": {
+      "vendor": "intel",
+      "family": 6,
+      "model": 79,
+      "stepping": 1
+    }
+  },
+  "triple": "x86_64-*-linux",
+  "processes": [
+    123
+  ]
+}
Index: lldb/test/API/commands/trace/intelpt-trace/trace.json
===================================================================
--- /dev/null
+++ 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
+    }
+  },
+  "triple": "x86_64-*-linux",
+  "processes": [
+    {
+      "pid": 1234,
+      "threads": [
+        {
+          "tid": 5678,
+          "traceFile": "3842849.trace"
+        }
+      ],
+      "modules": [
+        {
+          "file": "a.out",
+          "systemPath": "a.out",
+          "loadAddress": "0x0000000000400000",
+          "uuid": "6AA9A4E2-6F28-2F33-377D-59FECE874C71-5B41261A"
+        }
+      ]
+    }
+  ]
+}
Index: lldb/test/API/commands/trace/intelpt-trace/main.cpp
===================================================================
--- /dev/null
+++ 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;
+}
Index: lldb/test/API/commands/trace/TestTraceSchema.py
===================================================================
--- /dev/null
+++ 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"'])
Index: lldb/test/API/commands/trace/TestTraceLoad.py
===================================================================
--- /dev/null
+++ 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(), 5678)
+
+        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: structured data is expected to be "dictionary"', "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: structured data is missing the "triple" field.', "Value", '"family": 6', '"vendor": "intel"', "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: structured data is missing the "pid" field.'])
+        self.assertEqual(self.dbg.GetNumTargets(), 0)
Index: lldb/source/Utility/StructuredData.cpp
===================================================================
--- lldb/source/Utility/StructuredData.cpp
+++ lldb/source/Utility/StructuredData.cpp
@@ -42,7 +42,12 @@
                                     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) {
Index: lldb/source/Target/TraceSettingsParser.cpp
===================================================================
--- /dev/null
+++ lldb/source/Target/TraceSettingsParser.cpp
@@ -0,0 +1,255 @@
+//===-- 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;
+
+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"(  triple": string, // llvm-triple
+  "processes": [
+    {
+      "pid": integer,
+      "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 StructuredData::Dictionary &thread) {
+  llvm::Expected<lldb::tid_t> tid =
+      thread.FetchValueForKeyAsInteger<lldb::tid_t>("tid");
+  if (!tid)
+    return tid.takeError();
+
+  if (llvm::Expected<StringRef> trace_file =
+          thread.FetchValueForKeyAsString("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 StructuredData::Dictionary &process) {
+  llvm::Expected<StructuredData::Array *> threads =
+      process.FetchValueForKeyAsArray("threads");
+  if (!threads)
+    return threads.takeError();
+
+  for (size_t i = 0; i < (*threads)->GetSize(); i++) {
+    llvm::Expected<StructuredData::Dictionary *> thread =
+        (*threads)->GetItemAtIndex(i)->ToDictionary();
+    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 StructuredData::Dictionary &module) {
+  llvm::Expected<StringRef> load_address_str =
+      module.FetchValueForKeyAsString("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 =
+      module.FetchValueForKeyAsString("systemPath");
+  if (!system_path)
+    return system_path.takeError();
+  FileSpec system_file_spec(*system_path);
+  NormalizePath(system_file_spec);
+
+  llvm::Expected<llvm::Optional<StringRef>> file =
+      module.FetchValueForKeyAsOptionalString("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.GetArchitecture() = m_arch;
+  module_spec.SetObjectOffset(*load_address);
+
+  llvm::Expected<llvm::Optional<StringRef>> uuid_str =
+      module.FetchValueForKeyAsOptionalString("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 StructuredData::Dictionary &process) {
+  llvm::Expected<StructuredData::Array *> modules =
+      process.FetchValueForKeyAsArray("modules");
+  if (!modules)
+    return modules.takeError();
+
+  for (size_t i = 0; i < (*modules)->GetSize(); i++) {
+    llvm::Expected<StructuredData::Dictionary *> module =
+        (*modules)->GetItemAtIndex(i)->ToDictionary();
+    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 StructuredData::Dictionary &process) {
+  llvm::Expected<lldb::pid_t> pid =
+      process.FetchValueForKeyAsInteger<lldb::pid_t>("pid");
+  if (!pid)
+    return pid.takeError();
+
+  TargetSP target_sp;
+  Status error = debugger.GetTargetList().CreateTarget(
+      debugger, /*user_exe_path*/ llvm::StringRef(),
+      /*triple_str*/ llvm::StringRef(), 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(*pid);
+
+  if (llvm::Error err = ParseThreads(process_sp, process))
+    return err;
+
+  return ParseModules(target_sp, process);
+}
+
+llvm::Error TraceSettingsParser::ParseProcesses(Debugger &debugger) {
+  llvm::Expected<StructuredData::Array *> processes =
+      m_settings.FetchValueForKeyAsArray("processes");
+  if (!processes)
+    return processes.takeError();
+
+  for (size_t i = 0; i < (*processes)->GetSize(); i++) {
+    llvm::Expected<StructuredData::Dictionary *> process =
+        (*processes)->GetItemAtIndex(i)->ToDictionary();
+    if (!process)
+      return process.takeError();
+    if (llvm::Error err = ParseProcess(debugger, **process))
+      return err;
+  }
+  return llvm::Error::success();
+}
+
+llvm::Error TraceSettingsParser::ParseArchitecture() {
+  llvm::Expected<StringRef> triple =
+      m_settings.FetchValueForKeyAsString("triple");
+  if (!triple)
+    return triple.takeError();
+  m_arch = ArchSpec(*triple);
+  return llvm::Error::success();
+}
+
+llvm::Error TraceSettingsParser::ParseSettingsImpl(Debugger &debugger) {
+  if (llvm::Error err = ParseArchitecture())
+    return err;
+
+  if (llvm::Error err = ParseProcesses(debugger))
+    return err;
+  return ParsePluginSettings();
+}
+
+llvm::Error TraceSettingsParser::ParseSettings(Debugger &debugger) {
+  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());
+  }
+  return llvm::Error::success();
+}
Index: lldb/source/Target/Trace.cpp
===================================================================
--- /dev/null
+++ lldb/source/Target/Trace.cpp
@@ -0,0 +1,51 @@
+//===-- 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(const StructuredData::Dictionary &info, StringRef info_dir) {
+  llvm::Expected<StructuredData::Dictionary *> trace =
+      info.FetchValueForKeyAsDictionary("trace");
+  if (!trace)
+    return trace.takeError();
+  llvm::Expected<StringRef> type = (*trace)->FetchValueForKeyAsString("type");
+  if (!type)
+    return type.takeError();
+  ConstString plugin_name(*type);
+  auto create_callback = PluginManager::GetTraceCreateCallback(plugin_name);
+  if (create_callback) {
+    return create_callback(std::move(info), info_dir);
+  }
+  return createStringError(
+      std::errc::invalid_argument,
+      "no trace plug-in matches the specified type: \"%s\"",
+      type->str().c_str());
+}
+
+llvm::Error Trace::ParseSettings(Debugger &debugger) {
+  if (llvm::Expected<std::shared_ptr<TraceSettingsParser>> parser =
+          ParsePluginSettings(debugger)) {
+    m_thread_to_trace_file_map =
+        std::move((*parser)->m_thread_to_trace_file_map);
+    m_arch = std::move((*parser)->m_arch);
+    return llvm::Error::success();
+  } else
+    return parser.takeError();
+}
Index: lldb/source/Target/CMakeLists.txt
===================================================================
--- lldb/source/Target/CMakeLists.txt
+++ lldb/source/Target/CMakeLists.txt
@@ -65,6 +65,8 @@
   ThreadPlanTracer.cpp
   ThreadPlanStack.cpp
   ThreadSpec.cpp
+  Trace.cpp
+  TraceSettingsParser.cpp
   UnixSignals.cpp
   UnwindAssembly.cpp
   UnwindLLDB.cpp
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSettingsParser.h
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSettingsParser.h
@@ -0,0 +1,36 @@
+//===-- 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 "lldb/Target/TraceSettingsParser.h"
+#include "lldb/Utility/StructuredData.h"
+
+class TraceIntelPTSettingsParser : public lldb_private::TraceSettingsParser {
+
+public:
+  TraceIntelPTSettingsParser(lldb_private::StructuredData::Dictionary settings,
+                             llvm::StringRef settings_dir)
+      : lldb_private::TraceSettingsParser(settings, settings_dir) {}
+
+protected:
+  llvm::StringRef GetPluginSchema() override;
+
+  llvm::Error ParsePluginSettings() override;
+
+private:
+  llvm::Error ParsePTCPU(const lldb_private::StructuredData::Dictionary &trace);
+
+public:
+  pt_cpu m_pt_cpu;
+};
+
+#endif // liblldb_TraceIntelPTSettingsParser_h_
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSettingsParser.cpp
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSettingsParser.cpp
@@ -0,0 +1,65 @@
+//===-- 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 StructuredData::Dictionary &trace) {
+  llvm::Expected<StructuredData::Dictionary *> pt_cpu =
+      trace.FetchValueForKeyAsDictionary("pt_cpu");
+  if (!pt_cpu)
+    return pt_cpu.takeError();
+
+  llvm::Expected<llvm::StringRef> vendor =
+      (*pt_cpu)->FetchValueForKeyAsString("vendor");
+  if (!vendor)
+    return vendor.takeError();
+
+  llvm::Expected<uint16_t> family =
+      (*pt_cpu)->FetchValueForKeyAsInteger<uint16_t>("family");
+  if (!family)
+    return family.takeError();
+
+  llvm::Expected<uint8_t> model =
+      (*pt_cpu)->FetchValueForKeyAsInteger<uint8_t>("model");
+  if (!model)
+    return model.takeError();
+
+  llvm::Expected<uint8_t> stepping =
+      (*pt_cpu)->FetchValueForKeyAsInteger<uint8_t>("stepping");
+  if (!stepping)
+    return stepping.takeError();
+
+  m_pt_cpu = {vendor->compare("intel") == 0 ? pcv_intel : pcv_unknown, *family,
+              *model, *stepping};
+  return llvm::Error::success();
+}
+
+llvm::Error TraceIntelPTSettingsParser::ParsePluginSettings() {
+  llvm::Expected<StructuredData::Dictionary *> trace =
+      m_settings.FetchValueForKeyAsDictionary("trace");
+  if (!trace)
+    return trace.takeError();
+  return ParsePTCPU(**trace);
+}
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
@@ -0,0 +1,54 @@
+//===-- 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;
+
+  llvm::StringRef GetSchema() override;
+
+  /// PluginInterface protocol
+  /// \{
+  lldb_private::ConstString GetPluginName() override;
+
+  static void Initialize();
+
+  static void Terminate();
+
+  static lldb::TraceSP
+  CreateInstance(lldb_private::StructuredData::Dictionary settings,
+                 llvm::StringRef settings_dir);
+
+  static lldb_private::ConstString GetPluginNameStatic();
+
+  uint32_t GetPluginVersion() override;
+  /// \}
+
+protected:
+  TraceIntelPT(lldb_private::StructuredData::Dictionary settings,
+               llvm::StringRef settings_dir)
+      : Trace(settings, settings_dir) {}
+
+  llvm::Expected<std::shared_ptr<lldb_private::TraceSettingsParser>>
+  ParsePluginSettings(lldb_private::Debugger &debugger) override;
+
+private:
+  pt_cpu m_pt_cpu;
+};
+
+#endif // liblldb_TraceIntelPT_h_
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
@@ -0,0 +1,62 @@
+//===-- 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;
+}
+
+llvm::Expected<std::shared_ptr<TraceSettingsParser>>
+TraceIntelPT::ParsePluginSettings(Debugger &debugger) {
+  auto parser =
+      std::make_shared<TraceIntelPTSettingsParser>(m_settings, m_settings_dir);
+  if (llvm::Error err = parser->ParseSettings(debugger))
+    return std::move(err);
+
+  m_pt_cpu = std::move(parser->m_pt_cpu);
+  return parser;
+}
+
+StringRef TraceIntelPT::GetSchema() {
+  return TraceIntelPTSettingsParser(m_settings, m_settings_dir).GetSchema();
+}
+
+//------------------------------------------------------------------
+// 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(StructuredData::Dictionary settings,
+                                           StringRef settings_dir) {
+  return lldb::TraceSP(new TraceIntelPT(settings, settings_dir));
+}
Index: lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt
===================================================================
--- /dev/null
+++ 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
+  )
Index: lldb/source/Plugins/Trace/CMakeLists.txt
===================================================================
--- /dev/null
+++ 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()
Index: lldb/source/Plugins/CMakeLists.txt
===================================================================
--- lldb/source/Plugins/CMakeLists.txt
+++ lldb/source/Plugins/CMakeLists.txt
@@ -19,6 +19,7 @@
 add_subdirectory(SymbolFile)
 add_subdirectory(SystemRuntime)
 add_subdirectory(SymbolVendor)
+add_subdirectory(Trace)
 add_subdirectory(TypeSystem)
 add_subdirectory(UnwindAssembly)
 
Index: lldb/source/Interpreter/CommandInterpreter.cpp
===================================================================
--- lldb/source/Interpreter/CommandInterpreter.cpp
+++ lldb/source/Interpreter/CommandInterpreter.cpp
@@ -38,6 +38,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"
@@ -511,6 +512,7 @@
   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);
Index: lldb/source/Core/PluginManager.cpp
===================================================================
--- lldb/source/Core/PluginManager.cpp
+++ lldb/source/Core/PluginManager.cpp
@@ -1005,6 +1005,30 @@
   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;
Index: lldb/source/Commands/Options.td
===================================================================
--- lldb/source/Commands/Options.td
+++ lldb/source/Commands/Options.td
@@ -202,8 +202,8 @@
     Desc<"Move breakpoints to nearest code. If not set the "
     "target.move-to-nearest-codesetting is used.">;
   def breakpoint_set_file_colon_line : Option<"joint-specifier", "y">, Group<12>, Arg<"FileLineColumn">,
-    Required, Completion<"SourceFile">, 
-    Desc<"A specifier in the form filename:line[:column] for setting file & line breakpoints.">;  
+    Required, Completion<"SourceFile">,
+    Desc<"A specifier in the form filename:line[:column] for setting file & line breakpoints.">;
   /* Don't add this option till it actually does something useful...
   def breakpoint_set_exception_typename : Option<"exception-typename", "O">,
     Arg<"TypeName">, Desc<"The breakpoint will only stop if an "
@@ -751,7 +751,7 @@
   def source_list_reverse : Option<"reverse", "r">, Group<4>, Desc<"Reverse the"
     " listing to look backwards from the last displayed block of source.">;
   def source_list_file_colon_line : Option<"joint-specifier", "y">, Group<5>,
-    Arg<"FileLineColumn">, Completion<"SourceFile">, 
+    Arg<"FileLineColumn">, Completion<"SourceFile">,
     Desc<"A specifier in the form filename:line[:column] from which to display"
          " source.">;
 }
@@ -1161,3 +1161,19 @@
   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.">;
+}
Index: lldb/source/Commands/CommandObjectTrace.h
===================================================================
--- /dev/null
+++ lldb/source/Commands/CommandObjectTrace.h
@@ -0,0 +1,25 @@
+//===-- CommandObjectTrace.h ----------------------------------------------===//
+//
+// 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
Index: lldb/source/Commands/CommandObjectTrace.cpp
===================================================================
--- /dev/null
+++ lldb/source/Commands/CommandObjectTrace.cpp
@@ -0,0 +1,295 @@
+//===-- 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 <sstream>
+
+#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;
+    }
+
+    FileSpec json_file(command[0].ref());
+    Status error;
+    auto data_sp = StructuredData::ParseJSONFromFile(json_file, error);
+    if (error.Success()) {
+      StructuredData::Dictionary *dict = data_sp->GetAsDictionary();
+      if (dict) {
+        if (Expected<lldb::TraceSP> traceOrErr = Trace::FindPlugin(
+                *dict, 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());
+          error = trace_sp->ParseSettings(GetDebugger());
+        } else {
+          error.SetErrorString(llvm::toString(traceOrErr.takeError()));
+        }
+      } else {
+        error.SetErrorString("JSON data must be a dictionary");
+      }
+    }
+    if (error.Success()) {
+      result.SetStatus(eReturnStatusSuccessFinishResult);
+    } else {
+      result.AppendErrorWithFormat("%s\n", error.AsCString());
+      result.SetStatus(eReturnStatusFailed);
+    }
+    return result.Succeeded();
+  }
+
+  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;
+    }
+
+    const char *plugin_name = command[0].c_str();
+
+    // We create a fake dictionary with the plugin name in order to create an
+    // instance of the Trace class.
+    std::shared_ptr<StructuredData::Dictionary> trace(
+        new StructuredData::Dictionary());
+    trace->AddStringItem("type", plugin_name);
+    StructuredData::Dictionary dict;
+    dict.AddItem("trace", trace);
+
+    if (Expected<lldb::TraceSP> traceOrErr =
+            Trace::FindPlugin(dict, /*settings_dir*/ "")) {
+      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;
Index: lldb/source/Commands/CMakeLists.txt
===================================================================
--- lldb/source/Commands/CMakeLists.txt
+++ lldb/source/Commands/CMakeLists.txt
@@ -29,6 +29,7 @@
   CommandObjectStats.cpp
   CommandObjectTarget.cpp
   CommandObjectThread.cpp
+  CommandObjectTrace.cpp
   CommandObjectType.cpp
   CommandObjectVersion.cpp
   CommandObjectWatchpoint.cpp
Index: lldb/include/lldb/lldb-private-interfaces.h
===================================================================
--- lldb/include/lldb/lldb-private-interfaces.h
+++ lldb/include/lldb/lldb-private-interfaces.h
@@ -11,6 +11,7 @@
 
 #if defined(__cplusplus)
 
+#include "lldb/Utility/StructuredData.h"
 #include "lldb/lldb-enumerations.h"
 #include "lldb/lldb-forward.h"
 #include "lldb/lldb-private-enumerations.h"
@@ -104,6 +105,8 @@
                                            const char *repl_options);
 typedef int (*ComparisonFunction)(const void *, const void *);
 typedef void (*DebuggerInitializeCallback)(Debugger &debugger);
+typedef lldb::TraceSP (*TraceCreateInstance)(StructuredData::Dictionary info,
+                                             llvm::StringRef info_dir);
 
 } // namespace lldb_private
 
Index: lldb/include/lldb/lldb-forward.h
===================================================================
--- lldb/include/lldb/lldb-forward.h
+++ lldb/include/lldb/lldb-forward.h
@@ -226,6 +226,8 @@
 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::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;
Index: lldb/include/lldb/Utility/StructuredData.h
===================================================================
--- lldb/include/lldb/Utility/StructuredData.h
+++ lldb/include/lldb/Utility/StructuredData.h
@@ -10,11 +10,14 @@
 #define LLDB_UTILITY_STRUCTUREDDATA_H
 
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
 #include "llvm/Support/JSON.h"
 
 #include "lldb/Utility/ConstString.h"
 #include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Status.h"
 #include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
 #include "lldb/lldb-enumerations.h"
 
 #include <cassert>
@@ -95,9 +98,9 @@
     }
 
     Integer *GetAsInteger() {
-      return ((m_type == lldb::eStructuredDataTypeInteger)
-                  ? static_cast<Integer *>(this)
-                  : nullptr);
+      return (m_type == lldb::eStructuredDataTypeInteger)
+                 ? static_cast<Integer *>(this)
+                 : nullptr;
     }
 
     uint64_t GetIntegerValue(uint64_t fail_value = 0) {
@@ -149,6 +152,37 @@
 
     ObjectSP GetObjectForDotSeparatedPath(llvm::StringRef path);
 
+    /// \a llvm::Expected based accessors
+    /// \{
+    llvm::Expected<Dictionary *> ToDictionary() {
+      Dictionary *ptr = GetAsDictionary();
+      if (!ptr)
+        return CreateWrongTypeError("dictionary");
+      return ptr;
+    }
+
+    llvm::Expected<Array *> ToArray() {
+      Array *ptr = GetAsArray();
+      if (!ptr)
+        return CreateWrongTypeError("array");
+      return ptr;
+    }
+
+    llvm::Expected<Integer *> ToInteger() {
+      Integer *ptr = GetAsInteger();
+      if (!ptr)
+        return CreateWrongTypeError("integer");
+      return ptr;
+    }
+
+    llvm::Expected<String *> ToString() {
+      String *ptr = GetAsString();
+      if (!ptr)
+        return CreateWrongTypeError("string");
+      return ptr;
+    }
+    /// \}
+
     void DumpToStdout(bool pretty_print = true) const;
 
     virtual void Serialize(llvm::json::OStream &s) const = 0;
@@ -159,6 +193,17 @@
     }
 
   private:
+    llvm::Error CreateWrongTypeError(llvm::StringRef type) {
+      StreamString object_stream;
+      Dump(object_stream);
+      object_stream.Flush();
+
+      return llvm::createStringError(
+          std::errc::invalid_argument,
+          "structured data is expected to be \"%s\".\nValue:\n%s", type.data(),
+          object_stream.GetData());
+    }
+
     lldb::StructuredDataType m_type;
   };
 
@@ -399,6 +444,7 @@
       }
       return success;
     }
+
     template <class IntType>
     bool GetValueForKeyAsInteger(llvm::StringRef key, IntType &result) const {
       ObjectSP value_sp = GetValueForKey(key);
@@ -485,6 +531,61 @@
       return false;
     }
 
+    /// \a llvm::Expected based accessors
+    /// \{
+    llvm::Expected<ObjectSP> FetchValueForKey(llvm::StringRef key) const {
+      ObjectSP value = GetValueForKey(key);
+      if (!value)
+        return CreateMissingKeyError(key);
+      return value;
+    }
+
+    template <class IntType>
+    llvm::Expected<IntType>
+    FetchValueForKeyAsInteger(llvm::StringRef key) const {
+      llvm::Expected<ObjectSP> value = FetchValueForKey(key);
+      if (!value)
+        return value.takeError();
+      llvm::Expected<Integer *> int_value = (*value)->ToInteger();
+      if (!int_value)
+        return int_value.takeError();
+      return static_cast<IntType>((*int_value)->GetValue());
+    }
+
+    llvm::Expected<llvm::StringRef>
+    FetchValueForKeyAsString(llvm::StringRef key) const {
+      llvm::Expected<ObjectSP> value = FetchValueForKey(key);
+      if (!value)
+        return value.takeError();
+      llvm::Expected<String *> string_value = (*value)->ToString();
+      if (!string_value)
+        return value.takeError();
+      return (*string_value)->GetValue();
+    }
+
+    llvm::Expected<llvm::Optional<llvm::StringRef>>
+    FetchValueForKeyAsOptionalString(llvm::StringRef key) const {
+      if (!HasKey(key))
+        return llvm::None;
+      return FetchValueForKeyAsString(key);
+    }
+
+    llvm::Expected<Dictionary *>
+    FetchValueForKeyAsDictionary(llvm::StringRef key) const {
+      llvm::Expected<ObjectSP> value = FetchValueForKey(key);
+      if (!value)
+        return value.takeError();
+      return (*value)->ToDictionary();
+    }
+
+    llvm::Expected<Array *> FetchValueForKeyAsArray(llvm::StringRef key) const {
+      llvm::Expected<ObjectSP> value = FetchValueForKey(key);
+      if (!value)
+        return value.takeError();
+      return (*value)->ToArray();
+    }
+    /// \}
+
     bool HasKey(llvm::StringRef key) const {
       ConstString key_cs(key);
       collection::const_iterator search = m_dict.find(key_cs);
@@ -515,6 +616,16 @@
     void Serialize(llvm::json::OStream &s) const override;
 
   protected:
+    llvm::Error CreateMissingKeyError(llvm::StringRef key) const {
+      StreamString object_stream;
+      Dump(object_stream);
+      object_stream.Flush();
+      return llvm::createStringError(std::errc::invalid_argument,
+                                     "structured data is missing the \"%s\" "
+                                     "field.\nValue:\n%s",
+                                     key.data(), object_stream.GetData());
+    }
+
     typedef std::map<ConstString, ObjectSP> collection;
     collection m_dict;
   };
Index: lldb/include/lldb/Target/TraceSettingsParser.h
===================================================================
--- /dev/null
+++ lldb/include/lldb/Target/TraceSettingsParser.h
@@ -0,0 +1,95 @@
+//===-- 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/Utility/ArchSpec.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+/// \class TraceSettingsParser TraceSettingsParser.h
+/// "lldb/Target/TraceSettingsParser.h" A plug-in interface definition class for
+/// parsing \a Trace settings.
+///
+/// A \a Trace plug-in is initialized with some settings in a structured data
+/// format like JSON. This class offers basic parsing functionalities to convert
+/// those settings into objects used for the \a Trace plug-in.
+///
+/// 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(StructuredData::Dictionary settings,
+                      llvm::StringRef settings_dir)
+      : m_settings(std::move(settings)), m_settings_dir(settings_dir) {}
+
+  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.
+  ///
+  /// \return
+  ///   An error object containing the reason if there is a failure.
+  llvm::Error ParseSettings(Debugger &debugger);
+
+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 StructuredData::Dictionary &process);
+  llvm::Error ParseProcesses(lldb_private::Debugger &debugger);
+  llvm::Error ParseThread(lldb::ProcessSP &process_sp,
+                          const StructuredData::Dictionary &thread);
+  llvm::Error ParseThreads(lldb::ProcessSP &process_sp,
+                           const StructuredData::Dictionary &process);
+  llvm::Error ParseModule(lldb::TargetSP &target_sp,
+                          const StructuredData::Dictionary &module);
+  llvm::Error ParseModules(lldb::TargetSP &target_sp,
+                           const StructuredData::Dictionary &process);
+  llvm::Error ParseArchitecture();
+  llvm::Error ParseSettingsImpl(lldb_private::Debugger &debugger);
+
+public:
+  StructuredData::Dictionary m_settings;
+  std::string m_settings_dir;
+
+  /// Objects created as product of the parsing
+  /// \{
+  std::map<lldb::pid_t, std::map<lldb::tid_t, lldb_private::FileSpec>>
+      m_thread_to_trace_file_map;
+  lldb_private::ArchSpec m_arch;
+  std::vector<lldb::TargetSP> m_targets;
+  /// \}
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_TARGET_TRACE_SETTINGS_PARSER_H
Index: lldb/include/lldb/Target/Trace.h
===================================================================
--- /dev/null
+++ lldb/include/lldb/Target/Trace.h
@@ -0,0 +1,139 @@
+//===-- 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 "lldb/Core/PluginInterface.h"
+#include "lldb/Target/TraceSettingsParser.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/StructuredData.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class StructuredData;
+
+/// \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 structured 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 structured 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] settings
+  ///     Structured data in the form of a dictionary.
+  ///
+  /// \param[in] settings_dir
+  ///     Path to a directory used to resolve relative paths in the structured
+  ///     data. If the structured data is defined in a file, this should be the
+  ///     folder containing it.
+  static llvm::Expected<lldb::TraceSP>
+  FindPlugin(const StructuredData::Dictionary &settings,
+             llvm::StringRef settings_dir);
+
+  const ArchSpec &GetArchitecture() const { return m_arch; }
+
+  /// 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.
+  ///
+  /// \return
+  ///   An error object containing the reason if there is a failure.
+  llvm::Error ParseSettings(Debugger &debugger);
+
+  /// Get the JSON schema of the settings for the trace plug-in.
+  virtual llvm::StringRef GetSchema() = 0;
+
+protected:
+  Trace(StructuredData::Dictionary settings, llvm::StringRef settings_dir)
+      : m_settings(std::move(settings)), m_settings_dir(settings_dir) {}
+
+  /// Method that should be overriden to parse plugin-specific settings.
+  ///
+  /// The plug-in implementation should define its own implementation of \a
+  /// TraceSettingsParser for its own plug-in specific parsing.
+  ///
+  /// \param[in] debugger
+  ///   The debugger instance where the corresponding \a Target objects are
+  ///   created.
+  ///
+  /// \return
+  ///   A pointer to the \a TraceSettingsParser used by the plug-in
+  ///   implementation, or an error object in case of failures.
+  virtual llvm::Expected<std::shared_ptr<TraceSettingsParser>>
+  ParsePluginSettings(Debugger &debugger) = 0;
+
+private:
+  Trace(const Trace &) = delete;
+  const Trace &operator=(const Trace &) = delete;
+
+protected:
+  /// Structured data that holds all settings for this trace session.
+  StructuredData::Dictionary 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;
+  lldb_private::ArchSpec m_arch;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_TARGET_TRACE_H
Index: lldb/include/lldb/Core/PluginManager.h
===================================================================
--- lldb/include/lldb/Core/PluginManager.h
+++ lldb/include/lldb/Core/PluginManager.h
@@ -330,6 +330,14 @@
   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);
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to