wallace updated this revision to Diff 288788.
wallace added a comment.
- Removed whitespace only changes.
- Simplified the way a SettingsParser is created, now there's no need to
passinga a pointer around.
- Fixed the forward declaration in lldb-private-interfaces.
This should address all comments so far except for the llvm::json changes. I'm
okay with creating another patch with those changes if the reviewers prefer
that. They are simple, though.
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/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
llvm/include/llvm/Support/JSON.h
llvm/lib/Support/JSON.cpp
Index: llvm/lib/Support/JSON.cpp
===================================================================
--- llvm/lib/Support/JSON.cpp
+++ llvm/lib/Support/JSON.cpp
@@ -77,6 +77,62 @@
return V->getAsArray();
return nullptr;
}
+
+llvm::Error Object::createMissingKeyError(llvm::StringRef K) const {
+ std::string str;
+ llvm::raw_string_ostream OS(str);
+ json::Object obj = *this;
+ OS << llvm::formatv(
+ "JSON object is missing the \"{0}\" field.\nValue::\n{1:2}", K,
+ json::Value(std::move(obj)));
+ OS.flush();
+
+ return llvm::createStringError(std::errc::invalid_argument, OS.str().c_str());
+}
+
+llvm::Expected<const json::Value *> Object::getOrError(StringRef K) const {
+ if (const json::Value *value = get(K))
+ return value;
+ else
+ return createMissingKeyError(K);
+}
+
+llvm::Expected<int64_t> Object::getIntegerOrError(StringRef K) const {
+ if (llvm::Expected<const json::Value *> V = getOrError(K))
+ return V.get()->getAsIntegerOrError();
+ else
+ return V.takeError();
+}
+
+llvm::Expected<StringRef> Object::getStringOrError(StringRef K) const {
+ if (llvm::Expected<const json::Value *> V = getOrError(K))
+ return V.get()->getAsStringOrError();
+ else
+ return V.takeError();
+}
+
+llvm::Expected<const json::Array *> Object::getArrayOrError(StringRef K) const {
+ if (llvm::Expected<const json::Value *> V = getOrError(K))
+ return V.get()->getAsArrayOrError();
+ else
+ return V.takeError();
+}
+
+llvm::Expected<const json::Object *>
+Object::getObjectOrError(StringRef K) const {
+ if (llvm::Expected<const json::Value *> V = getOrError(K))
+ return V.get()->getAsObjectOrError();
+ else
+ return V.takeError();
+}
+
+llvm::Expected<llvm::Optional<StringRef>>
+Object::getOptionalStringOrError(StringRef K) const {
+ if (const json::Value *V = get(K))
+ return V->getAsStringOrError();
+ return llvm::None;
+}
+
bool operator==(const Object &LHS, const Object &RHS) {
if (LHS.size() != RHS.size())
return false;
@@ -150,6 +206,42 @@
}
}
+llvm::Error Value::createWrongTypeError(llvm::StringRef type) const {
+ std::string str;
+ llvm::raw_string_ostream OS(str);
+ OS << llvm::formatv("JSON value is expected to be \"{0}\".\nValue:\n{1:2}",
+ type, *this);
+ OS.flush();
+
+ return llvm::createStringError(std::errc::invalid_argument, OS.str().c_str());
+}
+
+llvm::Expected<int64_t> Value::getAsIntegerOrError() const {
+ llvm::Optional<int64_t> value = getAsInteger();
+ if (value.hasValue())
+ return *value;
+ return createWrongTypeError("integer");
+}
+
+llvm::Expected<StringRef> Value::getAsStringOrError() const {
+ llvm::Optional<StringRef> value = getAsString();
+ if (value.hasValue())
+ return *value;
+ return createWrongTypeError("string");
+}
+
+llvm::Expected<const json::Array *> Value::getAsArrayOrError() const {
+ if (const json::Array *value = getAsArray())
+ return value;
+ return createWrongTypeError("array");
+}
+
+llvm::Expected<const json::Object *> Value::getAsObjectOrError() const {
+ if (const json::Object *value = getAsObject())
+ return value;
+ return createWrongTypeError("object");
+}
+
void Value::destroy() {
switch (Type) {
case T_Null:
@@ -715,4 +807,3 @@
llvm_unreachable("json::Value format options should be an integer");
json::OStream(OS, IndentAmount).value(E);
}
-
Index: llvm/include/llvm/Support/JSON.h
===================================================================
--- llvm/include/llvm/Support/JSON.h
+++ llvm/include/llvm/Support/JSON.h
@@ -145,6 +145,20 @@
json::Object *getObject(StringRef K);
const json::Array *getArray(StringRef K) const;
json::Array *getArray(StringRef K);
+
+ /// \a llvm::Expected based accessors
+ /// \{
+ llvm::Expected<const json::Value *> getOrError(StringRef K) const;
+ llvm::Expected<int64_t> getIntegerOrError(StringRef K) const;
+ llvm::Expected<llvm::StringRef> getStringOrError(StringRef K) const;
+ llvm::Expected<llvm::Optional<llvm::StringRef>>
+ getOptionalStringOrError(StringRef K) const;
+ llvm::Expected<const json::Object *> getObjectOrError(StringRef K) const;
+ llvm::Expected<const json::Array *> getArrayOrError(StringRef K) const;
+ /// \}
+
+private:
+ llvm::Error createMissingKeyError(llvm::StringRef K) const;
};
bool operator==(const Object &LHS, const Object &RHS);
inline bool operator!=(const Object &LHS, const Object &RHS) {
@@ -438,7 +452,17 @@
return LLVM_LIKELY(Type == T_Array) ? &as<json::Array>() : nullptr;
}
+ /// \a llvm::Expected based accessors
+ /// \{
+ llvm::Expected<const json::Object *> getAsObjectOrError() const;
+ llvm::Expected<const json::Array *> getAsArrayOrError() const;
+ llvm::Expected<llvm::StringRef> getAsStringOrError() const;
+ llvm::Expected<int64_t> getAsIntegerOrError() const;
+ /// \}
+
private:
+ llvm::Error createWrongTypeError(llvm::StringRef type) const;
+
void destroy();
void copyFrom(const Value &M);
// We allow moving from *const* Values, by marking all members as mutable!
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
+ }
+ },
+ "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"
+ }
+ ]
+ },
+ {}
+ ]
+}
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,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": []
+ }
+ ]
+}
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,14 @@
+{
+ "trace": {
+ "type": "intel-pt",
+ "pt_cpu": {
+ "vendor": "intel",
+ "family": 6,
+ "model": 79,
+ "stepping": 1
+ }
+ },
+ "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
+ }
+ },
+ "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"
+ }
+ ]
+ }
+ ]
+}
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(), 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)
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,238 @@
+//===-- 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"( "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 = thread.getIntegerOrError("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 =
+ thread.getStringOrError("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 =
+ process.getArrayOrError("threads");
+ if (!threads)
+ return threads.takeError();
+
+ for (const json::Value &thread_val : **threads) {
+ llvm::Expected<const json::Object *> thread =
+ thread_val.getAsObjectOrError();
+ 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 =
+ module.getStringOrError("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.getStringOrError("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.getOptionalStringOrError("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 =
+ module.getOptionalStringOrError("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 =
+ process.getArrayOrError("modules");
+ if (!modules)
+ return modules.takeError();
+
+ for (const json::Value &module_val : **modules) {
+ llvm::Expected<const json::Object *> module =
+ module_val.getAsObjectOrError();
+ 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 = process.getIntegerOrError("pid");
+ if (!pid)
+ return pid.takeError();
+
+ llvm::Expected<StringRef> triple = process.getStringOrError("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 =
+ m_settings.getArrayOrError("processes");
+ if (!processes)
+ return processes.takeError();
+
+ for (const json::Value &process_val : **processes) {
+ llvm::Expected<const json::Object *> process =
+ process_val.getAsObjectOrError();
+ 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) {
+ 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,60 @@
+//===-- 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 json::Value &settings,
+ StringRef info_dir) {
+ llvm::Expected<const json::Object *> settings_obj =
+ settings.getAsObjectOrError();
+ if (!settings_obj)
+ return settings_obj.takeError();
+
+ llvm::Expected<const json::Object *> trace =
+ settings_obj.get()->getObjectOrError("trace");
+ if (!trace)
+ return trace.takeError();
+
+ llvm::Expected<StringRef> type = trace.get()->getStringOrError("type");
+ if (!type)
+ return type.takeError();
+
+ ConstString plugin_name(*type);
+ auto create_callback = PluginManager::GetTraceCreateCallback(plugin_name);
+ if (create_callback)
+ return create_callback(**settings_obj, 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::Error err = GetParser().ParseSettings(debugger))
+ return err;
+ return llvm::Error::success();
+}
+
+llvm::StringRef Trace::GetSchema() { return GetParser().GetSchema(); }
+
+std::map<lldb::pid_t, std::map<lldb::tid_t, lldb_private::FileSpec>> &
+Trace::GetThreadToTraceFileMap() {
+ return GetParser().m_thread_to_trace_file_map;
+}
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(const llvm::json::Object &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 llvm::json::Object &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,62 @@
+//===-- 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 =
+ trace.getObjectOrError("pt_cpu");
+ if (!pt_cpu)
+ return pt_cpu.takeError();
+
+ llvm::Expected<llvm::StringRef> vendor =
+ (*pt_cpu)->getStringOrError("vendor");
+ if (!vendor)
+ return vendor.takeError();
+
+ llvm::Expected<int64_t> family = (*pt_cpu)->getIntegerOrError("family");
+ if (!family)
+ return family.takeError();
+
+ llvm::Expected<int64_t> model = (*pt_cpu)->getIntegerOrError("model");
+ if (!model)
+ return model.takeError();
+
+ llvm::Expected<int64_t> stepping = (*pt_cpu)->getIntegerOrError("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 =
+ m_settings.getObjectOrError("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,51 @@
+//===-- 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(const llvm::json::Object &settings,
+ llvm::StringRef settings_dir);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ uint32_t GetPluginVersion() override;
+ /// \}
+
+protected:
+ TraceIntelPT(const llvm::json::Object &settings, llvm::StringRef settings_dir)
+ : Trace(), m_parser(settings, settings_dir) {}
+
+ TraceIntelPTSettingsParser &GetParser() override;
+
+private:
+ pt_cpu &GetPTCPU();
+
+ TraceIntelPTSettingsParser m_parser;
+};
+
+#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,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;
+}
+
+TraceIntelPTSettingsParser &TraceIntelPT::GetParser() { return m_parser; }
+
+pt_cpu &TraceIntelPT::GetPTCPU() { return GetParser().m_pt_cpu; }
+
+//------------------------------------------------------------------
+// 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(const llvm::json::Object &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
@@ -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,301 @@
+//===-- 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(
+ *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());
+ if (llvm::Error err = trace_sp->ParseSettings(GetDebugger()))
+ return end_with_failure(std::move(err));
+ } 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;
+ }
+
+ const char *plugin_name = command[0].c_str();
+ // We create a fake JSON object with the plugin name in order to create an
+ // instance of the Trace class.
+ json::Object trace;
+ trace.try_emplace("type", plugin_name);
+ json::Object settings;
+ settings.try_emplace("trace", std::move(trace));
+
+ if (Expected<lldb::TraceSP> traceOrErr = Trace::FindPlugin(
+ json::Value(std::move(settings)), /*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
@@ -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,8 @@
const char *repl_options);
typedef int (*ComparisonFunction)(const void *, const void *);
typedef void (*DebuggerInitializeCallback)(Debugger &debugger);
+typedef lldb::TraceSP (*TraceCreateInstance)(const llvm::json::Object &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/Target/TraceSettingsParser.h
===================================================================
--- /dev/null
+++ lldb/include/lldb/Target/TraceSettingsParser.h
@@ -0,0 +1,94 @@
+//===-- 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
+/// "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(const llvm::json::Object &settings,
+ llvm::StringRef settings_dir)
+ : m_settings(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 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);
+
+public:
+ /// 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;
+
+ /// 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;
+ 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,116 @@
+//===-- 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] 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(const llvm::json::Value &settings, llvm::StringRef settings_dir);
+
+ /// 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.
+ ///
+ /// \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.
+ llvm::StringRef GetSchema();
+
+protected:
+ Trace() {}
+
+ /// The actual plug-in should define its own implementaiton of \a
+ /// TraceSettingsParser for doing any custom parsing.
+ virtual TraceSettingsParser &GetParser() = 0;
+
+ std::map<lldb::pid_t, std::map<lldb::tid_t, lldb_private::FileSpec>> &
+ GetThreadToTraceFileMap();
+
+private:
+ Trace(const Trace &) = delete;
+ const Trace &operator=(const Trace &) = delete;
+};
+
+} // 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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits