diff --git a/lldb/include/lldb/Core/PluginManager.h b/lldb/include/lldb/Core/PluginManager.h --- a/lldb/include/lldb/Core/PluginManager.h +++ b/lldb/include/lldb/Core/PluginManager.h @@ -369,6 +369,28 @@ /// number plugins, otherwise the actual schema is returned. static llvm::StringRef GetTraceSchema(size_t index); + // TraceExporter + + /// \param[in] create_thread_trace_export_command + /// This callback is used to create a CommandObject that will be listed + /// under "thread trace export". Can be \b null. + static bool RegisterPlugin( + ConstString name, const char *description, + TraceExporterCreateInstance create_callback, + ThreadTraceExportCommandCreator create_thread_trace_export_command); + + static TraceExporterCreateInstance + GetTraceExporterCreateCallback(ConstString plugin_name); + + static bool UnregisterPlugin(TraceExporterCreateInstance create_callback); + + static const char *GetTraceExporterPluginNameAtIndex(uint32_t index); + + /// Return the callback used to create the CommandObject that will be listed + /// under "thread trace export". Can be \b null. + static ThreadTraceExportCommandCreator + GetThreadTraceExportCommandCreatorAtIndex(uint32_t index); + // UnwindAssembly static bool RegisterPlugin(ConstString name, const char *description, UnwindAssemblyCreateInstance create_callback); diff --git a/lldb/include/lldb/Target/TraceExporter.h b/lldb/include/lldb/Target/TraceExporter.h new file mode 100644 --- /dev/null +++ b/lldb/include/lldb/Target/TraceExporter.h @@ -0,0 +1,42 @@ +//===-- TraceExporter.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_EXPORTER_H +#define LLDB_TARGET_TRACE_EXPORTER_H + +#include "lldb/Core/PluginInterface.h" + +namespace lldb_private { + +/// \class TraceExporter TraceExporter.h "lldb/Target/TraceExporter.h" +/// A plug-in interface definition class for trace exporters. +/// +/// Trace exporter plug-ins operate on traces, converting the trace data +/// provided by an \a lldb_private::TraceCursor into a different format that can +/// be digested by other tools, e.g. Chrome Trace Event Profiler. +/// +/// Trace exporters are supposed to operate on an architecture-agnostic fashion, +/// as a TraceCursor, which feeds the data, hides the actual trace technology +/// being used. +class TraceExporter : public PluginInterface { +public: + /// Create an instance of a trace exporter plugin given its name. + /// + /// \param[in] plugin_Name + /// Plug-in name to search. + /// + /// \return + /// A \a TraceExporterUP instance, or an \a llvm::Error if the plug-in + /// name doesn't match any registered plug-ins. + static llvm::Expected + FindPlugin(llvm::StringRef plugin_name); +}; + +} // namespace lldb_private + +#endif // LLDB_TARGET_TRACE_EXPORTER_H diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h --- a/lldb/include/lldb/lldb-forward.h +++ b/lldb/include/lldb/lldb-forward.h @@ -230,6 +230,7 @@ class ThreadPostMortemTrace; class Trace; class TraceCursor; +class TraceExporter; class Type; class TypeAndOrName; class TypeCategoryImpl; @@ -441,6 +442,7 @@ typedef std::weak_ptr ThreadPlanWP; typedef std::shared_ptr ThreadPlanTracerSP; typedef std::shared_ptr TraceSP; +typedef std::unique_ptr TraceExporterUP; typedef std::unique_ptr TraceCursorUP; typedef std::shared_ptr TypeSP; typedef std::weak_ptr TypeWP; diff --git a/lldb/include/lldb/lldb-private-interfaces.h b/lldb/include/lldb/lldb-private-interfaces.h --- a/lldb/include/lldb/lldb-private-interfaces.h +++ b/lldb/include/lldb/lldb-private-interfaces.h @@ -113,11 +113,17 @@ const char *repl_options); typedef int (*ComparisonFunction)(const void *, const void *); typedef void (*DebuggerInitializeCallback)(Debugger &debugger); +/// Trace +/// \{ typedef llvm::Expected (*TraceCreateInstanceForSessionFile)( const llvm::json::Value &trace_session_file, llvm::StringRef session_file_dir, lldb_private::Debugger &debugger); typedef llvm::Expected (*TraceCreateInstanceForLiveProcess)( Process &process); +typedef llvm::Expected (*TraceExporterCreateInstance)(); +typedef lldb::CommandObjectSP (*ThreadTraceExportCommandCreator)( + CommandInterpreter &interpreter); +/// \} } // namespace lldb_private #endif // #if defined(__cplusplus) diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp --- a/lldb/source/Commands/CommandObjectThread.cpp +++ b/lldb/source/Commands/CommandObjectThread.cpp @@ -1924,13 +1924,38 @@ // Next are the subcommands of CommandObjectMultiwordTrace +// CommandObjectTraceExport + +class CommandObjectTraceExport : public CommandObjectMultiword { +public: + CommandObjectTraceExport(CommandInterpreter &interpreter) + : CommandObjectMultiword( + interpreter, "trace thread export", + "Commands for exporting traces of the threads in the current " + "process to different formats.", + "thread trace export []") { + + for (uint32_t i = 0; true; i++) { + if (const char *plugin_name = + PluginManager::GetTraceExporterPluginNameAtIndex(i)) { + if (ThreadTraceExportCommandCreator command_creator = + PluginManager::GetThreadTraceExportCommandCreatorAtIndex(i)) { + LoadSubCommand(plugin_name, command_creator(interpreter)); + } + } else { + break; + } + } + } +}; + // CommandObjectTraceStart class CommandObjectTraceStart : public CommandObjectTraceProxy { public: CommandObjectTraceStart(CommandInterpreter &interpreter) : CommandObjectTraceProxy( - /*live_debug_session_only*/ true, interpreter, "thread trace start", + /*live_debug_session_only=*/true, interpreter, "thread trace start", "Start tracing threads with the corresponding trace " "plug-in for the current process.", "thread trace start []") {} @@ -2248,6 +2273,8 @@ CommandObjectSP(new CommandObjectTraceStart(interpreter))); LoadSubCommand("stop", CommandObjectSP(new CommandObjectTraceStop(interpreter))); + LoadSubCommand("export", + CommandObjectSP(new CommandObjectTraceExport(interpreter))); } ~CommandObjectMultiwordTrace() override = default; diff --git a/lldb/source/Core/PluginManager.cpp b/lldb/source/Core/PluginManager.cpp --- a/lldb/source/Core/PluginManager.cpp +++ b/lldb/source/Core/PluginManager.cpp @@ -1076,6 +1076,59 @@ return llvm::StringRef(); } +#pragma mark TraceExporter + +struct TraceExporterInstance + : public PluginInstance { + TraceExporterInstance( + ConstString name, std::string description, + TraceExporterCreateInstance create_instance, + ThreadTraceExportCommandCreator create_thread_trace_export_command) + : PluginInstance( + name, std::move(description), create_instance), + create_thread_trace_export_command(create_thread_trace_export_command) { + } + + ThreadTraceExportCommandCreator create_thread_trace_export_command; +}; + +typedef PluginInstances TraceExporterInstances; + +static TraceExporterInstances &GetTraceExporterInstances() { + static TraceExporterInstances g_instances; + return g_instances; +} + +bool PluginManager::RegisterPlugin( + ConstString name, const char *description, + TraceExporterCreateInstance create_callback, + ThreadTraceExportCommandCreator create_thread_trace_export_command) { + return GetTraceExporterInstances().RegisterPlugin( + name, description, create_callback, create_thread_trace_export_command); +} + +TraceExporterCreateInstance +PluginManager::GetTraceExporterCreateCallback(ConstString plugin_name) { + return GetTraceExporterInstances().GetCallbackForName(plugin_name); +} + +bool PluginManager::UnregisterPlugin( + TraceExporterCreateInstance create_callback) { + return GetTraceExporterInstances().UnregisterPlugin(create_callback); +} + +ThreadTraceExportCommandCreator +PluginManager::GetThreadTraceExportCommandCreatorAtIndex(uint32_t index) { + if (TraceExporterInstance *instance = + GetTraceExporterInstances().GetInstanceAtIndex(index)) + return instance->create_thread_trace_export_command; + return nullptr; +} + +const char *PluginManager::GetTraceExporterPluginNameAtIndex(uint32_t index) { + return GetTraceExporterInstances().GetNameAtIndex(index); +} + #pragma mark UnwindAssembly typedef PluginInstance UnwindAssemblyInstance; diff --git a/lldb/source/Plugins/CMakeLists.txt b/lldb/source/Plugins/CMakeLists.txt --- a/lldb/source/Plugins/CMakeLists.txt +++ b/lldb/source/Plugins/CMakeLists.txt @@ -20,6 +20,7 @@ add_subdirectory(SystemRuntime) add_subdirectory(SymbolVendor) add_subdirectory(Trace) +add_subdirectory(TraceExporter) add_subdirectory(TypeSystem) add_subdirectory(UnwindAssembly) diff --git a/lldb/source/Plugins/TraceExporter/CMakeLists.txt b/lldb/source/Plugins/TraceExporter/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/TraceExporter/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(ctf) diff --git a/lldb/source/Plugins/TraceExporter/ctf/CMakeLists.txt b/lldb/source/Plugins/TraceExporter/ctf/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/TraceExporter/ctf/CMakeLists.txt @@ -0,0 +1,17 @@ +lldb_tablegen(TraceExporterCTFCommandOptions.inc -gen-lldb-option-defs + SOURCE TraceExporterCTFOptions.td + TARGET TraceExporterCTFOptionsGen) + +add_lldb_library(lldbPluginTraceExporterCTF PLUGIN +CommandObjectThreadTraceExportCTF.cpp + TraceExporterCTF.cpp + + LINK_LIBS + lldbCore + lldbSymbol + lldbTarget + LINK_COMPONENTS + Support + ) + +add_dependencies(lldbPluginTraceExporterCTF TraceExporterCTFOptionsGen) diff --git a/lldb/source/Plugins/TraceExporter/ctf/CommandObjectThreadTraceExportCTF.h b/lldb/source/Plugins/TraceExporter/ctf/CommandObjectThreadTraceExportCTF.h new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/TraceExporter/ctf/CommandObjectThreadTraceExportCTF.h @@ -0,0 +1,56 @@ +//===-- CommandObjectThreadTraceExportCTF.h -------------------*- C++ //-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_COMMANDOBJECTTHREADTRACEEXPORTCTF_H +#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_COMMANDOBJECTTHREADTRACEEXPORTCTF_H + +#include "TraceExporterCTF.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +namespace lldb_private { +namespace ctf { + +class CommandObjectThreadTraceExportCTF : public CommandObjectParsed { +public: + class CommandOptions : public Options { + public: + CommandOptions() : Options() { OptionParsingStarting(nullptr); } + + Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, + ExecutionContext *execution_context) override; + + void OptionParsingStarting(ExecutionContext *execution_context) override; + + llvm::ArrayRef GetDefinitions() override; + + llvm::Optional m_thread_index; + }; + + CommandObjectThreadTraceExportCTF(CommandInterpreter &interpreter) + : CommandObjectParsed( + interpreter, "thread trace export ctf", + "Export a given thread's trace to Chrome Trace Format", + "thread trace export ctf []", + lldb::eCommandRequiresProcess | lldb::eCommandTryTargetAPILock | + lldb::eCommandProcessMustBeLaunched | + lldb::eCommandProcessMustBePaused), + m_options() {} + + Options *GetOptions() override { return &m_options; } + +protected: + bool DoExecute(Args &command, CommandReturnObject &result) override; + + CommandOptions m_options; +}; + +} // namespace ctf +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_COMMANDOBJECTTHREADTRACEEXPORTCTF_H diff --git a/lldb/source/Plugins/TraceExporter/ctf/CommandObjectThreadTraceExportCTF.cpp b/lldb/source/Plugins/TraceExporter/ctf/CommandObjectThreadTraceExportCTF.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/TraceExporter/ctf/CommandObjectThreadTraceExportCTF.cpp @@ -0,0 +1,66 @@ +//===-- CommandObjectThreadTraceExportCTF.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 "CommandObjectThreadTraceExportCTF.h" + +#include "lldb/Host/OptionParser.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::ctf; +using namespace llvm; + +// CommandObjectThreadTraceExportCTF + +#define LLDB_OPTIONS_thread_trace_export_ctf +#include "TraceExporterCTFCommandOptions.inc" + +Status CommandObjectThreadTraceExportCTF::CommandOptions::SetOptionValue( + uint32_t option_idx, llvm::StringRef option_arg, + ExecutionContext *execution_context) { + Status error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) { + case 't': { + int64_t thread_index; + if (option_arg.empty() || option_arg.getAsInteger(0, thread_index) || + thread_index < 0) + error.SetErrorStringWithFormat("invalid integer value for option '%s'", + option_arg.str().c_str()); + else + m_thread_index = thread_index; + break; + } + default: + llvm_unreachable("Unimplemented option"); + } + return error; +} + +void CommandObjectThreadTraceExportCTF::CommandOptions::OptionParsingStarting( + ExecutionContext *execution_context) { + m_thread_index = None; +} + +llvm::ArrayRef +CommandObjectThreadTraceExportCTF::CommandOptions::GetDefinitions() { + return llvm::makeArrayRef(g_thread_trace_export_ctf_options); +} + +bool CommandObjectThreadTraceExportCTF::DoExecute(Args &command, + CommandReturnObject &result) { + Stream &s = result.GetOutputStream(); + // TODO: create an actual instance of the exporter and invoke it + if (m_options.m_thread_index) + s.Printf("got thread index %d\n", (int)m_options.m_thread_index.getValue()); + else + s.Printf("didn't get a thread index\n"); + + return result.Succeeded(); +} diff --git a/lldb/source/Plugins/TraceExporter/ctf/TraceExporterCTF.h b/lldb/source/Plugins/TraceExporter/ctf/TraceExporterCTF.h new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/TraceExporter/ctf/TraceExporterCTF.h @@ -0,0 +1,42 @@ +//===-- TraceExporterCTF.h --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_TRACE_EXPORTER_CTF_H +#define LLDB_SOURCE_PLUGINS_TRACE_EXPORTER_CTF_H + +#include "lldb/Target/TraceExporter.h" + +namespace lldb_private { +namespace ctf { + +/// Trace Exporter Plugin that can produce traces in Chrome Trace Format. +/// Still in development. +class TraceExporterCTF : public TraceExporter { +public: + ~TraceExporterCTF() override = default; + + /// PluginInterface protocol + /// \{ + static llvm::Expected CreateInstance(); + + ConstString GetPluginName() override; + + static void Initialize(); + + static void Terminate(); + + static ConstString GetPluginNameStatic(); + + uint32_t GetPluginVersion() override; + /// \} +}; + +} // namespace ctf +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_TRACE_EXPORTER_CTF_H diff --git a/lldb/source/Plugins/TraceExporter/ctf/TraceExporterCTF.cpp b/lldb/source/Plugins/TraceExporter/ctf/TraceExporterCTF.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/TraceExporter/ctf/TraceExporterCTF.cpp @@ -0,0 +1,53 @@ +//===-- TraceExporterCTF.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 "TraceExporterCTF.h" + +#include + +#include "CommandObjectThreadTraceExportCTF.h" +#include "lldb/Core/PluginManager.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::ctf; +using namespace llvm; + +LLDB_PLUGIN_DEFINE(TraceExporterCTF) + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ + +static CommandObjectSP +GetThreadTraceExportCommand(CommandInterpreter &interpreter) { + return std::make_shared(interpreter); +} + +void TraceExporterCTF::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + "Chrome Trace Format Exporter", CreateInstance, + GetThreadTraceExportCommand); +} + +void TraceExporterCTF::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +ConstString TraceExporterCTF::GetPluginNameStatic() { + static ConstString g_name("ctf"); + return g_name; +} + +ConstString TraceExporterCTF::GetPluginName() { return GetPluginNameStatic(); } + +uint32_t TraceExporterCTF::GetPluginVersion() { return 1; } + +Expected TraceExporterCTF::CreateInstance() { + return std::make_unique(); +} diff --git a/lldb/source/Plugins/TraceExporter/ctf/TraceExporterCTFOptions.td b/lldb/source/Plugins/TraceExporter/ctf/TraceExporterCTFOptions.td new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/TraceExporter/ctf/TraceExporterCTFOptions.td @@ -0,0 +1,9 @@ +include "../../../../source/Commands/OptionsBase.td" + +let Command = "thread trace export ctf" in { + def thread_trace_export_ctf: Option<"tid", "t">, + Group<1>, + Arg<"ThreadIndex">, + Desc<"Export the trace for the specified thread index. Otherwise, the " + "currently selected thread will be used.">; +} diff --git a/lldb/source/Target/CMakeLists.txt b/lldb/source/Target/CMakeLists.txt --- a/lldb/source/Target/CMakeLists.txt +++ b/lldb/source/Target/CMakeLists.txt @@ -68,6 +68,7 @@ ThreadSpec.cpp Trace.cpp TraceCursor.cpp + TraceExporter.cpp TraceInstructionDumper.cpp UnixSignals.cpp UnwindAssembly.cpp diff --git a/lldb/source/Target/TraceExporter.cpp b/lldb/source/Target/TraceExporter.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Target/TraceExporter.cpp @@ -0,0 +1,32 @@ +//===-- TraceExporter.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/TraceExporter.h" + +#include "lldb/Core/PluginManager.h" + +using namespace lldb; +using namespace lldb_private; +using namespace llvm; + +static Error createInvalidPlugInError(StringRef plugin_name) { + return createStringError( + std::errc::invalid_argument, + "no trace expoter plug-in matches the specified type: \"%s\"", + plugin_name.data()); +} + +Expected +TraceExporter::FindPlugin(llvm::StringRef plugin_name) { + ConstString name(plugin_name); + if (auto create_callback = + PluginManager::GetTraceExporterCreateCallback(name)) + return create_callback(); + + return createInvalidPlugInError(plugin_name); +}