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 lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits