wallace updated this revision to Diff 287527. wallace added a comment. - Added a command that shows the schema of a given trace plugin. - Added a test for such command
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/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/Target/CMakeLists.txt lldb/source/Target/Trace.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
Index: lldb/test/API/commands/trace/intelpt-trace/trace_bad2.json =================================================================== --- /dev/null +++ lldb/test/API/commands/trace/intelpt-trace/trace_bad2.json @@ -0,0 +1,12 @@ +{ + "plugin": { + "type": "intel-pt", + "pt_cpu": { + "vendor": "intel", + "family": 6, + "model": 79, + "stepping": 1 + } + }, + "processes": [] +} Index: lldb/test/API/commands/trace/intelpt-trace/trace_bad.json =================================================================== --- /dev/null +++ lldb/test/API/commands/trace/intelpt-trace/trace_bad.json @@ -0,0 +1,15 @@ +{ + "plugin": { + "type": "intel-pt", + "pt_cpu": { + "vendor": "intel", + "family": 6, + "model": 79, + "stepping": 1 + } + }, + "triple": "x86_64-*-linux", + "processes": [ + 123 + ] +} Index: lldb/test/API/commands/trace/intelpt-trace/trace.json =================================================================== --- /dev/null +++ lldb/test/API/commands/trace/intelpt-trace/trace.json @@ -0,0 +1,30 @@ +{ + "plugin": { + "type": "intel-pt", + "pt_cpu": { + "vendor": "intel", + "family": 6, + "model": 79, + "stepping": 1 + } + }, + "triple": "x86_64-*-linux", + "processes": [ + { + "pid": 1234, + "threads": [ + { + "tid": 5678, + "traceFile": "3842849.trace" + } + ], + "modules": [ + { + "file": "a.out", + "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,52 @@ +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): + schema = """{ + "plugin": { + "type": "intel-pt", + "pt_cpu": { + "vendor": "intel" | "unknown", + "family": integer, + "model": integer, + "stepping": integer + } + }, + "triple": string, // llvm-triple + "processes": [ + { + "pid": integer, + "threads": [ + { + "tid": integer, + "traceFile": string // path to trace file relative to the settings file + } + ], + "modules": [ + { + "file": string, // path to trace file relative to the settings file + "loadAddress": string, // address in hex or decimal form + "uuid"?: string, + } + ] + } + ] +}""" + self.expect("trace schema intel-pt", substrs=[schema]) + + 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,126 @@ +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil +from lldbsuite.test.decorators import * + +class TestTraceLoad(TestBase): + + mydir = TestBase.compute_mydir(__file__) + NO_DEBUG_INFO_TESTCASE = True + + def setUp(self): + TestBase.setUp(self) + if 'intel-pt' not in configuration.enabled_plugins: + self.skipTest("The intel-pt test plugin is not enabled") + + + def testLoadTrace(self): + src_dir = self.getSourceDir() + trace_definition_file = os.path.join(src_dir, "intelpt-trace", "trace.json") + self.expect("trace load -v " + trace_definition_file, substrs=["intel-pt"]) + + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + self.assertEqual(process.GetProcessID(), 1234) + + self.assertEqual(process.GetNumThreads(), 1) + self.assertEqual(process.GetThreadAtIndex(0).GetThreadID(), 5678) + + self.assertEqual(target.GetNumModules(), 1) + module = target.GetModuleAtIndex(0) + path = module.GetFileSpec() + self.assertEqual(path.fullpath, os.path.join(src_dir, "intelpt-trace", "a.out")) + self.assertGreater(module.GetNumSections(), 0) + self.assertEqual(module.GetSectionAtIndex(0).GetFileAddress(), 0x400000) + + self.assertEqual("6AA9A4E2-6F28-2F33-377D-59FECE874C71-5B41261A", module.GetUUIDString()) + + + def testLoadInvalidTraces(self): + src_dir = self.getSourceDir() + # We test first an invalid type + trace_definition_file = os.path.join(src_dir, "intelpt-trace", "trace_bad.json") + failed_output = """error: structured data is expected to be "dictionary". +Value: +123 +Schema: +{ + "plugin": { + "type": "intel-pt", + "pt_cpu": { + "vendor": "intel" | "unknown", + "family": integer, + "model": integer, + "stepping": integer + } + }, + "triple": string, // llvm-triple + "processes": [ + { + "pid": integer, + "threads": [ + { + "tid": integer, + "traceFile": string // path to trace file relative to the settings file + } + ], + "modules": [ + { + "file": string, // path to trace file relative to the settings file + "loadAddress": string, // address in hex or decimal form + "uuid"?: string, + } + ] + } + ] +}""" + self.expect("trace load -v " + trace_definition_file, substrs=[failed_output], error=True) + + # Now we test a missing field + trace_definition_file2 = os.path.join(src_dir, "intelpt-trace", "trace_bad2.json") + failed_output2 = """error: structured data is missing the "triple" "string" field. +Value: +{ + "plugin": { + "pt_cpu": { + "family": 6, + "model": 79, + "stepping": 1, + "vendor": "intel" + }, + "type": "intel-pt" + }, + "processes": [] +} +Schema: +{ + "plugin": { + "type": "intel-pt", + "pt_cpu": { + "vendor": "intel" | "unknown", + "family": integer, + "model": integer, + "stepping": integer + } + }, + "triple": string, // llvm-triple + "processes": [ + { + "pid": integer, + "threads": [ + { + "tid": integer, + "traceFile": string // path to trace file relative to the settings file + } + ], + "modules": [ + { + "file": string, // path to trace file relative to the settings file + "loadAddress": string, // address in hex or decimal form + "uuid"?: string, + } + ] + } + ] +}""" + self.expect("trace load -v " + trace_definition_file2, substrs=[failed_output2], error=True) 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/Trace.cpp =================================================================== --- /dev/null +++ lldb/source/Target/Trace.cpp @@ -0,0 +1,273 @@ +//===-- 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 "Plugins/Process/Utility/HistoryThread.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Target/Process.h" +#include "lldb/Utility/StreamString.h" +#include "llvm/Support/Format.h" + +using namespace lldb; +using namespace lldb_private; +using namespace llvm; + +llvm::Expected<lldb::TraceSP> Trace::FindPlugin(StructuredData::Dictionary info, + const char *info_dir) { + StructuredData::Dictionary *plugin = nullptr; + if (!info.GetValueForKeyAsDictionary("plugin", plugin)) + return createStringError(std::errc::invalid_argument, + "structured data is missing the \"plugin\" field " + "that identifies the trace plug-in"); + StringRef type; + if (!plugin->GetValueForKeyAsString("type", type)) + return createStringError( + std::errc::invalid_argument, + "structured data is missing the \"plugin.type\" field " + "that identifies the trace plug-in"); + ConstString plugin_name(type); + auto create_callback = PluginManager::GetTraceCreateCallback(plugin_name); + if (create_callback) { + return create_callback(std::move(info), info_dir); + } + return createStringError( + std::errc::invalid_argument, + "no trace plug-in matches the specified type: \"%s\"", + type.str().c_str()); +} + +//------------------------------------------------------------------ +// Structured data parsing +//------------------------------------------------------------------ + +const char *Trace::GetSchema() { + static std::string schema; + if (schema.empty()) { + std::string raw_schema(R"({ + "plugin": <<<plugin_schema>>>, + "triple": string, // llvm-triple + "processes": [ + { + "pid": integer, + "threads": [ + { + "tid": integer, + "traceFile": string // path to trace file relative to the settings file + } + ], + "modules": [ + { + "file": string, // path to trace file relative to the settings file + "loadAddress": string, // address in hex or decimal form + "uuid"?: string, + } + ] + } + ] +})"); + std::string plugin_schema(GetPluginSchema()); + // We need to add spaces to indent correctly the plugin schema + plugin_schema = std::regex_replace(plugin_schema, std::regex("\n"), "\n "); + schema = std::regex_replace(raw_schema, std::regex("<<<plugin_schema>>>"), + plugin_schema); + } + return schema.c_str(); +} + +bool Trace::SetMissingFieldError(Status &error, const char *field, + const char *type, + const StructuredData::Dictionary &dict) { + StreamString object_stream; + dict.Dump(object_stream); + object_stream.Flush(); + + error.SetErrorStringWithFormat("structured data is missing the \"%s\" \"%s\" " + "field.\nValue:\n%s\nSchema:\n%s", + field, type, object_stream.GetData(), + GetSchema()); + return false; +} + +bool Trace::SetWrongTypeError(Status &error, const char *type, + const StructuredData::Object &object) { + StreamString object_stream; + object.Dump(object_stream); + object_stream.Flush(); + + error.SetErrorStringWithFormat( + "structured data is expected to be \"%s\".\nValue:\n%s\nSchema:\n%s", + type, object_stream.GetData(), GetSchema()); + return false; +} + +bool Trace::ExtractDictionaryFromArray(const StructuredData::Array &array, + size_t index, + StructuredData::Dictionary *&dict, + Status &error) { + StructuredData::ObjectSP item = array.GetItemAtIndex(index); + dict = item->GetAsDictionary(); + if (!dict) + return SetWrongTypeError(error, "dictionary", *item); + return true; +} + +bool Trace::ExtractOptionalStringFromDictionary( + const StructuredData::Dictionary &dict, const char *field, StringRef &value, + Status &error) { + if (!dict.HasKey(field)) + return true; + if (dict.GetValueForKeyAsString(field, value)) + return true; + return SetWrongTypeError(error, "string", *dict.GetValueForKey(field)); +} + +bool Trace::ParseThread(ProcessSP &process_sp, + const StructuredData::Dictionary &thread, + Status &error) { + lldb::tid_t tid; + if (!thread.GetValueForKeyAsInteger("tid", tid)) + return SetMissingFieldError(error, "tid", "integer", thread); + + ThreadSP thread_sp(new HistoryThread(*process_sp, tid, /*callstack*/ {})); + process_sp->GetThreadList().AddThread(thread_sp); + return true; +} + +bool Trace::ParseThreads(ProcessSP &process_sp, + const StructuredData::Dictionary &process, + Status &error) { + StructuredData::Array *threads = nullptr; + if (!process.GetValueForKeyAsArray("threads", threads)) + return SetMissingFieldError(error, "threads", "array", process); + + for (size_t i = 0; i < threads->GetSize(); i++) { + StructuredData::Dictionary *thread = nullptr; + if (!ExtractDictionaryFromArray(*threads, i, thread, error)) + return false; + if (!ParseThread(process_sp, *thread, error)) + return false; + } + return true; +} + +bool Trace::ParseModule(TargetSP &target_sp, + const StructuredData::Dictionary &module, + Status &error) { + StringRef load_address_str; + if (!module.GetValueForKeyAsString("loadAddress", load_address_str)) + return SetMissingFieldError(error, "loadAddress", "string", module); + addr_t load_address; + if (load_address_str.getAsInteger(0, load_address)) + return SetMissingFieldError(error, "loadAddress", "string integer", module); + + StringRef file; + if (!module.GetValueForKeyAsString("file", file)) + return SetMissingFieldError(error, "file", "string", module); + + FileSpec file_spec(file); + if (file_spec.IsRelative()) + file_spec.PrependPathComponent(m_settings_dir); + + ModuleSpec module_spec; + module_spec.GetFileSpec() = file_spec; + module_spec.GetArchitecture() = GetArchitecture(); + module_spec.SetObjectOffset(load_address); + + StringRef uuid_str; + if (ExtractOptionalStringFromDictionary(module, "uuid", uuid_str, error)) + module_spec.GetUUID().SetFromStringRef(uuid_str); + if (error.Fail()) + return false; + + ModuleSP module_sp = + target_sp->GetOrCreateModule(module_spec, /*notify*/ false, &error); + return error.Success(); +} + +bool Trace::ParseModules(TargetSP &target_sp, + const StructuredData::Dictionary &process, + Status &error) { + StructuredData::Array *modules = nullptr; + if (!process.GetValueForKeyAsArray("modules", modules)) + return SetMissingFieldError(error, "modules", "array", process); + + for (size_t i = 0; i < modules->GetSize(); i++) { + StructuredData::Dictionary *module = nullptr; + if (!ExtractDictionaryFromArray(*modules, i, module, error)) + return false; + if (!ParseModule(target_sp, *module, error)) + return false; + } + return true; +} + +bool Trace::ParseProcess(Debugger &debugger, + const StructuredData::Dictionary &process, + Status &error) { + lldb::pid_t pid; + if (!process.GetValueForKeyAsInteger("pid", pid)) + return SetMissingFieldError(error, "pid", "integer", process); + + TargetSP target_sp; + error = debugger.GetTargetList().CreateTarget( + debugger, /*user_exe_path*/ llvm::StringRef(), + /*triple_str*/ llvm::StringRef(), eLoadDependentsNo, + /*platform_options*/ nullptr, target_sp); + + if (!target_sp) + return false; + + debugger.GetTargetList().SetSelectedTarget(target_sp.get()); + + ProcessSP process_sp(target_sp->CreateProcess( + /*listener*/ nullptr, /*plugin_name*/ llvm::StringRef(), + /*crash_file*/ nullptr)); + process_sp->SetID(pid); + + if (!ParseThreads(process_sp, process, error)) + return false; + + return ParseModules(target_sp, process, error); +} + +bool Trace::ParseProcesses(Debugger &debugger, Status &error) { + StructuredData::Array *processes = nullptr; + if (!m_settings.GetValueForKeyAsArray("processes", processes)) + return SetMissingFieldError(error, "processes", "array", m_settings); + + for (size_t i = 0; i < processes->GetSize(); i++) { + StructuredData::Dictionary *process = nullptr; + if (!ExtractDictionaryFromArray(*processes, i, process, error)) + return false; + if (!ParseProcess(debugger, *process, error)) + return false; + } + return true; +} + +bool Trace::ParseArchitecture(Status &error) { + StringRef triple; + if (!m_settings.GetValueForKeyAsString("triple", triple)) + return SetMissingFieldError(error, "triple", "string", m_settings); + m_arch = ArchSpec(triple); + return true; +} + +bool Trace::ParseSettings(Debugger &debugger, Status &error) { + if (!ParseArchitecture(error)) + return false; + if (!ParseProcesses(debugger, error)) + return false; + return ParsePluginSettings(debugger, error); +} Index: lldb/source/Target/CMakeLists.txt =================================================================== --- lldb/source/Target/CMakeLists.txt +++ lldb/source/Target/CMakeLists.txt @@ -65,6 +65,7 @@ ThreadPlanTracer.cpp ThreadPlanStack.cpp ThreadSpec.cpp + Trace.cpp UnixSignals.cpp UnwindAssembly.cpp UnwindLLDB.cpp Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h =================================================================== --- /dev/null +++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h @@ -0,0 +1,62 @@ +//===-- 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 "lldb/lldb-private.h" +#include <llvm/ADT/Optional.h> + +#include "lldb/Target/Trace.h" + +class TraceIntelPT : public lldb_private::Trace { +public: + void Dump(lldb_private::Stream *s) const override; + + /// Static Functions + /// \{ + static void Initialize(); + + static void Terminate(); + + static lldb::TraceSP + CreateInstance(lldb_private::StructuredData::Dictionary settings, + const char *settings_dir); + + static lldb_private::ConstString GetPluginNameStatic(); + /// \} + + /// PluginInterface protocol + /// \{ + lldb_private::ConstString GetPluginName() override; + + uint32_t GetPluginVersion() override; + /// \} + +protected: + TraceIntelPT(lldb_private::StructuredData::Dictionary settings, + const char *settings_dir) + : Trace(settings, settings_dir) {} + + bool ParsePluginSettings(lldb_private::Debugger &debugger, + lldb_private::Status &error) override; + + const char *GetPluginSchema() override; + +private: + /// Structured data parsing + /// \{ + bool ParsePTCPU(const lldb_private::StructuredData::Dictionary &plugin, + lldb_private::Status &error); + /// \} + + pt_cpu m_pt_cpu; +}; + +#endif // liblldb_TraceIntelPT_h_ Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp =================================================================== --- /dev/null +++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp @@ -0,0 +1,97 @@ +//===-- 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 "lldb/Core/PluginManager.h" + +using namespace lldb; +using namespace lldb_private; + +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; +} + +//------------------------------------------------------------------ +// Structured data parsing +//------------------------------------------------------------------ + +const char *TraceIntelPT::GetPluginSchema() { + return R"({ + "type": "intel-pt", + "pt_cpu": { + "vendor": "intel" | "unknown", + "family": integer, + "model": integer, + "stepping": integer + } +})"; +} + +bool TraceIntelPT::ParsePTCPU(const StructuredData::Dictionary &plugin, + Status &error) { + StructuredData::Dictionary *pt_cpu = nullptr; + if (!plugin.GetValueForKeyAsDictionary("pt_cpu", pt_cpu)) + return SetMissingFieldError(error, "pt_cpu", "dictionary", plugin); + + llvm::StringRef vendor; + if (!pt_cpu->GetValueForKeyAsString("vendor", vendor)) + return SetMissingFieldError(error, "vendor", "string", *pt_cpu); + + uint16_t family; + if (!pt_cpu->GetValueForKeyAsInteger("family", family)) + return SetMissingFieldError(error, "family", "integer", *pt_cpu); + + uint8_t model; + if (!pt_cpu->GetValueForKeyAsInteger("model", model)) + return SetMissingFieldError(error, "model", "integer", *pt_cpu); + + uint8_t stepping; + if (!pt_cpu->GetValueForKeyAsInteger("stepping", stepping)) + return SetMissingFieldError(error, "stepping", "integer", *pt_cpu); + + m_pt_cpu = {vendor.compare("intel") == 0 ? pcv_intel : pcv_unknown, family, + model, stepping}; + return true; +} + +bool TraceIntelPT::ParsePluginSettings(lldb_private::Debugger &debugger, + Status &error) { + StructuredData::Dictionary *plugin = nullptr; + if (!m_settings.GetValueForKeyAsDictionary("plugin", plugin)) + return SetMissingFieldError(error, "plugin", "dictionary", m_settings); + return ParsePTCPU(*plugin, error); +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ + +ConstString TraceIntelPT::GetPluginName() { return GetPluginNameStatic(); } + +uint32_t TraceIntelPT::GetPluginVersion() { return 1; } + +void TraceIntelPT::Dump(lldb_private::Stream *s) const {} + +lldb::TraceSP TraceIntelPT::CreateInstance(StructuredData::Dictionary settings, + const char *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,39 @@ +option(LLDB_BUILD_INTEL_PT "Enable Building of Intel(R) Processor Trace Tool" OFF) + +if (NOT LLDB_BUILD_INTEL_PT) + return() +endif() + +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}) + +if (NOT LIBIPT_LIBRARY_PATH) + find_library(LIBIPT_LIBRARY ipt) +else() + if (NOT EXISTS "${LIBIPT_LIBRARY_PATH}") + message (FATAL_ERROR "invalid libipt library path provided") + endif() + find_library(LIBIPT_LIBRARY ipt PATHS ${LIBIPT_LIBRARY_PATH}) +endif() + +if (NOT LIBIPT_LIBRARY) + message (FATAL_ERROR "libipt library not found") +endif() + +add_lldb_library(lldbPluginTraceIntelPT PLUGIN + TraceIntelPT.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 @@ +add_subdirectory(intel-pt) Index: lldb/source/Plugins/CMakeLists.txt =================================================================== --- lldb/source/Plugins/CMakeLists.txt +++ lldb/source/Plugins/CMakeLists.txt @@ -19,6 +19,7 @@ add_subdirectory(SymbolFile) add_subdirectory(SystemRuntime) add_subdirectory(SymbolVendor) +add_subdirectory(Trace) add_subdirectory(TypeSystem) add_subdirectory(UnwindAssembly) Index: lldb/source/Interpreter/CommandInterpreter.cpp =================================================================== --- lldb/source/Interpreter/CommandInterpreter.cpp +++ lldb/source/Interpreter/CommandInterpreter.cpp @@ -38,6 +38,7 @@ #include "Commands/CommandObjectStats.h" #include "Commands/CommandObjectTarget.h" #include "Commands/CommandObjectThread.h" +#include "Commands/CommandObjectTrace.h" #include "Commands/CommandObjectType.h" #include "Commands/CommandObjectVersion.h" #include "Commands/CommandObjectWatchpoint.h" @@ -511,6 +512,7 @@ REGISTER_COMMAND_OBJECT("statistics", CommandObjectStats); REGISTER_COMMAND_OBJECT("target", CommandObjectMultiwordTarget); REGISTER_COMMAND_OBJECT("thread", CommandObjectMultiwordThread); + REGISTER_COMMAND_OBJECT("trace", CommandObjectTrace); REGISTER_COMMAND_OBJECT("type", CommandObjectType); REGISTER_COMMAND_OBJECT("version", CommandObjectVersion); REGISTER_COMMAND_OBJECT("watchpoint", CommandObjectMultiwordWatchpoint); Index: lldb/source/Core/PluginManager.cpp =================================================================== --- lldb/source/Core/PluginManager.cpp +++ lldb/source/Core/PluginManager.cpp @@ -1005,6 +1005,30 @@ return GetSymbolVendorInstances().GetCallbackAtIndex(idx); } +#pragma mark Trace + +typedef PluginInstance<TraceCreateInstance> TraceInstance; +typedef PluginInstances<TraceInstance> TraceInstances; + +static TraceInstances &GetTraceInstances() { + static TraceInstances g_instances; + return g_instances; +} + +bool PluginManager::RegisterPlugin(ConstString name, const char *description, + TraceCreateInstance create_callback) { + return GetTraceInstances().RegisterPlugin(name, description, create_callback); +} + +bool PluginManager::UnregisterPlugin(TraceCreateInstance create_callback) { + return GetTraceInstances().UnregisterPlugin(create_callback); +} + +TraceCreateInstance +PluginManager::GetTraceCreateCallback(ConstString plugin_name) { + return GetTraceInstances().GetCallbackForName(plugin_name); +} + #pragma mark UnwindAssembly typedef PluginInstance<UnwindAssemblyCreateInstance> UnwindAssemblyInstance; Index: lldb/source/Commands/Options.td =================================================================== --- lldb/source/Commands/Options.td +++ lldb/source/Commands/Options.td @@ -202,8 +202,8 @@ Desc<"Move breakpoints to nearest code. If not set the " "target.move-to-nearest-codesetting is used.">; def breakpoint_set_file_colon_line : Option<"joint-specifier", "y">, Group<12>, Arg<"FileLineColumn">, - Required, Completion<"SourceFile">, - Desc<"A specifier in the form filename:line[:column] for setting file & line breakpoints.">; + Required, Completion<"SourceFile">, + Desc<"A specifier in the form filename:line[:column] for setting file & line breakpoints.">; /* Don't add this option till it actually does something useful... def breakpoint_set_exception_typename : Option<"exception-typename", "O">, Arg<"TypeName">, Desc<"The breakpoint will only stop if an " @@ -751,7 +751,7 @@ def source_list_reverse : Option<"reverse", "r">, Group<4>, Desc<"Reverse the" " listing to look backwards from the last displayed block of source.">; def source_list_file_colon_line : Option<"joint-specifier", "y">, Group<5>, - Arg<"FileLineColumn">, Completion<"SourceFile">, + Arg<"FileLineColumn">, Completion<"SourceFile">, Desc<"A specifier in the form filename:line[:column] from which to display" " source.">; } @@ -1161,3 +1161,19 @@ def watchpoint_delete_force : Option<"force", "f">, Group<1>, Desc<"Delete all watchpoints without querying for confirmation.">; } + +let Command = "trace load" in { + def trace_load_verbose : Option<"verbose", "v">, Group<1>, + Desc<"Show verbose trace load logging for debugging the plug-in " + "implementation.">; +} + +let Command = "trace dump" in { + def trace_dump_verbose : Option<"verbose", "v">, Group<1>, + Desc<"Show verbose trace information.">; +} + +let Command = "trace schema" in { + def trace_schema_verbose : Option<"verbose", "v">, Group<1>, + Desc<"Show verbose trace schema logging for debugging the plug-in.">; +} Index: lldb/source/Commands/CommandObjectTrace.h =================================================================== --- /dev/null +++ lldb/source/Commands/CommandObjectTrace.h @@ -0,0 +1,25 @@ +//===-- CommandObjectTrace.h ----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTTRACE_H +#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTTRACE_H + +#include "lldb/Interpreter/CommandObjectMultiword.h" + +namespace lldb_private { + +class CommandObjectTrace : public CommandObjectMultiword { +public: + CommandObjectTrace(CommandInterpreter &interpreter); + + ~CommandObjectTrace() override; +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTTRACE_H Index: lldb/source/Commands/CommandObjectTrace.cpp =================================================================== --- /dev/null +++ lldb/source/Commands/CommandObjectTrace.cpp @@ -0,0 +1,293 @@ +//===-- 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 "lldb/Core/Debugger.h" +#include "lldb/Host/OptionParser.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Interpreter/OptionGroupFormat.h" +#include "lldb/Interpreter/OptionValueBoolean.h" +#include "lldb/Interpreter/OptionValueLanguage.h" +#include "lldb/Interpreter/OptionValueString.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Target/Trace.h" + +using namespace lldb; +using namespace lldb_private; +using namespace llvm; + +// CommandObjectTraceLoad +#define LLDB_OPTIONS_trace_load +#include "CommandOptions.inc" + +#pragma mark CommandObjectTraceLoad + +class CommandObjectTraceLoad : public CommandObjectParsed { +public: + class CommandOptions : public Options { + public: + CommandOptions() : Options() { OptionParsingStarting(nullptr); } + + ~CommandOptions() override = default; + + Status SetOptionValue(uint32_t option_idx, StringRef option_arg, + ExecutionContext *execution_context) override { + Status error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) { + case 'v': { + m_verbose = true; + break; + } + default: + llvm_unreachable("Unimplemented option"); + } + return error; + } + + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_verbose = false; + } + + ArrayRef<OptionDefinition> GetDefinitions() override { + return makeArrayRef(g_trace_load_options); + } + + bool m_verbose; // Enable verbose logging for debugging purposes. + }; + + CommandObjectTraceLoad(CommandInterpreter &interpreter) + : CommandObjectParsed(interpreter, "trace load", + "Load processor trace data from a JSON file.", + "trace load"), + m_options() {} + + ~CommandObjectTraceLoad() override = default; + + Options *GetOptions() override { return &m_options; } + +protected: + bool DoExecute(Args &command, CommandReturnObject &result) override { + if (command.size() != 1) { + result.AppendError("a single path to a JSON file containing trace " + "information is required"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + FileSpec json_file(command[0].ref()); + Status error; + auto data_sp = StructuredData::ParseJSONFromFile(json_file, error); + if (error.Success()) { + StructuredData::Dictionary *dict = data_sp->GetAsDictionary(); + if (dict) { + if (Expected<lldb::TraceSP> traceOrErr = Trace::FindPlugin( + *dict, json_file.GetDirectory().AsCString())) { + lldb::TraceSP trace_sp = traceOrErr.get(); + if (m_options.m_verbose) + result.AppendMessageWithFormat( + "loading trace with plugin %s", + trace_sp->GetPluginName().AsCString()); + trace_sp->ParseSettings(GetDebugger(), error); + } else { + error.SetErrorString(llvm::toString(traceOrErr.takeError())); + } + } else { + error.SetErrorString("JSON data must be a dictionary"); + } + } + if (error.Success()) { + result.SetStatus(eReturnStatusSuccessFinishResult); + } else { + result.AppendErrorWithFormat("%s\n", error.AsCString()); + result.SetStatus(eReturnStatusFailed); + } + return result.Succeeded(); + } + + CommandOptions m_options; +}; + +// CommandObjectTraceDump +#define LLDB_OPTIONS_trace_dump +#include "CommandOptions.inc" + +#pragma mark CommandObjectTraceDump + +class CommandObjectTraceDump : public CommandObjectParsed { +public: + class CommandOptions : public Options { + public: + CommandOptions() : Options() { OptionParsingStarting(nullptr); } + + ~CommandOptions() override = default; + + Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, + ExecutionContext *execution_context) override { + Status error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) { + case 'v': { + m_verbose = true; + break; + } + default: + llvm_unreachable("Unimplemented option"); + } + return error; + } + + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_verbose = false; + } + + llvm::ArrayRef<OptionDefinition> GetDefinitions() override { + return llvm::makeArrayRef(g_trace_dump_options); + } + + bool m_verbose; // Enable verbose logging for debugging purposes. + }; + + CommandObjectTraceDump(CommandInterpreter &interpreter) + : CommandObjectParsed(interpreter, "trace dump", + "Dump the loaded processor trace data.", + "trace dump"), + m_options() {} + + ~CommandObjectTraceDump() override = default; + + Options *GetOptions() override { return &m_options; } + +protected: + bool DoExecute(Args &command, CommandReturnObject &result) override { + Status error; + // TODO: fill in the dumping code here! + if (error.Success()) { + result.SetStatus(eReturnStatusSuccessFinishResult); + } else { + result.AppendErrorWithFormat("%s\n", error.AsCString()); + result.SetStatus(eReturnStatusFailed); + } + return result.Succeeded(); + } + + CommandOptions m_options; +}; + +// CommandObjectTraceSchema +#define LLDB_OPTIONS_trace_schema +#include "CommandOptions.inc" + +#pragma mark CommandObjectTraceSchema + +class CommandObjectTraceSchema : public CommandObjectParsed { +public: + class CommandOptions : public Options { + public: + CommandOptions() : Options() { OptionParsingStarting(nullptr); } + + ~CommandOptions() override = default; + + Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, + ExecutionContext *execution_context) override { + Status error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) { + case 'v': { + m_verbose = true; + break; + } + default: + llvm_unreachable("Unimplemented option"); + } + return error; + } + + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_verbose = false; + } + + llvm::ArrayRef<OptionDefinition> GetDefinitions() override { + return llvm::makeArrayRef(g_trace_schema_options); + } + + bool m_verbose; // Enable verbose logging for debugging purposes. + }; + + CommandObjectTraceSchema(CommandInterpreter &interpreter) + : CommandObjectParsed(interpreter, "trace schema", + "Show the schema of the given trace plugin.", + "trace schema <plug-in>"), + m_options() {} + + ~CommandObjectTraceSchema() override = default; + + Options *GetOptions() override { return &m_options; } + +protected: + bool DoExecute(Args &command, CommandReturnObject &result) override { + Status error; + if (command.empty()) { + result.SetError( + "trace schema cannot be invoked without a plug-in as argument"); + return false; + } + + const char *plugin_name = command[0].c_str(); + + // We create a fake dictionary with the plugin name in order to create an + // instance of the Trace class. + std::shared_ptr<StructuredData::Dictionary> plugin( + new StructuredData::Dictionary()); + plugin->AddStringItem("type", plugin_name); + StructuredData::Dictionary dict; + dict.AddItem("plugin", plugin); + + if (Expected<lldb::TraceSP> traceOrErr = + Trace::FindPlugin(dict, /*settings_dir*/ "")) { + lldb::TraceSP trace_sp = traceOrErr.get(); + result.AppendMessage(trace_sp->GetSchema()); + } else { + error.SetErrorString(llvm::toString(traceOrErr.takeError())); + } + + if (error.Success()) { + result.SetStatus(eReturnStatusSuccessFinishResult); + } else { + result.AppendErrorWithFormat("%s\n", error.AsCString()); + result.SetStatus(eReturnStatusFailed); + } + return result.Succeeded(); + } + + CommandOptions m_options; +}; + +// CommandObjectTrace + +CommandObjectTrace::CommandObjectTrace(CommandInterpreter &interpreter) + : CommandObjectMultiword(interpreter, "trace", + "Commands for loading and using processor " + "trace information.", + "trace [<sub-command-options>]") { + LoadSubCommand("load", + CommandObjectSP(new CommandObjectTraceLoad(interpreter))); + LoadSubCommand("dump", + CommandObjectSP(new CommandObjectTraceDump(interpreter))); + LoadSubCommand("schema", + CommandObjectSP(new CommandObjectTraceSchema(interpreter))); +} + +CommandObjectTrace::~CommandObjectTrace() = default; Index: lldb/source/Commands/CMakeLists.txt =================================================================== --- lldb/source/Commands/CMakeLists.txt +++ lldb/source/Commands/CMakeLists.txt @@ -29,6 +29,7 @@ CommandObjectStats.cpp CommandObjectTarget.cpp CommandObjectThread.cpp + CommandObjectTrace.cpp CommandObjectType.cpp CommandObjectVersion.cpp CommandObjectWatchpoint.cpp Index: lldb/include/lldb/lldb-private-interfaces.h =================================================================== --- lldb/include/lldb/lldb-private-interfaces.h +++ lldb/include/lldb/lldb-private-interfaces.h @@ -11,6 +11,7 @@ #if defined(__cplusplus) +#include "lldb/Utility/StructuredData.h" #include "lldb/lldb-enumerations.h" #include "lldb/lldb-forward.h" #include "lldb/lldb-private-enumerations.h" @@ -104,6 +105,8 @@ const char *repl_options); typedef int (*ComparisonFunction)(const void *, const void *); typedef void (*DebuggerInitializeCallback)(Debugger &debugger); +typedef lldb::TraceSP (*TraceCreateInstance)(StructuredData::Dictionary info, + const char *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,7 @@ class ThreadPlanStepThrough; class ThreadPlanTracer; class ThreadSpec; +class Trace; class TraceOptions; class Type; class TypeAndOrName; @@ -432,6 +433,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/Trace.h =================================================================== --- /dev/null +++ lldb/include/lldb/Target/Trace.h @@ -0,0 +1,243 @@ +//===-- Trace.h -------------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_TARGET_TRACE_H +#define LLDB_TARGET_TRACE_H + +#include "lldb/Core/PluginInterface.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/StructuredData.h" +#include "lldb/lldb-private.h" +#include "llvm/Support/Error.h" + +namespace lldb_private { + +class StructuredData; + +/// \class Trace Trace.h "lldb/Target/Trace.h" +/// A plug-in interface definition class for trace information. +/// +/// Trace plug-ins allow processor trace information to be loaded into LLDB so +/// that the data can be dumped, used for reverse and forward stepping to allow +/// introspection into the reason your process crashed or found its way to its +/// current state. +/// +/// Trace information can be loaded into a target without a process to allow +/// introspection of the trace information during post mortem analysis, such as +/// when loading core files. +/// +/// Processor trace information can also be fetched through the process +/// interfaces during a live debug session if your process supports gathering +/// this information. +class Trace : public PluginInterface { +public: + ~Trace() override = default; + + /// Dump the trace data that this plug-in has access to. + /// + /// This function will dump all of the trace data for all threads in a user + /// readable format. Options for dumping can be added as this API is iterated + /// on. + /// + /// \param[in] s + /// A stream object to dump the information to. + virtual void Dump(Stream *s) const = 0; + + /// Find a trace plug-in using structured data. + /// + /// When loading trace data from disk, the information for the trace data + /// can be contained in multiple files and require plug-in specific + /// information about the CPU. Using structured data like JSON provides an + /// easy way to specify all of the settings and information that we will need + /// to load trace data into LLDB. This structured data can include: + /// - The plug-in name (this allows a specific plug-in to be selected) + /// - Architecture or target triple + /// - one or more paths to the trace data file on disk + /// - core trace data + /// - thread events or related information + /// - shared library load information to use for this trace data that + /// allows a target to be created so the trace information can be + /// symbolicated so that the trace information can be displayed to the + /// user + /// - shared library path + /// - load address + /// - information on how to fetch the shared library + /// - path to locally cached file on disk + /// - URL to download the file + /// - Any information needed to load the trace file + /// - CPU information + /// - Custom plug-in information needed to decode the trace information + /// correctly. + /// + /// + /// \param[in] settings + /// Structured data in the form of a dictionary. + /// \param[in] settings_dir + /// Path to a directory used to resolve relative paths in the structured + /// data. If the structured data is defined in a file, this should be the + /// folder containing it. + static llvm::Expected<lldb::TraceSP> + FindPlugin(StructuredData::Dictionary settings, const char *settings_dir); + + /// Method that should be invoked to perform the structured data parsing and + /// initialize the tracing information. + /// + /// \param[in] debugger + /// The debugger instance that can provide access to \a Targets, \a Modules, + /// etc. + /// + /// \param[out] error + /// The error object that would contain a reason if there is a failure. + /// + /// \return + /// \b false is there is an error, \b true otherwise. + bool ParseSettings(lldb_private::Debugger &debugger, Status &error); + + const ArchSpec &GetArchitecture() const { return m_arch; } + +protected: + Trace(StructuredData::Dictionary settings, const char *settings_dir) + : m_settings(std::move(settings)), m_settings_dir(settings_dir) {} + + /// Method that should be used by the implementations of this class to do + /// specific structured data parsing and initialize any information they need. + /// + /// \param[in] debugger + /// The debugger instance that can provide access to \a Targets, \a Modules, + /// etc. + /// + /// \param[out] error + /// The error object that would contain a reason if there is a failure. + /// + /// \return + /// \b false is there is an error, \b true otherwise. + virtual bool ParsePluginSettings(lldb_private::Debugger &debugger, + Status &error) = 0; + +private: + Trace(const Trace &) = delete; + const Trace &operator=(const Trace &) = delete; + + /// Structured data parsing + /// \{ +protected: + /// Set a descriptive error message regarding a missing field in a \a + /// StructuredData::Dictionary. + /// + /// \param[out] error + /// The error object where the message is written to. + /// + /// \param[in] field + /// The field name that is missing in the dictionary. + /// + /// \param[in] type + /// The type of the missing field. + /// + /// \param[in] dictionary + /// The dictionary that is missing the provided field. It is printed in + /// the error object to make it more explanatory. + /// + /// \return + /// \b false. + bool SetMissingFieldError(Status &error, const char *field, const char *type, + const StructuredData::Dictionary &dict); + + /// Set a descriptive error message regarding a \a StructuredData::Object with + /// incorrect type. + /// + /// \param[out] error + /// The error object where the message is written to. + /// + /// \param[in] type + /// The expected type. + /// + /// \param[in] object + /// The object that is of the wrong type. It is printed in the error + /// object to make it more explanatory. + /// + /// \return + /// \b false. + bool SetWrongTypeError(Status &error, const char *type, + const StructuredData::Object &object); + + /// Helper function that extracts a dictionary object from an array. It + /// performs syntax check and + /// sets a descriptive error message if the structured data is invalid. + /// + /// \param[in] array + /// The array to extract the dictionary object from. + /// + /// \param[in] index + /// The index of the array to extract the dictionary object from. + /// + /// \param[out] error + /// The error object that would contain a reason if there is a failure. + /// + /// \return + /// \b false is the is an error, \b true otherwise. + bool ExtractDictionaryFromArray(const StructuredData::Array &array, + size_t index, + StructuredData::Dictionary *&dict, + Status &error); + + /// Helper function that extracts an optional string from a dictionary. It + /// performs syntax check and + /// sets a descriptive error message if the structured data is invalid. + /// + /// \param[in] dict + /// The dictionary to extract the string from. + /// + /// \param[in] field + /// The name of the field pointing to the string to extract. + /// + /// \param[out] value + /// The variable where the extracted string will be stored. + /// + /// \param[out] error + /// The error object that would contain a reason if there is a failure. + /// + /// \return + /// \b true if a string was extracted, \b false otherwise. + bool + ExtractOptionalStringFromDictionary(const StructuredData::Dictionary &dict, + const char *field, llvm::StringRef &value, + Status &error); + + virtual const char *GetPluginSchema() = 0; + +private: + bool ParseProcess(lldb_private::Debugger &debugger, + const StructuredData::Dictionary &process, Status &error); + bool ParseProcesses(lldb_private::Debugger &debugger, Status &error); + bool ParseThread(lldb::ProcessSP &process_sp, + const StructuredData::Dictionary &thread, Status &error); + bool ParseThreads(lldb::ProcessSP &process_sp, + const StructuredData::Dictionary &process, Status &error); + bool ParseModule(lldb::TargetSP &target_sp, + const StructuredData::Dictionary &module, Status &error); + bool ParseModules(lldb::TargetSP &target_sp, + const StructuredData::Dictionary &process, Status &error); + bool ParseArchitecture(Status &error); + +public: + const char *GetSchema(); + /// \} + +protected: + // Structured data that holds all settings for this trace session. + StructuredData::Dictionary m_settings; + std::string m_settings_dir; + +private: + lldb_private::ArchSpec m_arch; +}; + +} // namespace lldb_private + +#endif // LLDB_TARGET_TRACE_H Index: lldb/include/lldb/Core/PluginManager.h =================================================================== --- lldb/include/lldb/Core/PluginManager.h +++ lldb/include/lldb/Core/PluginManager.h @@ -330,6 +330,14 @@ static SymbolVendorCreateInstance GetSymbolVendorCreateCallbackAtIndex(uint32_t idx); + // Trace + static bool RegisterPlugin(ConstString name, const char *description, + TraceCreateInstance create_callback); + + static bool UnregisterPlugin(TraceCreateInstance create_callback); + + static TraceCreateInstance GetTraceCreateCallback(ConstString plugin_name); + // UnwindAssembly static bool RegisterPlugin(ConstString name, const char *description, UnwindAssemblyCreateInstance create_callback);
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits