Hey Walter, Really excited to see that this landed! I have one post-commit review comment inline --
> On Sep 21, 2020, at 5:13 PM, Walter Erquinigo via lldb-commits > <lldb-commits@lists.llvm.org> wrote: > > > Author: Walter Erquinigo > Date: 2020-09-21T17:13:18-07:00 > New Revision: 74c93956e1c1f1054dfb040ce26830016e0f3095 > > URL: > https://github.com/llvm/llvm-project/commit/74c93956e1c1f1054dfb040ce26830016e0f3095 > DIFF: > https://github.com/llvm/llvm-project/commit/74c93956e1c1f1054dfb040ce26830016e0f3095.diff > > LOG: Add a "Trace" plug-in to LLDB to add process trace support in stages. > > This is the first in a series of patches that will adds a new processor trace > plug-in to LLDB. > > The idea for this first patch to to add the plug-in interface with simple > commands for the trace files that can "load" and "dump" the trace > information. We can test the functionality and ensure people are happy with > the way things are done and how things are organized before moving on to > adding more functionality. > > Processor trace information can be view in a few different ways: > - post mortem where a trace is saved off that can be viewed later in the > debugger > - gathered while a process is running and allow the user to step back in time > (with no variables, memory or registers) to see how each thread arrived at > where it is currently stopped. > > This patch attempts to start with the first solution of loading a trace file > after the fact. The idea is that we will use a JSON file to load the trace > information. JSON allows us to specify information about the trace like: > - plug-in name in LLDB > - path to trace file > - shared library load information so we can re-create a target and > symbolicate the information in the trace > - any other info that the trace plug-in will need to be able to successfully > parse the trace information > - cpu type > - version info > - ??? > > A new "trace" command was added at the top level of the LLDB commmands: > - "trace load" > - "trace dump" > > I did this because if we load trace information we don't need to have a > process and we might end up creating a new target for the trace information > that will become active. If anyone has any input on where this would be > better suited, please let me know. Walter Erquinigo will end up filling in > the Intel PT specific plug-in so that it works and is tested once we can > agree that the direction of this patch is the correct one, so please feel > free to chime in with ideas on comments! > > Reviewed By: clayborg > > Differential Revision: https://reviews.llvm.org/D85705 > > Added: > lldb/include/lldb/Target/Trace.h > lldb/include/lldb/Target/TraceSettingsParser.h > lldb/source/Commands/CommandObjectTrace.cpp > lldb/source/Commands/CommandObjectTrace.h > 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/Trace.cpp > lldb/source/Target/TraceSettingsParser.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 > > Modified: > lldb/include/lldb/Core/PluginManager.h > lldb/include/lldb/lldb-forward.h > lldb/include/lldb/lldb-private-interfaces.h > lldb/source/Commands/CMakeLists.txt > lldb/source/Commands/Options.td > lldb/source/Core/PluginManager.cpp > lldb/source/Interpreter/CommandInterpreter.cpp > lldb/source/Plugins/CMakeLists.txt > lldb/source/Target/CMakeLists.txt > lldb/source/Utility/StructuredData.cpp > > Removed: > > > > ################################################################################ > diff --git a/lldb/include/lldb/Core/PluginManager.h > b/lldb/include/lldb/Core/PluginManager.h > index 5e0c9395dae0..cd962b668163 100644 > --- a/lldb/include/lldb/Core/PluginManager.h > +++ b/lldb/include/lldb/Core/PluginManager.h > @@ -330,6 +330,14 @@ class PluginManager { > 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); > > diff --git a/lldb/include/lldb/Target/Trace.h > b/lldb/include/lldb/Target/Trace.h > new file mode 100644 > index 000000000000..2328fba822a2 > --- /dev/null > +++ b/lldb/include/lldb/Target/Trace.h > @@ -0,0 +1,145 @@ > +//===-- 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; Does this need to be `virtual ~Trace() = ...`? Otherwise, when a std::shared_ptr<Trace> is destroyed, the destructor for the derived TraceIntelPT instance won't run. vedant > + > + /// 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] debugger > + /// The debugger instance were new Target will be created as part of > the > + /// JSON data parsing. > + /// > + /// \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(Debugger &debugger, const llvm::json::Value &settings, > + llvm::StringRef settings_dir); > + > + /// Create an instance of trace plug-in by name. > + /// > + /// \param[in] plugin_name > + /// Name of the trace plugin. > + static llvm::Expected<lldb::TraceSP> FindPlugin(llvm::StringRef > plugin_name); > + > + /// 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. > + /// > + /// \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. > + /// > + /// \return > + /// An error object containing the reason if there is a failure. > + llvm::Error ParseSettings(Debugger &debugger, > + const llvm::json::Object &settings, > + llvm::StringRef settings_dir); > + > + /// 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 implementation of \a > + /// TraceSettingsParser for doing any custom parsing. > + virtual std::unique_ptr<lldb_private::TraceSettingsParser> CreateParser() > = 0; > + > +private: > + Trace(const Trace &) = delete; > + const Trace &operator=(const Trace &) = delete; > + > +protected: > + friend class TraceSettingsParser; > + /// 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; > + > + 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_H > > diff --git a/lldb/include/lldb/Target/TraceSettingsParser.h > b/lldb/include/lldb/Target/TraceSettingsParser.h > new file mode 100644 > index 000000000000..bc18c107ed83 > --- /dev/null > +++ b/lldb/include/lldb/Target/TraceSettingsParser.h > @@ -0,0 +1,136 @@ > +//===-- 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 > +/// A plug-in interface definition class for parsing \a Trace settings. > +/// > +/// 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(Trace &trace) : m_trace(trace) {} > + > + 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. > + /// > + /// \param[in] settings > + /// The settings to parse. > + /// > + /// \param[in] settings_dir > + /// The directory that contains the settings file used to resolve > relative > + /// paths. > + /// > + /// \return > + /// An error object containing the reason if there is a failure. > + llvm::Error ParseSettings(Debugger &debugger, > + const llvm::json::Object &settings, > + llvm::StringRef settings_dir); > + > +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); > + > + Trace &m_trace; > + > +protected: > + /// Objects created as product of the parsing > + /// \{ > + /// 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; > + > + 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 > + > +namespace json_helpers { > +/// JSON parsing helpers based on \a llvm::Expected. > +/// \{ > +llvm::Error CreateWrongTypeError(const llvm::json::Value &value, > + llvm::StringRef type); > + > +llvm::Expected<int64_t> ToIntegerOrError(const llvm::json::Value &value); > + > +llvm::Expected<llvm::StringRef> ToStringOrError(const llvm::json::Value > &value); > + > +llvm::Expected<const llvm::json::Array &> > +ToArrayOrError(const llvm::json::Value &value); > + > +llvm::Expected<const llvm::json::Object &> > +ToObjectOrError(const llvm::json::Value &value); > + > +llvm::Error CreateMissingKeyError(llvm::json::Object obj, llvm::StringRef > key); > + > +llvm::Expected<const llvm::json::Value &> > +GetValueOrError(const llvm::json::Object &obj, llvm::StringRef key); > + > +llvm::Expected<int64_t> GetIntegerOrError(const llvm::json::Object &obj, > + llvm::StringRef key); > + > +llvm::Expected<llvm::StringRef> GetStringOrError(const llvm::json::Object > &obj, > + llvm::StringRef key); > + > +llvm::Expected<const llvm::json::Array &> > +GetArrayOrError(const llvm::json::Object &obj, llvm::StringRef key); > + > +llvm::Expected<const llvm::json::Object &> > +GetObjectOrError(const llvm::json::Object &obj, llvm::StringRef key); > + > +llvm::Expected<llvm::Optional<llvm::StringRef>> > +GetOptionalStringOrError(const llvm::json::Object &obj, llvm::StringRef key); > +/// \} > +} // namespace json_helpers > + > +#endif // LLDB_TARGET_TRACE_SETTINGS_PARSER_H > > diff --git a/lldb/include/lldb/lldb-forward.h > b/lldb/include/lldb/lldb-forward.h > index 6682413820d6..0a7f08c95571 100644 > --- a/lldb/include/lldb/lldb-forward.h > +++ b/lldb/include/lldb/lldb-forward.h > @@ -226,6 +226,8 @@ class ThreadPlanStepRange; > 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::ThreadCollection> > ThreadCollectionSP; > 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; > > diff --git a/lldb/include/lldb/lldb-private-interfaces.h > b/lldb/include/lldb/lldb-private-interfaces.h > index 1568e7a3cb51..9b47b73b772c 100644 > --- a/lldb/include/lldb/lldb-private-interfaces.h > +++ b/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,7 @@ typedef lldb::REPLSP (*REPLCreateInstance)(Status &error, > const char *repl_options); > typedef int (*ComparisonFunction)(const void *, const void *); > typedef void (*DebuggerInitializeCallback)(Debugger &debugger); > +typedef lldb::TraceSP (*TraceCreateInstance)(); > > } // namespace lldb_private > > > diff --git a/lldb/source/Commands/CMakeLists.txt > b/lldb/source/Commands/CMakeLists.txt > index 3e57670fd040..e29a2414d8ff 100644 > --- a/lldb/source/Commands/CMakeLists.txt > +++ b/lldb/source/Commands/CMakeLists.txt > @@ -31,6 +31,7 @@ add_lldb_library(lldbCommands > CommandObjectStats.cpp > CommandObjectTarget.cpp > CommandObjectThread.cpp > + CommandObjectTrace.cpp > CommandObjectType.cpp > CommandObjectVersion.cpp > CommandObjectWatchpoint.cpp > > diff --git a/lldb/source/Commands/CommandObjectTrace.cpp > b/lldb/source/Commands/CommandObjectTrace.cpp > new file mode 100644 > index 000000000000..c622914e0d79 > --- /dev/null > +++ b/lldb/source/Commands/CommandObjectTrace.cpp > @@ -0,0 +1,292 @@ > +//===-- 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( > + GetDebugger(), *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()); > + } 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; > + } > + > + StringRef plugin_name(command[0].c_str()); > + > + if (Expected<lldb::TraceSP> traceOrErr = Trace::FindPlugin(plugin_name)) > { > + 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; > > diff --git a/lldb/source/Commands/CommandObjectTrace.h > b/lldb/source/Commands/CommandObjectTrace.h > new file mode 100644 > index 000000000000..2dca0e26b243 > --- /dev/null > +++ b/lldb/source/Commands/CommandObjectTrace.h > @@ -0,0 +1,25 @@ > +//===-- CommandObjectTrace.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_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 > > diff --git a/lldb/source/Commands/Options.td > b/lldb/source/Commands/Options.td > index b41b1871ad81..f2401dc5f326 100644 > --- a/lldb/source/Commands/Options.td > +++ b/lldb/source/Commands/Options.td > @@ -1175,3 +1175,19 @@ let Command = "watchpoint delete" in { > 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.">; > +} > > diff --git a/lldb/source/Core/PluginManager.cpp > b/lldb/source/Core/PluginManager.cpp > index 3545ef66cc38..e025aa1d3cdb 100644 > --- a/lldb/source/Core/PluginManager.cpp > +++ b/lldb/source/Core/PluginManager.cpp > @@ -1005,6 +1005,30 @@ > PluginManager::GetSymbolVendorCreateCallbackAtIndex(uint32_t idx) { > 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; > > diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp > b/lldb/source/Interpreter/CommandInterpreter.cpp > index 1f6746800097..88b07d5e3a0a 100644 > --- a/lldb/source/Interpreter/CommandInterpreter.cpp > +++ b/lldb/source/Interpreter/CommandInterpreter.cpp > @@ -37,6 +37,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" > @@ -512,6 +513,7 @@ void CommandInterpreter::LoadCommandDictionary() { > 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); > > diff --git a/lldb/source/Plugins/CMakeLists.txt > b/lldb/source/Plugins/CMakeLists.txt > index d91ba749f86c..3da23eca0773 100644 > --- a/lldb/source/Plugins/CMakeLists.txt > +++ b/lldb/source/Plugins/CMakeLists.txt > @@ -19,6 +19,7 @@ add_subdirectory(StructuredData) > add_subdirectory(SymbolFile) > add_subdirectory(SystemRuntime) > add_subdirectory(SymbolVendor) > +add_subdirectory(Trace) > add_subdirectory(TypeSystem) > add_subdirectory(UnwindAssembly) > > > diff --git a/lldb/source/Plugins/Trace/CMakeLists.txt > b/lldb/source/Plugins/Trace/CMakeLists.txt > new file mode 100644 > index 000000000000..edbb5f14b4e6 > --- /dev/null > +++ b/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() > > diff --git a/lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt > b/lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt > new file mode 100644 > index 000000000000..aeaba0cdeb20 > --- /dev/null > +++ b/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 > + ) > > diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp > b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp > new file mode 100644 > index 000000000000..13a990470626 > --- /dev/null > +++ b/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; > +} > + > +std::unique_ptr<lldb_private::TraceSettingsParser> > +TraceIntelPT::CreateParser() { > + return std::make_unique<TraceIntelPTSettingsParser>(*this); > +} > + > +//------------------------------------------------------------------ > +// 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() { > + return lldb::TraceSP(new TraceIntelPT()); > +} > > diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h > b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h > new file mode 100644 > index 000000000000..edc781e08ad4 > --- /dev/null > +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h > @@ -0,0 +1,48 @@ > +//===-- 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(); > + > + static lldb_private::ConstString GetPluginNameStatic(); > + > + uint32_t GetPluginVersion() override; > + /// \} > + > +protected: > + TraceIntelPT() : Trace() {} > + > + std::unique_ptr<lldb_private::TraceSettingsParser> CreateParser() override; > + > +private: > + friend class TraceIntelPTSettingsParser; > + pt_cpu m_pt_cpu; > +}; > + > +#endif // liblldb_TraceIntelPT_h_ > > diff --git > a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSettingsParser.cpp > b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSettingsParser.cpp > new file mode 100644 > index 000000000000..c8f90c9a978d > --- /dev/null > +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSettingsParser.cpp > @@ -0,0 +1,69 @@ > +//===-- 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 = > + json_helpers::GetObjectOrError(trace, "pt_cpu"); > + if (!pt_cpu) > + return pt_cpu.takeError(); > + > + llvm::Expected<llvm::StringRef> vendor = > + json_helpers::GetStringOrError(*pt_cpu, "vendor"); > + if (!vendor) > + return vendor.takeError(); > + > + llvm::Expected<int64_t> family = > + json_helpers::GetIntegerOrError(*pt_cpu, "family"); > + if (!family) > + return family.takeError(); > + > + llvm::Expected<int64_t> model = > + json_helpers::GetIntegerOrError(*pt_cpu, "model"); > + if (!model) > + return model.takeError(); > + > + llvm::Expected<int64_t> stepping = > + json_helpers::GetIntegerOrError(*pt_cpu, "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 = > + json_helpers::GetObjectOrError(m_settings, "trace"); > + if (!trace) > + return trace.takeError(); > + if (llvm::Error err = ParsePTCPU(*trace)) > + return err; > + > + m_trace.m_pt_cpu = m_pt_cpu; > + return llvm::Error::success(); > +} > > diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSettingsParser.h > b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSettingsParser.h > new file mode 100644 > index 000000000000..b8aaa106b10d > --- /dev/null > +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSettingsParser.h > @@ -0,0 +1,39 @@ > +//===-- 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 "TraceIntelPT.h" > +#include "lldb/Target/TraceSettingsParser.h" > +#include "lldb/Utility/StructuredData.h" > + > +class TraceIntelPT; > + > +class TraceIntelPTSettingsParser : public lldb_private::TraceSettingsParser { > + > +public: > + TraceIntelPTSettingsParser(TraceIntelPT &trace) > + : lldb_private::TraceSettingsParser((lldb_private::Trace &)trace), > + m_trace(trace) {} > + > +protected: > + llvm::StringRef GetPluginSchema() override; > + > + llvm::Error ParsePluginSettings() override; > + > +private: > + llvm::Error ParsePTCPU(const llvm::json::Object &trace); > + > + TraceIntelPT &m_trace; > + pt_cpu m_pt_cpu; > +}; > + > +#endif // liblldb_TraceIntelPTSettingsParser_h_ > > diff --git a/lldb/source/Target/CMakeLists.txt > b/lldb/source/Target/CMakeLists.txt > index ca80b5b64778..c3a09c6c0098 100644 > --- a/lldb/source/Target/CMakeLists.txt > +++ b/lldb/source/Target/CMakeLists.txt > @@ -65,6 +65,8 @@ add_lldb_library(lldbTarget > ThreadPlanTracer.cpp > ThreadPlanStack.cpp > ThreadSpec.cpp > + Trace.cpp > + TraceSettingsParser.cpp > UnixSignals.cpp > UnwindAssembly.cpp > UnwindLLDB.cpp > > diff --git a/lldb/source/Target/Trace.cpp b/lldb/source/Target/Trace.cpp > new file mode 100644 > index 000000000000..6a1f8eadee1b > --- /dev/null > +++ b/lldb/source/Target/Trace.cpp > @@ -0,0 +1,78 @@ > +//===-- 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(Debugger &debugger, > + const json::Value &settings, > + StringRef info_dir) { > + llvm::Expected<const json::Object &> settings_obj = > + json_helpers::ToObjectOrError(settings); > + if (!settings_obj) > + return settings_obj.takeError(); > + > + llvm::Expected<const json::Object &> trace = > + json_helpers::GetObjectOrError(*settings_obj, "trace"); > + if (!trace) > + return trace.takeError(); > + > + llvm::Expected<StringRef> type = > + json_helpers::GetStringOrError(*trace, "type"); > + if (!type) > + return type.takeError(); > + > + ConstString plugin_name(*type); > + auto create_callback = PluginManager::GetTraceCreateCallback(plugin_name); > + if (create_callback) { > + TraceSP instance = create_callback(); > + if (llvm::Error err = > + instance->ParseSettings(debugger, *settings_obj, info_dir)) > + return std::move(err); > + return instance; > + } > + > + return createStringError( > + std::errc::invalid_argument, > + "no trace plug-in matches the specified type: \"%s\"", > + plugin_name.AsCString()); > +} > + > +llvm::Expected<lldb::TraceSP> Trace::FindPlugin(StringRef name) { > + ConstString plugin_name(name); > + auto create_callback = PluginManager::GetTraceCreateCallback(plugin_name); > + if (create_callback) > + return create_callback(); > + > + return createStringError( > + std::errc::invalid_argument, > + "no trace plug-in matches the specified type: \"%s\"", > + plugin_name.AsCString()); > +} > + > +llvm::Error Trace::ParseSettings(Debugger &debugger, > + const llvm::json::Object &settings, > + llvm::StringRef settings_dir) { > + if (llvm::Error err = > + CreateParser()->ParseSettings(debugger, settings, settings_dir)) > + return err; > + > + return llvm::Error::success(); > +} > + > +llvm::StringRef Trace::GetSchema() { return CreateParser()->GetSchema(); } > > diff --git a/lldb/source/Target/TraceSettingsParser.cpp > b/lldb/source/Target/TraceSettingsParser.cpp > new file mode 100644 > index 000000000000..09e60bb35a4a > --- /dev/null > +++ b/lldb/source/Target/TraceSettingsParser.cpp > @@ -0,0 +1,351 @@ > +//===-- 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; > + > +namespace json_helpers { > + > +llvm::Error CreateWrongTypeError(const json::Value &value, > + llvm::StringRef type) { > + std::string s; > + llvm::raw_string_ostream os(s); > + os << llvm::formatv("JSON value is expected to be \"{0}\".\nValue:\n{1:2}", > + type, value); > + os.flush(); > + > + return llvm::createStringError(std::errc::invalid_argument, > os.str().c_str()); > +} > + > +llvm::Expected<int64_t> ToIntegerOrError(const json::Value &value) { > + llvm::Optional<int64_t> v = value.getAsInteger(); > + if (v.hasValue()) > + return *v; > + return CreateWrongTypeError(value, "integer"); > +} > + > +llvm::Expected<StringRef> ToStringOrError(const json::Value &value) { > + llvm::Optional<StringRef> v = value.getAsString(); > + if (v.hasValue()) > + return *v; > + return CreateWrongTypeError(value, "string"); > +} > + > +llvm::Expected<const json::Array &> ToArrayOrError(const json::Value &value) > { > + if (const json::Array *v = value.getAsArray()) > + return *v; > + return CreateWrongTypeError(value, "array"); > +} > + > +llvm::Expected<const json::Object &> ToObjectOrError(const json::Value > &value) { > + if (const json::Object *v = value.getAsObject()) > + return *v; > + return CreateWrongTypeError(value, "object"); > +} > + > +llvm::Error CreateMissingKeyError(json::Object obj, llvm::StringRef key) { > + std::string str; > + llvm::raw_string_ostream os(str); > + os << llvm::formatv( > + "JSON object is missing the \"{0}\" field.\nValue:\n{1:2}", key, > + json::Value(std::move(obj))); > + os.flush(); > + > + return llvm::createStringError(std::errc::invalid_argument, > os.str().c_str()); > +} > + > +llvm::Expected<const json::Value &> GetValueOrError(const json::Object &obj, > + StringRef key) { > + if (const json::Value *v = obj.get(key)) > + return *v; > + else > + return CreateMissingKeyError(obj, key); > +} > + > +llvm::Expected<int64_t> GetIntegerOrError(const json::Object &obj, > + StringRef key) { > + if (llvm::Expected<const json::Value &> v = GetValueOrError(obj, key)) > + return ToIntegerOrError(*v); > + else > + return v.takeError(); > +} > + > +llvm::Expected<StringRef> GetStringOrError(const json::Object &obj, > + StringRef key) { > + if (llvm::Expected<const json::Value &> v = GetValueOrError(obj, key)) > + return ToStringOrError(*v); > + else > + return v.takeError(); > +} > + > +llvm::Expected<const json::Array &> GetArrayOrError(const json::Object &obj, > + StringRef key) { > + if (llvm::Expected<const json::Value &> v = GetValueOrError(obj, key)) > + return ToArrayOrError(*v); > + else > + return v.takeError(); > +} > + > +llvm::Expected<const json::Object &> GetObjectOrError(const json::Object > &obj, > + StringRef key) { > + if (llvm::Expected<const json::Value &> v = GetValueOrError(obj, key)) > + return ToObjectOrError(*v); > + else > + return v.takeError(); > +} > + > +llvm::Expected<llvm::Optional<StringRef>> > +GetOptionalStringOrError(const json::Object &obj, StringRef key) { > + if (const json::Value *v = obj.get(key)) > + return ToStringOrError(*v); > + return llvm::None; > +} > + > +} // namespace json_helpers > + > +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 = > + json_helpers::GetIntegerOrError(thread, "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 = > + json_helpers::GetStringOrError(thread, "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 = > + json_helpers::GetArrayOrError(process, "threads"); > + if (!threads) > + return threads.takeError(); > + > + for (const json::Value &thread_val : *threads) { > + llvm::Expected<const json::Object &> thread = > + json_helpers::ToObjectOrError(thread_val); > + 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 = > + json_helpers::GetStringOrError(module, "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 = > + json_helpers::GetStringOrError(module, "systemPath"); > + if (!system_path) > + return system_path.takeError(); > + FileSpec system_file_spec(*system_path); > + NormalizePath(system_file_spec); > + > + llvm::Expected<llvm::Optional<StringRef>> file = > + json_helpers::GetOptionalStringOrError(module, "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 = > + json_helpers::GetOptionalStringOrError(module, "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 = > + json_helpers::GetArrayOrError(process, "modules"); > + if (!modules) > + return modules.takeError(); > + > + for (const json::Value &module_val : *modules) { > + llvm::Expected<const json::Object &> module = > + json_helpers::ToObjectOrError(module_val); > + 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 = json_helpers::GetIntegerOrError(process, > "pid"); > + if (!pid) > + return pid.takeError(); > + > + llvm::Expected<StringRef> triple = > + json_helpers::GetStringOrError(process, "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 = > + json_helpers::GetArrayOrError(m_settings, "processes"); > + if (!processes) > + return processes.takeError(); > + > + for (const json::Value &process_val : *processes) { > + llvm::Expected<const json::Object &> process = > + json_helpers::ToObjectOrError(process_val); > + 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, > + const llvm::json::Object &settings, > + llvm::StringRef settings_dir) { > + m_settings = settings; > + m_settings_dir = settings_dir.str(); > + 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()); > + } > + > + m_trace.m_settings = m_settings; > + m_trace.m_settings_dir = m_settings_dir; > + m_trace.m_thread_to_trace_file_map = m_thread_to_trace_file_map; > + m_trace.m_targets = m_targets; > + > + return llvm::Error::success(); > +} > > diff --git a/lldb/source/Utility/StructuredData.cpp > b/lldb/source/Utility/StructuredData.cpp > index 359c49ae254b..cb26329dc726 100644 > --- a/lldb/source/Utility/StructuredData.cpp > +++ b/lldb/source/Utility/StructuredData.cpp > @@ -42,7 +42,12 @@ StructuredData::ParseJSONFromFile(const FileSpec > &input_spec, Status &error) { > 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) { > > diff --git a/lldb/test/API/commands/trace/TestTraceLoad.py > b/lldb/test/API/commands/trace/TestTraceLoad.py > new file mode 100644 > index 000000000000..f418b8d5a822 > --- /dev/null > +++ b/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) > > diff --git a/lldb/test/API/commands/trace/TestTraceSchema.py > b/lldb/test/API/commands/trace/TestTraceSchema.py > new file mode 100644 > index 000000000000..1549325962ae > --- /dev/null > +++ b/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"']) > > diff --git a/lldb/test/API/commands/trace/intelpt-trace/3842849.trace > b/lldb/test/API/commands/trace/intelpt-trace/3842849.trace > new file mode 100644 > index 000000000000..14f7b5ef4aed > Binary files /dev/null and > b/lldb/test/API/commands/trace/intelpt-trace/3842849.trace > diff er > > diff --git a/lldb/test/API/commands/trace/intelpt-trace/a.out > b/lldb/test/API/commands/trace/intelpt-trace/a.out > new file mode 100755 > index 000000000000..98a27fb532d6 > Binary files /dev/null and b/lldb/test/API/commands/trace/intelpt-trace/a.out > diff er > > diff --git a/lldb/test/API/commands/trace/intelpt-trace/main.cpp > b/lldb/test/API/commands/trace/intelpt-trace/main.cpp > new file mode 100644 > index 000000000000..3e4ad1c0b843 > --- /dev/null > +++ b/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; > +} > > diff --git a/lldb/test/API/commands/trace/intelpt-trace/trace.json > b/lldb/test/API/commands/trace/intelpt-trace/trace.json > new file mode 100644 > index 000000000000..543b71d0cefe > --- /dev/null > +++ b/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" > + } > + ] > + } > + ] > +} > > diff --git a/lldb/test/API/commands/trace/intelpt-trace/trace_bad.json > b/lldb/test/API/commands/trace/intelpt-trace/trace_bad.json > new file mode 100644 > index 000000000000..9d00a82ebcf7 > --- /dev/null > +++ b/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 > + ] > +} > > diff --git a/lldb/test/API/commands/trace/intelpt-trace/trace_bad2.json > b/lldb/test/API/commands/trace/intelpt-trace/trace_bad2.json > new file mode 100644 > index 000000000000..16ece129c8d8 > --- /dev/null > +++ b/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": [] > + } > + ] > +} > > diff --git a/lldb/test/API/commands/trace/intelpt-trace/trace_bad3.json > b/lldb/test/API/commands/trace/intelpt-trace/trace_bad3.json > new file mode 100644 > index 000000000000..dc587a8a4c52 > --- /dev/null > +++ b/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" > + } > + ] > + }, > + {} > + ] > +} > > > > _______________________________________________ > lldb-commits mailing list > lldb-commits@lists.llvm.org > https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits