Author: Walter Erquinigo Date: 2020-10-09T17:32:04-07:00 New Revision: ea1f49741ec4c0a37796b88f6bb8d09d7ab1e8c3
URL: https://github.com/llvm/llvm-project/commit/ea1f49741ec4c0a37796b88f6bb8d09d7ab1e8c3 DIFF: https://github.com/llvm/llvm-project/commit/ea1f49741ec4c0a37796b88f6bb8d09d7ab1e8c3.diff LOG: [intel pt] Refactor parsing With the feedback I was getting in different diffs, I realized that splitting the parsing logic into two classes was not easy to deal with. I do see value in doing that, but I'd rather leave that as a refactor after most of the intel-pt logic is in place. Thus, I'm merging the common parser into the intel pt one, having thus only one that is fully aware of Intel PT during parsing and object creation. Besides, based on the feedback in https://reviews.llvm.org/D88769, I'm creating a ThreadIntelPT class that will be able to orchestrate decoding of its own trace and can handle the stop events correctly. This leaves the TraceIntelPT class as an initialization class that glues together different components. Right now it can initialize a trace session from a json file, and in the future will be able to initialize a trace session from a live process. Besides, I'm renaming SettingsParser to SessionParser, which I think is a better name, as the json object represents a trace session of possibly many processes. With the current set of targets, we have the following - Trace: main interface for dealing with trace sessions - TraceIntelPT: plugin Trace for dealing with intel pt sessions - TraceIntelPTSessionParser: a parser of a json trace session file that can create a corresponding TraceIntelPT instance along with Targets, ProcessTraces (to be created in https://reviews.llvm.org/D88769), and ThreadIntelPT threads. - ProcessTrace: (to be created in https://reviews.llvm.org/D88769) can handle the correct state of the traces as the user traverses the trace. I don't think there'll be a need an intel-pt specific implementation of this class. - ThreadIntelPT: a thread implementation that can handle the decoding of its own trace file, along with keeping track of the current position the user is looking at when doing reverse debugging. Differential Revision: https://reviews.llvm.org/D88841 Added: lldb/include/lldb/Target/TraceSessionFileParser.h lldb/source/Plugins/Trace/intel-pt/ThreadIntelPT.cpp lldb/source/Plugins/Trace/intel-pt/ThreadIntelPT.h lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h lldb/source/Target/TraceSessionFileParser.cpp Modified: 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/CommandObjectTrace.cpp lldb/source/Core/PluginManager.cpp 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/test/API/commands/trace/TestTraceLoad.py lldb/test/API/commands/trace/TestTraceSchema.py Removed: lldb/include/lldb/Target/TraceSettingsParser.h lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSettingsParser.cpp lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSettingsParser.h lldb/source/Target/TraceSettingsParser.cpp ################################################################################ diff --git a/lldb/include/lldb/Core/PluginManager.h b/lldb/include/lldb/Core/PluginManager.h index cd962b668163..77ace59a98b8 100644 --- a/lldb/include/lldb/Core/PluginManager.h +++ b/lldb/include/lldb/Core/PluginManager.h @@ -332,12 +332,35 @@ class PluginManager { // Trace static bool RegisterPlugin(ConstString name, const char *description, - TraceCreateInstance create_callback); + TraceCreateInstance create_callback, + llvm::StringRef schema); static bool UnregisterPlugin(TraceCreateInstance create_callback); static TraceCreateInstance GetTraceCreateCallback(ConstString plugin_name); + /// Get the JSON schema for a trace session file corresponding to the given + /// plugin. + /// + /// \param[in] plugin_name + /// The name of the plugin. + /// + /// \return + /// An empty \a StringRef if no plugin was found with that plugin name, + /// otherwise the actual schema is returned. + static llvm::StringRef GetTraceSchema(ConstString plugin_name); + + /// Get the JSON schema for a trace session file corresponding to the plugin + /// given by its index. + /// + /// \param[in] index + /// The index of the plugin to get the schema of. + /// + /// \return + /// An empty \a StringRef if the index is greater than or equal to the + /// number plugins, otherwise the actual schema is returned. + static llvm::StringRef GetTraceSchema(size_t index); + // 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 index 0aa2da7dbad4..09d18525a558 100644 --- a/lldb/include/lldb/Target/Trace.h +++ b/lldb/include/lldb/Target/Trace.h @@ -12,7 +12,6 @@ #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" @@ -72,70 +71,31 @@ class Trace : public PluginInterface { /// correctly. /// /// \param[in] debugger - /// The debugger instance were new Target will be created as part of the + /// The debugger instance where new Targets will be created as part of the /// JSON data parsing. /// - /// \param[in] settings - /// JSON object describing a trace. + /// \param[in] trace_session_file + /// The contents of the trace session file describing the trace session. + /// See \a TraceSessionFileParser::BuildSchema for more information about + /// the schema of this JSON file. /// - /// \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. + /// \param[in] session_file_dir + /// The path to the directory that contains the session file. It's used to + /// resolved relative paths in the session file. static llvm::Expected<lldb::TraceSP> - FindPlugin(Debugger &debugger, const llvm::json::Value &settings, - llvm::StringRef settings_dir); + FindPlugin(Debugger &debugger, const llvm::json::Value &trace_session_file, + llvm::StringRef session_file_dir); - /// Create an instance of trace plug-in by name. + /// Get the schema of a Trace plug-in given its name. /// /// \param[in] plugin_name /// Name of the trace plugin. - static llvm::Expected<lldb::TraceSP> FindPlugin(llvm::StringRef plugin_name); + static llvm::Expected<llvm::StringRef> + FindPluginSchema(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::Value &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; + /// The JSON schema of this Trace plug-in. + virtual llvm::StringRef GetSchema() = 0; }; } // namespace lldb_private diff --git a/lldb/include/lldb/Target/TraceSessionFileParser.h b/lldb/include/lldb/Target/TraceSessionFileParser.h new file mode 100644 index 000000000000..4bcce9db8406 --- /dev/null +++ b/lldb/include/lldb/Target/TraceSessionFileParser.h @@ -0,0 +1,142 @@ +//===-- TraceSessionFileParser.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_TRACESESSIONPARSER_H +#define LLDB_TARGET_TRACESESSIONPARSER_H + +#include "llvm/Support/JSON.h" + +#include "lldb/lldb-private.h" + +namespace lldb_private { + +/// \class TraceSessionFileParser TraceSessionFileParser.h +/// +/// Base class for parsing the common information of JSON trace session files. +/// Contains the basic C++ structs that represent the JSON data, which include +/// \a JSONTraceSession as the root object. +/// +/// See \a Trace::FindPlugin for more information regarding these JSON files. +class TraceSessionFileParser { +public: + /// C++ structs representing the JSON trace session. + /// \{ + struct JSONAddress { + lldb::addr_t value; + }; + + struct JSONModule { + std::string system_path; + llvm::Optional<std::string> file; + JSONAddress load_address; + llvm::Optional<std::string> uuid; + }; + + struct JSONThread { + int64_t tid; + std::string trace_file; + }; + + struct JSONProcess { + int64_t pid; + std::string triple; + std::vector<JSONThread> threads; + std::vector<JSONModule> modules; + }; + + struct JSONTracePluginSettings { + std::string type; + }; + + /// The trace plug-in implementation should provide its own TPluginSettings, + /// which corresponds to the "trace" section of the schema. + template <class TPluginSettings> struct JSONTraceSession { + std::vector<JSONProcess> processes; + TPluginSettings trace; + }; + /// \} + + TraceSessionFileParser(llvm::StringRef session_file_dir, + llvm::StringRef schema) + : m_session_file_dir(session_file_dir), m_schema(schema) {} + + /// Build the full schema for a Trace plug-in. + /// + /// \param[in] plugin_schema + /// The subschema that corresponds to the "trace" section of the schema. + /// + /// \return + /// The full schema containing the common attributes and the plug-in + /// specific attributes. + static std::string BuildSchema(llvm::StringRef plugin_schema); + +protected: + /// Resolve non-absolute paths relative to the session file folder. It + /// modifies the given file_spec. + void NormalizePath(lldb_private::FileSpec &file_spec); + + llvm::Error ParseModule(lldb::TargetSP &target_sp, const JSONModule &module); + + /// Create a user-friendly error message upon a JSON-parsing failure using the + /// \a json::ObjectMapper functionality. + /// + /// \param[in] root + /// The \a llvm::json::Path::Root used to parse the JSON \a value. + /// + /// \param[in] value + /// The json value that failed to parse. + /// + /// \return + /// An \a llvm::Error containing the user-friendly error message. + llvm::Error CreateJSONError(llvm::json::Path::Root &root, + const llvm::json::Value &value); + + std::string m_session_file_dir; + llvm::StringRef m_schema; +}; +} // namespace lldb_private + +namespace llvm { +namespace json { + +bool fromJSON(const Value &value, + lldb_private::TraceSessionFileParser::JSONAddress &address, + Path path); + +bool fromJSON(const Value &value, + lldb_private::TraceSessionFileParser::JSONModule &module, + Path path); + +bool fromJSON(const Value &value, + lldb_private::TraceSessionFileParser::JSONThread &thread, + Path path); + +bool fromJSON(const Value &value, + lldb_private::TraceSessionFileParser::JSONProcess &process, + Path path); + +bool fromJSON(const Value &value, + lldb_private::TraceSessionFileParser::JSONTracePluginSettings + &plugin_settings, + Path path); + +template <class TPluginSettings> +bool fromJSON( + const Value &value, + lldb_private::TraceSessionFileParser::JSONTraceSession<TPluginSettings> + &session, + Path path) { + ObjectMapper o(value, path); + return o && o.map("trace", session.trace) && + o.map("processes", session.processes); +} + +} // namespace json +} // namespace llvm + +#endif // LLDB_TARGET_TRACESESSIONPARSER_H diff --git a/lldb/include/lldb/Target/TraceSettingsParser.h b/lldb/include/lldb/Target/TraceSettingsParser.h deleted file mode 100644 index 47e6556e1273..000000000000 --- a/lldb/include/lldb/Target/TraceSettingsParser.h +++ /dev/null @@ -1,200 +0,0 @@ -//===-- 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: - struct JSONAddress { - lldb::addr_t value; - }; - - struct JSONModule { - std::string system_path; - llvm::Optional<std::string> file; - JSONAddress load_address; - llvm::Optional<std::string> uuid; - }; - - struct JSONThread { - int64_t tid; - std::string trace_file; - }; - - struct JSONProcess { - int64_t pid; - std::string triple; - std::vector<JSONThread> threads; - std::vector<JSONModule> modules; - }; - - struct JSONTracePluginSettings { - std::string type; - }; - - struct JSONTraceSettings { - std::vector<JSONProcess> processes; - JSONTracePluginSettings trace; - }; - - 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::Value &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. - /// - /// \param[in] plugin_settings - /// The settings to parse specific to the plugin. - /// - /// \return - /// An error object containing the reason if there is a failure. - virtual llvm::Error - ParsePluginSettings(const llvm::json::Value &plugin_settings) = 0; - - /// Create a user-friendly error message upon a JSON-parsing failure using the - /// \a json::ObjectMapper functionality. - /// - /// \param[in] root - /// The \a llvm::json::Path::Root used to parse the JSON \a value. - /// - /// \param[in] value - /// The json value that failed to parse. - /// - /// \return - /// An \a llvm::Error containing the user-friendly error message. - llvm::Error CreateJSONError(llvm::json::Path::Root &root, - const llvm::json::Value &value); - -private: - /// Resolve non-absolute paths relativeto the settings folder - void NormalizePath(lldb_private::FileSpec &file_spec); - - llvm::Error ParseProcess(lldb_private::Debugger &debugger, - const JSONProcess &process); - void ParseThread(lldb::ProcessSP &process_sp, const JSONThread &thread); - llvm::Error ParseModule(lldb::TargetSP &target_sp, const JSONModule &module); - llvm::Error ParseSettingsImpl(lldb_private::Debugger &debugger, - const llvm::json::Value &settings); - - Trace &m_trace; - -protected: - /// Objects created as product of the parsing - /// \{ - /// 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 llvm { -namespace json { - -inline bool fromJSON(const llvm::json::Value &value, - lldb_private::TraceSettingsParser::JSONAddress &address, - llvm::json::Path path) { - llvm::Optional<llvm::StringRef> s = value.getAsString(); - if (s.hasValue() && !s->getAsInteger(0, address.value)) - return true; - - path.report("expected numeric string"); - return false; -} - -inline bool fromJSON(const llvm::json::Value &value, - lldb_private::TraceSettingsParser::JSONModule &module, - llvm::json::Path path) { - llvm::json::ObjectMapper o(value, path); - return o && o.map("systemPath", module.system_path) && - o.map("file", module.file) && - o.map("loadAddress", module.load_address) && - o.map("uuid", module.uuid); -} - -inline bool fromJSON(const llvm::json::Value &value, - lldb_private::TraceSettingsParser::JSONThread &thread, - llvm::json::Path path) { - llvm::json::ObjectMapper o(value, path); - return o && o.map("tid", thread.tid) && o.map("traceFile", thread.trace_file); -} - -inline bool fromJSON(const llvm::json::Value &value, - lldb_private::TraceSettingsParser::JSONProcess &process, - llvm::json::Path path) { - llvm::json::ObjectMapper o(value, path); - return o && o.map("pid", process.pid) && o.map("triple", process.triple) && - o.map("threads", process.threads) && o.map("modules", process.modules); -} - -inline bool fromJSON( - const llvm::json::Value &value, - lldb_private::TraceSettingsParser::JSONTracePluginSettings &plugin_settings, - llvm::json::Path path) { - llvm::json::ObjectMapper o(value, path); - return o && o.map("type", plugin_settings.type); -} - -inline bool -fromJSON(const llvm::json::Value &value, - lldb_private::TraceSettingsParser::JSONTraceSettings &settings, - llvm::json::Path path) { - llvm::json::ObjectMapper o(value, path); - return o && o.map("trace", settings.trace) && - o.map("processes", settings.processes); -} - -} // namespace json -} // namespace llvm - -#endif // LLDB_TARGET_TRACE_SETTINGS_PARSER_H diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h index 0a7f08c95571..d7f476af8f0f 100644 --- a/lldb/include/lldb/lldb-forward.h +++ b/lldb/include/lldb/lldb-forward.h @@ -227,7 +227,7 @@ class ThreadPlanStepThrough; class ThreadPlanTracer; class ThreadSpec; class Trace; -class TraceSettingsParser; +class TraceSessionFileParser; class TraceOptions; class Type; class TypeAndOrName; diff --git a/lldb/include/lldb/lldb-private-interfaces.h b/lldb/include/lldb/lldb-private-interfaces.h index 9b47b73b772c..1938de58af44 100644 --- a/lldb/include/lldb/lldb-private-interfaces.h +++ b/lldb/include/lldb/lldb-private-interfaces.h @@ -21,6 +21,7 @@ namespace llvm { namespace json { class Object; +class Value; } } // namespace llvm @@ -110,7 +111,9 @@ 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)(); +typedef llvm::Expected<lldb::TraceSP> (*TraceCreateInstance)( + const llvm::json::Value &trace_session_file, + llvm::StringRef session_file_dir, lldb_private::Debugger &debugger); } // namespace lldb_private diff --git a/lldb/source/Commands/CommandObjectTrace.cpp b/lldb/source/Commands/CommandObjectTrace.cpp index c622914e0d79..170630b85b2e 100644 --- a/lldb/source/Commands/CommandObjectTrace.cpp +++ b/lldb/source/Commands/CommandObjectTrace.cpp @@ -12,6 +12,7 @@ #include "llvm/Support/MemoryBuffer.h" #include "lldb/Core/Debugger.h" +#include "lldb/Core/PluginManager.h" #include "lldb/Host/OptionParser.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandObject.h" @@ -71,7 +72,7 @@ class CommandObjectTraceLoad : public CommandObjectParsed { CommandObjectTraceLoad(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "trace load", - "Load processor trace data from a JSON file.", + "Load a processor trace session from a JSON file.", "trace load"), m_options() {} @@ -82,8 +83,9 @@ class CommandObjectTraceLoad : public CommandObjectParsed { 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.AppendError( + "a single path to a JSON file containing a trace session" + "is required"); result.SetStatus(eReturnStatusFailed); return false; } @@ -105,13 +107,14 @@ class CommandObjectTraceLoad : public CommandObjectParsed { buffer_or_error.getError().message().c_str())); } - llvm::Expected<json::Value> settings = + llvm::Expected<json::Value> session_file = json::parse(buffer_or_error.get()->getBuffer().str()); - if (!settings) - return end_with_failure(settings.takeError()); + if (!session_file) + return end_with_failure(session_file.takeError()); - if (Expected<lldb::TraceSP> traceOrErr = Trace::FindPlugin( - GetDebugger(), *settings, json_file.GetDirectory().AsCString())) { + if (Expected<lldb::TraceSP> traceOrErr = + Trace::FindPlugin(GetDebugger(), *session_file, + json_file.GetDirectory().AsCString())) { lldb::TraceSP trace_sp = traceOrErr.get(); if (m_options.m_verbose) result.AppendMessageWithFormat("loading trace with plugin %s\n", @@ -237,7 +240,8 @@ class CommandObjectTraceSchema : public CommandObjectParsed { CommandObjectTraceSchema(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "trace schema", "Show the schema of the given trace plugin.", - "trace schema <plug-in>"), + "trace schema <plug-in>. Use the plug-in name " + "\"all\" to see all schemas.\n"), m_options() {} ~CommandObjectTraceSchema() override = default; @@ -254,12 +258,21 @@ class CommandObjectTraceSchema : public CommandObjectParsed { } 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()); + if (plugin_name == "all") { + size_t index = 0; + while (true) { + StringRef schema = PluginManager::GetTraceSchema(index++); + if (schema.empty()) + break; + + result.AppendMessage(schema); + } } else { - error.SetErrorString(llvm::toString(traceOrErr.takeError())); + if (Expected<StringRef> schemaOrErr = + Trace::FindPluginSchema(plugin_name)) + result.AppendMessage(*schemaOrErr); + else + error = schemaOrErr.takeError(); } if (error.Success()) { diff --git a/lldb/source/Core/PluginManager.cpp b/lldb/source/Core/PluginManager.cpp index e025aa1d3cdb..1f6487cfe77f 100644 --- a/lldb/source/Core/PluginManager.cpp +++ b/lldb/source/Core/PluginManager.cpp @@ -264,12 +264,13 @@ template <typename Instance> class PluginInstances { const std::vector<Instance> &GetInstances() const { return m_instances; } std::vector<Instance> &GetInstances() { return m_instances; } -private: Instance *GetInstanceAtIndex(uint32_t idx) { if (idx < m_instances.size()) return &m_instances[idx]; return nullptr; } + +private: std::vector<Instance> m_instances; }; @@ -1007,7 +1008,16 @@ PluginManager::GetSymbolVendorCreateCallbackAtIndex(uint32_t idx) { #pragma mark Trace -typedef PluginInstance<TraceCreateInstance> TraceInstance; +struct TraceInstance : public PluginInstance<TraceCreateInstance> { + TraceInstance(ConstString name, std::string description, + CallbackType create_callback, llvm::StringRef schema) + : PluginInstance<TraceCreateInstance>(name, std::move(description), + create_callback), + schema(schema) {} + + llvm::StringRef schema; +}; + typedef PluginInstances<TraceInstance> TraceInstances; static TraceInstances &GetTraceInstances() { @@ -1016,8 +1026,10 @@ static TraceInstances &GetTraceInstances() { } bool PluginManager::RegisterPlugin(ConstString name, const char *description, - TraceCreateInstance create_callback) { - return GetTraceInstances().RegisterPlugin(name, description, create_callback); + TraceCreateInstance create_callback, + llvm::StringRef schema) { + return GetTraceInstances().RegisterPlugin(name, description, create_callback, + schema); } bool PluginManager::UnregisterPlugin(TraceCreateInstance create_callback) { @@ -1029,6 +1041,19 @@ PluginManager::GetTraceCreateCallback(ConstString plugin_name) { return GetTraceInstances().GetCallbackForName(plugin_name); } +llvm::StringRef PluginManager::GetTraceSchema(ConstString plugin_name) { + for (const TraceInstance &instance : GetTraceInstances().GetInstances()) + if (instance.name == plugin_name) + return instance.schema; + return llvm::StringRef(); +} + +llvm::StringRef PluginManager::GetTraceSchema(size_t index) { + if (TraceInstance *instance = GetTraceInstances().GetInstanceAtIndex(index)) + return instance->schema; + return llvm::StringRef(); +} + #pragma mark UnwindAssembly typedef PluginInstance<UnwindAssemblyCreateInstance> UnwindAssemblyInstance; diff --git a/lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt b/lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt index aeaba0cdeb20..5b3091926d37 100644 --- a/lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt +++ b/lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt @@ -11,7 +11,8 @@ find_library(LIBIPT_LIBRARY ipt PATHS ${LIBIPT_LIBRARY_PATH} REQUIRED) add_lldb_library(lldbPluginTraceIntelPT PLUGIN TraceIntelPT.cpp - TraceIntelPTSettingsParser.cpp + TraceIntelPTSessionFileParser.cpp + ThreadIntelPT.cpp LINK_LIBS lldbCore diff --git a/lldb/source/Plugins/Trace/intel-pt/ThreadIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/ThreadIntelPT.cpp new file mode 100644 index 000000000000..05650018bfd1 --- /dev/null +++ b/lldb/source/Plugins/Trace/intel-pt/ThreadIntelPT.cpp @@ -0,0 +1,38 @@ +//===-- ThreadIntelPT.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 "ThreadIntelPT.h" + +#include <memory> + +#include "Plugins/Process/Utility/RegisterContextHistory.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::trace_intel_pt; + +void ThreadIntelPT::RefreshStateAfterStop() {} + +RegisterContextSP ThreadIntelPT::GetRegisterContext() { + if (!m_reg_context_sp) + m_reg_context_sp = CreateRegisterContextForFrame(nullptr); + + return m_reg_context_sp; +} + +RegisterContextSP +ThreadIntelPT::CreateRegisterContextForFrame(StackFrame *frame) { + // Eventually this will calculate the register context based on the current + // trace position. + return std::make_shared<RegisterContextHistory>( + *this, 0, GetProcess()->GetAddressByteSize(), LLDB_INVALID_ADDRESS); +} + +bool ThreadIntelPT::CalculateStopInfo() { return false; } diff --git a/lldb/source/Plugins/Trace/intel-pt/ThreadIntelPT.h b/lldb/source/Plugins/Trace/intel-pt/ThreadIntelPT.h new file mode 100644 index 000000000000..ad6b772e2b28 --- /dev/null +++ b/lldb/source/Plugins/Trace/intel-pt/ThreadIntelPT.h @@ -0,0 +1,54 @@ +//===-- ThreadIntelPT.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_THREADINTELPT_H +#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_THREADINTELPT_H + +#include "lldb/Target/Thread.h" + +namespace lldb_private { +namespace trace_intel_pt { + +class ThreadIntelPT : public Thread { +public: + /// Create an Intel PT-traced thread. + /// + /// \param[in] process + /// The process that owns this thread. + /// + /// \param[in] tid + /// The thread id of this thread. + /// + /// \param[in] trace_file + /// The trace file for this thread. + /// + /// \param[in] pt_cpu + /// The Intel CPU information required to decode the \a trace_file. + ThreadIntelPT(Process &process, lldb::tid_t tid, const FileSpec &trace_file) + : Thread(process, tid), m_trace_file(trace_file) {} + + void RefreshStateAfterStop() override; + + lldb::RegisterContextSP GetRegisterContext() override; + + lldb::RegisterContextSP + CreateRegisterContextForFrame(StackFrame *frame) override; + +protected: + bool CalculateStopInfo() override; + + lldb::RegisterContextSP m_thread_reg_ctx_sp; + +private: + FileSpec m_trace_file; +}; + +} // namespace trace_intel_pt +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_THREADINTELPT_H diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp index 13a990470626..c4e38e668c25 100644 --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp @@ -8,18 +8,20 @@ #include "TraceIntelPT.h" -#include "TraceIntelPTSettingsParser.h" +#include "TraceIntelPTSessionFileParser.h" #include "lldb/Core/PluginManager.h" using namespace lldb; using namespace lldb_private; +using namespace lldb_private::trace_intel_pt; using namespace llvm; -LLDB_PLUGIN_DEFINE_ADV(TraceIntelPT, TraceIntelPT) +LLDB_PLUGIN_DEFINE(TraceIntelPT) void TraceIntelPT::Initialize() { PluginManager::RegisterPlugin(GetPluginNameStatic(), "Intel Processor Trace", - CreateInstance); + CreateInstance, + TraceIntelPTSessionFileParser::GetSchema()); } void TraceIntelPT::Terminate() { @@ -31,9 +33,8 @@ ConstString TraceIntelPT::GetPluginNameStatic() { return g_name; } -std::unique_ptr<lldb_private::TraceSettingsParser> -TraceIntelPT::CreateParser() { - return std::make_unique<TraceIntelPTSettingsParser>(*this); +StringRef TraceIntelPT::GetSchema() { + return TraceIntelPTSessionFileParser::GetSchema(); } //------------------------------------------------------------------ @@ -46,6 +47,10 @@ uint32_t TraceIntelPT::GetPluginVersion() { return 1; } void TraceIntelPT::Dump(lldb_private::Stream *s) const {} -lldb::TraceSP TraceIntelPT::CreateInstance() { - return lldb::TraceSP(new TraceIntelPT()); +Expected<lldb::TraceSP> +TraceIntelPT::CreateInstance(const json::Value &trace_session_file, + StringRef session_file_dir, Debugger &debugger) { + return TraceIntelPTSessionFileParser(debugger, trace_session_file, + session_file_dir) + .Parse(); } diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h index d221caff3c18..ac8bccd0d932 100644 --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h @@ -6,45 +6,71 @@ // //===----------------------------------------------------------------------===// -#ifndef liblldb_TraceIntelPT_h_ -#define liblldb_TraceIntelPT_h_ +#ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPT_H +#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPT_H #include "intel-pt.h" #include "llvm/ADT/Optional.h" -#include "TraceIntelPTSettingsParser.h" +#include "TraceIntelPTSessionFileParser.h" #include "lldb/Target/Trace.h" #include "lldb/lldb-private.h" -class TraceIntelPT : public lldb_private::Trace { +namespace lldb_private { +namespace trace_intel_pt { + +class TraceIntelPT : public Trace { public: - void Dump(lldb_private::Stream *s) const override; + void Dump(Stream *s) const override; ~TraceIntelPT() override = default; /// PluginInterface protocol /// \{ - lldb_private::ConstString GetPluginName() override; + ConstString GetPluginName() override; static void Initialize(); static void Terminate(); - static lldb::TraceSP CreateInstance(); + /// Create an instance of this class. + /// + /// \param[in] trace_session_file + /// The contents of the trace session file. See \a Trace::FindPlugin. + /// + /// \param[in] session_file_dir + /// The path to the directory that contains the session file. It's used to + /// resolved relative paths in the session file. + /// + /// \param[in] debugger + /// The debugger instance where new Targets will be created as part of the + /// JSON data parsing. + /// + /// \return + /// A trace instance or an error in case of failures. + static llvm::Expected<lldb::TraceSP> + CreateInstance(const llvm::json::Value &trace_session_file, + llvm::StringRef session_file_dir, Debugger &debugger); - static lldb_private::ConstString GetPluginNameStatic(); + static ConstString GetPluginNameStatic(); uint32_t GetPluginVersion() override; /// \} -protected: - TraceIntelPT() : Trace() {} + llvm::StringRef GetSchema() override; - std::unique_ptr<lldb_private::TraceSettingsParser> CreateParser() override; + TraceIntelPT(const pt_cpu &pt_cpu, const std::vector<lldb::TargetSP> &targets) + : m_pt_cpu(pt_cpu) { + for (const lldb::TargetSP &target_sp : targets) + m_targets.push_back(target_sp); + } private: - friend class TraceIntelPTSettingsParser; pt_cpu m_pt_cpu; + std::vector<std::weak_ptr<Target>> m_targets; }; -#endif // liblldb_TraceIntelPT_h_ +} // namespace trace_intel_pt +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPT_H diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp new file mode 100644 index 000000000000..33db8facdcec --- /dev/null +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp @@ -0,0 +1,135 @@ +//===-- TraceIntelPTSessionFileParser.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 "TraceIntelPTSessionFileParser.h" + +#include "ThreadIntelPT.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::trace_intel_pt; +using namespace llvm; + +StringRef TraceIntelPTSessionFileParser::GetSchema() { + static std::string schema; + if (schema.empty()) { + schema = TraceSessionFileParser::BuildSchema(R"({ + "type": "intel-pt", + "pt_cpu": { + "vendor": "intel" | "unknown", + "family": integer, + "model": integer, + "stepping": integer + } + })"); + } + return schema; +} + +void TraceIntelPTSessionFileParser::ParseThread( + ProcessSP &process_sp, const TraceSessionFileParser::JSONThread &thread) { + lldb::tid_t tid = static_cast<lldb::tid_t>(thread.tid); + + FileSpec trace_file(thread.trace_file); + NormalizePath(trace_file); + + ThreadSP thread_sp(new ThreadIntelPT(*process_sp, tid, trace_file)); + process_sp->GetThreadList().AddThread(thread_sp); +} + +Error TraceIntelPTSessionFileParser::ParseProcess( + const TraceSessionFileParser::JSONProcess &process) { + TargetSP target_sp; + Status error = m_debugger.GetTargetList().CreateTarget( + m_debugger, /*user_exe_path*/ StringRef(), process.triple, + eLoadDependentsNo, + /*platform_options*/ nullptr, target_sp); + + if (!target_sp) + return error.ToError(); + + m_targets.push_back(target_sp); + m_debugger.GetTargetList().SetSelectedTarget(target_sp.get()); + + ProcessSP process_sp(target_sp->CreateProcess( + /*listener*/ nullptr, /*plugin_name*/ StringRef(), + /*crash_file*/ nullptr)); + process_sp->SetID(static_cast<lldb::pid_t>(process.pid)); + + for (const TraceSessionFileParser::JSONThread &thread : process.threads) + ParseThread(process_sp, thread); + + for (const TraceSessionFileParser::JSONModule &module : process.modules) { + if (Error err = ParseModule(target_sp, module)) + return err; + } + return Error::success(); +} + +void TraceIntelPTSessionFileParser::ParsePTCPU(const JSONPTCPU &pt_cpu) { + m_pt_cpu = {pt_cpu.vendor.compare("intel") == 0 ? pcv_intel : pcv_unknown, + static_cast<uint16_t>(pt_cpu.family), + static_cast<uint8_t>(pt_cpu.model), + static_cast<uint8_t>(pt_cpu.stepping)}; +} + +Error TraceIntelPTSessionFileParser::ParseImpl() { + json::Path::Root root("traceSession"); + TraceSessionFileParser::JSONTraceSession<JSONTraceIntelPTSettings> session; + if (!json::fromJSON(m_trace_session_file, session, root)) { + return CreateJSONError(root, m_trace_session_file); + } + + ParsePTCPU(session.trace.pt_cpu); + for (const TraceSessionFileParser::JSONProcess &process : session.processes) { + if (Error err = ParseProcess(process)) + return err; + } + return Error::success(); +} + +Expected<TraceSP> TraceIntelPTSessionFileParser::Parse() { + if (Error err = ParseImpl()) { + // Delete all targets that were created + for (auto target_sp : m_targets) + m_debugger.GetTargetList().DeleteTarget(target_sp); + m_targets.clear(); + return std::move(err); + } + + return std::make_shared<TraceIntelPT>(m_pt_cpu, m_targets); +} + +namespace llvm { +namespace json { + +bool fromJSON(const Value &value, + TraceIntelPTSessionFileParser::JSONPTCPU &pt_cpu, Path path) { + ObjectMapper o(value, path); + return o && o.map("vendor", pt_cpu.vendor) && + o.map("family", pt_cpu.family) && o.map("model", pt_cpu.model) && + o.map("stepping", pt_cpu.stepping); +} + +bool fromJSON( + const Value &value, + TraceIntelPTSessionFileParser::JSONTraceIntelPTSettings &plugin_settings, + Path path) { + ObjectMapper o(value, path); + return o && o.map("pt_cpu", plugin_settings.pt_cpu) && + fromJSON( + value, + (TraceSessionFileParser::JSONTracePluginSettings &)plugin_settings, + path); +} + +} // namespace json +} // namespace llvm diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h new file mode 100644 index 000000000000..64f620042ef7 --- /dev/null +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h @@ -0,0 +1,96 @@ +//===-- TraceIntelPTSessionFileParser.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_TRACEINTELPTSESSIONFILEPARSER_H +#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTSESSIONFILEPARSER_H + +#include "intel-pt.h" + +#include "TraceIntelPT.h" +#include "lldb/Target/TraceSessionFileParser.h" + +namespace lldb_private { +namespace trace_intel_pt { + +class TraceIntelPT; + +class TraceIntelPTSessionFileParser : public TraceSessionFileParser { +public: + struct JSONPTCPU { + std::string vendor; + int64_t family; + int64_t model; + int64_t stepping; + }; + + struct JSONTraceIntelPTSettings + : TraceSessionFileParser::JSONTracePluginSettings { + JSONPTCPU pt_cpu; + }; + + /// See \a TraceSessionFileParser::TraceSessionFileParser for the description + /// of these fields. + TraceIntelPTSessionFileParser(Debugger &debugger, + const llvm::json::Value &trace_session_file, + llvm::StringRef session_file_dir) + : TraceSessionFileParser(session_file_dir, GetSchema()), + m_debugger(debugger), m_trace_session_file(trace_session_file) {} + + /// \return + /// The JSON schema for the session data. + static llvm::StringRef GetSchema(); + + /// Parse the structured data trace session and create the corresponding \a + /// Target objects. In case of an error, no targets are created. + /// + /// \return + /// A \a lldb::TraceSP instance with the trace session data. In case of + /// errors, return a null pointer. + llvm::Expected<lldb::TraceSP> Parse(); + +private: + llvm::Error ParseImpl(); + + llvm::Error ParseProcess(const TraceSessionFileParser::JSONProcess &process); + + void ParseThread(lldb::ProcessSP &process_sp, + const TraceSessionFileParser::JSONThread &thread); + + void ParsePTCPU(const JSONPTCPU &pt_cpu); + + Debugger &m_debugger; + const llvm::json::Value &m_trace_session_file; + + /// Objects created as product of the parsing + /// \{ + pt_cpu m_pt_cpu; + std::vector<lldb::TargetSP> m_targets; + /// \} +}; + +} // namespace trace_intel_pt +} // namespace lldb_private + +namespace llvm { +namespace json { + +bool fromJSON( + const Value &value, + lldb_private::trace_intel_pt::TraceIntelPTSessionFileParser::JSONPTCPU + &pt_cpu, + Path path); + +bool fromJSON(const Value &value, + lldb_private::trace_intel_pt::TraceIntelPTSessionFileParser:: + JSONTraceIntelPTSettings &plugin_settings, + Path path); + +} // namespace json +} // namespace llvm + +#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTSESSIONFILEPARSER_H diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSettingsParser.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSettingsParser.cpp deleted file mode 100644 index c67822d49155..000000000000 --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSettingsParser.cpp +++ /dev/null @@ -1,45 +0,0 @@ -//===-- 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 - } - })"; -} - -void TraceIntelPTSettingsParser::ParsePTCPU(const JSONPTCPU &pt_cpu) { - m_pt_cpu = {pt_cpu.vendor.compare("intel") == 0 ? pcv_intel : pcv_unknown, - static_cast<uint16_t>(pt_cpu.family), - static_cast<uint8_t>(pt_cpu.model), - static_cast<uint8_t>(pt_cpu.stepping)}; -} - -llvm::Error TraceIntelPTSettingsParser::ParsePluginSettings( - const llvm::json::Value &plugin_settings) { - json::Path::Root root("settings.trace"); - JSONIntelPTSettings settings; - if (!json::fromJSON(plugin_settings, settings, root)) - return CreateJSONError(root, plugin_settings); - - ParsePTCPU(settings.pt_cpu); - - 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 deleted file mode 100644 index 8c9b9ea89829..000000000000 --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSettingsParser.h +++ /dev/null @@ -1,73 +0,0 @@ -//===-- 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: - struct JSONPTCPU { - std::string vendor; - int64_t family; - int64_t model; - int64_t stepping; - }; - - struct JSONIntelPTSettings { - JSONPTCPU pt_cpu; - }; - - TraceIntelPTSettingsParser(TraceIntelPT &trace) - : lldb_private::TraceSettingsParser((lldb_private::Trace &)trace), - m_trace(trace) {} - -protected: - llvm::StringRef GetPluginSchema() override; - - llvm::Error - ParsePluginSettings(const llvm::json::Value &plugin_settings) override; - -private: - void ParsePTCPU(const JSONPTCPU &pt_cpu); - - TraceIntelPT &m_trace; - pt_cpu m_pt_cpu; -}; - -namespace llvm { -namespace json { - -inline bool fromJSON(const llvm::json::Value &value, - TraceIntelPTSettingsParser::JSONPTCPU &pt_cpu, - llvm::json::Path path) { - llvm::json::ObjectMapper o(value, path); - return o && o.map("vendor", pt_cpu.vendor) && - o.map("family", pt_cpu.family) && o.map("model", pt_cpu.model) && - o.map("stepping", pt_cpu.stepping); -} - -inline bool -fromJSON(const llvm::json::Value &value, - TraceIntelPTSettingsParser::JSONIntelPTSettings &intel_pt_settings, - llvm::json::Path path) { - llvm::json::ObjectMapper o(value, path); - return o && o.map("pt_cpu", intel_pt_settings.pt_cpu); -} - -} // namespace json -} // namespace llvm - -#endif // liblldb_TraceIntelPTSettingsParser_h_ diff --git a/lldb/source/Target/CMakeLists.txt b/lldb/source/Target/CMakeLists.txt index c3a09c6c0098..8374fcca8451 100644 --- a/lldb/source/Target/CMakeLists.txt +++ b/lldb/source/Target/CMakeLists.txt @@ -66,7 +66,7 @@ add_lldb_library(lldbTarget ThreadPlanStack.cpp ThreadSpec.cpp Trace.cpp - TraceSettingsParser.cpp + TraceSessionFileParser.cpp UnixSignals.cpp UnwindAssembly.cpp UnwindLLDB.cpp diff --git a/lldb/source/Target/Trace.cpp b/lldb/source/Target/Trace.cpp index 8d305c9886ca..1b83aec486b7 100644 --- a/lldb/source/Target/Trace.cpp +++ b/lldb/source/Target/Trace.cpp @@ -18,78 +18,61 @@ using namespace lldb; using namespace lldb_private; using namespace llvm; -// Helper structs used to extract the type of a trace settings json without +// Helper structs used to extract the type of a trace session json without // having to parse the entire object. struct JSONSimplePluginSettings { std::string type; }; -struct JSONSimpleTraceSettings { +struct JSONSimpleTraceSession { JSONSimplePluginSettings trace; }; namespace llvm { namespace json { -bool fromJSON(const json::Value &value, - JSONSimplePluginSettings &plugin_settings, json::Path path) { +bool fromJSON(const Value &value, JSONSimplePluginSettings &plugin_settings, + Path path) { json::ObjectMapper o(value, path); return o && o.map("type", plugin_settings.type); } -bool fromJSON(const json::Value &value, JSONSimpleTraceSettings &settings, - json::Path path) { +bool fromJSON(const Value &value, JSONSimpleTraceSession &session, Path path) { json::ObjectMapper o(value, path); - return o && o.map("trace", settings.trace); + return o && o.map("trace", session.trace); } } // namespace json } // namespace llvm -llvm::Expected<lldb::TraceSP> Trace::FindPlugin(Debugger &debugger, - const json::Value &settings, - StringRef info_dir) { - JSONSimpleTraceSettings json_settings; - json::Path::Root root("settings"); - if (!json::fromJSON(settings, json_settings, root)) - return root.getError(); - - ConstString plugin_name(json_settings.trace.type); - auto create_callback = PluginManager::GetTraceCreateCallback(plugin_name); - if (create_callback) { - TraceSP instance = create_callback(); - if (llvm::Error err = instance->ParseSettings(debugger, settings, info_dir)) - return std::move(err); - return instance; - } - +static Error createInvalidPlugInError(StringRef plugin_name) { return createStringError( std::errc::invalid_argument, "no trace plug-in matches the specified type: \"%s\"", - plugin_name.AsCString()); + plugin_name.data()); } -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(); +Expected<lldb::TraceSP> Trace::FindPlugin(Debugger &debugger, + const json::Value &trace_session_file, + StringRef session_file_dir) { + JSONSimpleTraceSession json_session; + json::Path::Root root("traceSession"); + if (!json::fromJSON(trace_session_file, json_session, root)) + return root.getError(); - return createStringError( - std::errc::invalid_argument, - "no trace plug-in matches the specified type: \"%s\"", - plugin_name.AsCString()); + ConstString plugin_name(json_session.trace.type); + if (auto create_callback = PluginManager::GetTraceCreateCallback(plugin_name)) + return create_callback(trace_session_file, session_file_dir, debugger); + + return createInvalidPlugInError(json_session.trace.type); } -llvm::Error Trace::ParseSettings(Debugger &debugger, - const llvm::json::Value &settings, - llvm::StringRef settings_dir) { - if (llvm::Error err = - CreateParser()->ParseSettings(debugger, settings, settings_dir)) - return err; +Expected<StringRef> Trace::FindPluginSchema(StringRef name) { + ConstString plugin_name(name); + StringRef schema = PluginManager::GetTraceSchema(plugin_name); + if (!schema.empty()) + return schema; - return llvm::Error::success(); + return createInvalidPlugInError(name); } - -llvm::StringRef Trace::GetSchema() { return CreateParser()->GetSchema(); } diff --git a/lldb/source/Target/TraceSessionFileParser.cpp b/lldb/source/Target/TraceSessionFileParser.cpp new file mode 100644 index 000000000000..d164d1fc2418 --- /dev/null +++ b/lldb/source/Target/TraceSessionFileParser.cpp @@ -0,0 +1,133 @@ +//===-- TraceSessionFileParser.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/TraceSessionFileParser.h" + +#include <sstream> + +#include "lldb/Core/Module.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; +using namespace llvm; + +void TraceSessionFileParser::NormalizePath(lldb_private::FileSpec &file_spec) { + if (file_spec.IsRelative()) + file_spec.PrependPathComponent(m_session_file_dir); +} + +Error TraceSessionFileParser::ParseModule(lldb::TargetSP &target_sp, + const JSONModule &module) { + FileSpec system_file_spec(module.system_path); + NormalizePath(system_file_spec); + + FileSpec local_file_spec(module.file.hasValue() ? *module.file + : module.system_path); + NormalizePath(local_file_spec); + + ModuleSpec module_spec; + module_spec.GetFileSpec() = local_file_spec; + module_spec.GetPlatformFileSpec() = system_file_spec; + module_spec.SetObjectOffset(module.load_address.value); + + if (module.uuid.hasValue()) + module_spec.GetUUID().SetFromStringRef(*module.uuid); + + Status error; + ModuleSP module_sp = + target_sp->GetOrCreateModule(module_spec, /*notify*/ false, &error); + return error.ToError(); +} + +Error TraceSessionFileParser::CreateJSONError(json::Path::Root &root, + const json::Value &value) { + std::string err; + raw_string_ostream os(err); + root.printErrorContext(value, os); + return createStringError( + std::errc::invalid_argument, "%s\n\nContext:\n%s\n\nSchema:\n%s", + toString(root.getError()).c_str(), os.str().c_str(), m_schema.data()); +} + +std::string TraceSessionFileParser::BuildSchema(StringRef plugin_schema) { + std::ostringstream schema_builder; + schema_builder << "{\n \"trace\": "; + schema_builder << plugin_schema.data() << ","; + 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 session file. +} +)"; + return schema_builder.str(); +} + +namespace llvm { +namespace json { + +bool fromJSON(const Value &value, TraceSessionFileParser::JSONAddress &address, + Path path) { + Optional<StringRef> s = value.getAsString(); + if (s.hasValue() && !s->getAsInteger(0, address.value)) + return true; + + path.report("expected numeric string"); + return false; +} + +bool fromJSON(const Value &value, TraceSessionFileParser::JSONModule &module, + Path path) { + ObjectMapper o(value, path); + return o && o.map("systemPath", module.system_path) && + o.map("file", module.file) && + o.map("loadAddress", module.load_address) && + o.map("uuid", module.uuid); +} + +bool fromJSON(const Value &value, TraceSessionFileParser::JSONThread &thread, + Path path) { + ObjectMapper o(value, path); + return o && o.map("tid", thread.tid) && o.map("traceFile", thread.trace_file); +} + +bool fromJSON(const Value &value, TraceSessionFileParser::JSONProcess &process, + Path path) { + ObjectMapper o(value, path); + return o && o.map("pid", process.pid) && o.map("triple", process.triple) && + o.map("threads", process.threads) && o.map("modules", process.modules); +} + +bool fromJSON(const Value &value, + TraceSessionFileParser::JSONTracePluginSettings &plugin_settings, + Path path) { + ObjectMapper o(value, path); + return o && o.map("type", plugin_settings.type); +} + +} // namespace json +} // namespace llvm diff --git a/lldb/source/Target/TraceSettingsParser.cpp b/lldb/source/Target/TraceSettingsParser.cpp deleted file mode 100644 index cc3369fabcbf..000000000000 --- a/lldb/source/Target/TraceSettingsParser.cpp +++ /dev/null @@ -1,175 +0,0 @@ -//===-- 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 <sstream> - -#include "Plugins/Process/Utility/HistoryThread.h" -#include "lldb/Core/Debugger.h" -#include "lldb/Target/Process.h" - -using namespace lldb; -using namespace lldb_private; -using namespace llvm; - -StringRef TraceSettingsParser::GetSchema() { - static std::string schema; - if (schema.empty()) { - std::ostringstream schema_builder; - schema_builder << "{\n \"trace\": "; - schema_builder << GetPluginSchema().str() << ",\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); -} - -void TraceSettingsParser::ParseThread(ProcessSP &process_sp, - const JSONThread &thread) { - lldb::tid_t tid = static_cast<lldb::tid_t>(thread.tid); - - FileSpec spec(thread.trace_file); - NormalizePath(spec); - m_thread_to_trace_file_map[process_sp->GetID()][tid] = spec; - - ThreadSP thread_sp(new HistoryThread(*process_sp, tid, /*callstack*/ {})); - process_sp->GetThreadList().AddThread(thread_sp); -} - -llvm::Error TraceSettingsParser::ParseModule(TargetSP &target_sp, - const JSONModule &module) { - FileSpec system_file_spec(module.system_path); - NormalizePath(system_file_spec); - - FileSpec local_file_spec(module.file.hasValue() ? *module.file - : module.system_path); - NormalizePath(local_file_spec); - - ModuleSpec module_spec; - module_spec.GetFileSpec() = local_file_spec; - module_spec.GetPlatformFileSpec() = system_file_spec; - module_spec.SetObjectOffset(module.load_address.value); - - if (module.uuid.hasValue()) - module_spec.GetUUID().SetFromStringRef(*module.uuid); - - Status error; - ModuleSP module_sp = - target_sp->GetOrCreateModule(module_spec, /*notify*/ false, &error); - return error.ToError(); -} - -llvm::Error TraceSettingsParser::ParseProcess(Debugger &debugger, - const JSONProcess &process) { - TargetSP target_sp; - Status error = debugger.GetTargetList().CreateTarget( - debugger, /*user_exe_path*/ llvm::StringRef(), process.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>(process.pid)); - - for (const JSONThread &thread : process.threads) - ParseThread(process_sp, thread); - - for (const JSONModule &module : process.modules) { - if (llvm::Error err = ParseModule(target_sp, module)) - return err; - } - return llvm::Error::success(); -} - -llvm::Error -TraceSettingsParser::CreateJSONError(json::Path::Root &root, - const llvm::json::Value &value) { - std::string err; - raw_string_ostream os(err); - root.printErrorContext(value, os); - return createStringError(std::errc::invalid_argument, - "%s\n\nContext:\n%s\n\nSchema:\n%s", - llvm::toString(root.getError()).c_str(), - os.str().c_str(), GetSchema().data()); -} - -llvm::Error -TraceSettingsParser::ParseSettingsImpl(Debugger &debugger, - const llvm::json::Value &raw_settings) { - json::Path::Root root("settings"); - JSONTraceSettings settings; - if (!json::fromJSON(raw_settings, settings, root)) - return CreateJSONError(root, raw_settings); - - for (const JSONProcess &process : settings.processes) { - if (llvm::Error err = ParseProcess(debugger, process)) - return err; - } - - json::Object plugin_obj = *raw_settings.getAsObject()->getObject("trace"); - json::Value plugin_settings(std::move(plugin_obj)); - return ParsePluginSettings(plugin_settings); -} - -llvm::Error -TraceSettingsParser::ParseSettings(Debugger &debugger, - const llvm::json::Value &raw_settings, - llvm::StringRef settings_dir) { - m_settings_dir = settings_dir.str(); - - if (llvm::Error err = ParseSettingsImpl(debugger, raw_settings)) { - // 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 err; - } - - m_trace.m_settings = *raw_settings.getAsObject(); - 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/test/API/commands/trace/TestTraceLoad.py b/lldb/test/API/commands/trace/TestTraceLoad.py index 5747c136877a..673536a09dd6 100644 --- a/lldb/test/API/commands/trace/TestTraceLoad.py +++ b/lldb/test/API/commands/trace/TestTraceLoad.py @@ -40,7 +40,7 @@ def testLoadInvalidTraces(self): src_dir = self.getSourceDir() # We test first an invalid type self.expect("trace load -v " + os.path.join(src_dir, "intelpt-trace", "trace_bad.json"), error=True, - substrs=['''error: expected object at settings.processes[0] + substrs=['''error: expected object at traceSession.processes[0] Context: { @@ -53,7 +53,7 @@ def testLoadInvalidTraces(self): Schema: { - "trace": { + "trace": { "type": "intel-pt", "pt_cpu": { "vendor": "intel" | "unknown", @@ -63,32 +63,35 @@ def testLoadInvalidTraces(self): } },''']) - # Now we test a missing field in the global settings + # Now we test a missing field in the global session file self.expect("trace load -v " + os.path.join(src_dir, "intelpt-trace", "trace_bad2.json"), error=True, - substrs=['error: missing value at settings.processes[1].triple', "Context", "Schema"]) + substrs=['error: missing value at traceSession.processes[1].triple', "Context", "Schema"]) # Now we test a missing field in the intel-pt settings self.expect("trace load -v " + os.path.join(src_dir, "intelpt-trace", "trace_bad4.json"), error=True, - substrs=['''error: missing value at settings.trace.pt_cpu.family + substrs=['''error: missing value at traceSession.trace.pt_cpu.family Context: { - "pt_cpu": /* error: missing value */ { - "model": 79, - "stepping": 1, - "vendor": "intel" - }, - "type": "intel-pt" + "processes": [], + "trace": { + "pt_cpu": /* error: missing value */ { + "model": 79, + "stepping": 1, + "vendor": "intel" + }, + "type": "intel-pt" + } }''', "Schema"]) # Now we test an incorrect load address in the intel-pt settings self.expect("trace load -v " + os.path.join(src_dir, "intelpt-trace", "trace_bad5.json"), error=True, - substrs=['error: expected numeric string at settings.processes[0].modules[0].loadAddress', + substrs=['error: expected numeric string at traceSession.processes[0].modules[0].loadAddress', '"loadAddress": /* error: expected numeric string */ 400000,', "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) self.expect("trace load -v " + os.path.join(src_dir, "intelpt-trace", "trace_bad3.json"), error=True, - substrs=['error: missing value at settings.processes[1].pid']) + substrs=['error: missing value at traceSession.processes[1].pid']) self.assertEqual(self.dbg.GetNumTargets(), 0) diff --git a/lldb/test/API/commands/trace/TestTraceSchema.py b/lldb/test/API/commands/trace/TestTraceSchema.py index 1549325962ae..b30ed329152d 100644 --- a/lldb/test/API/commands/trace/TestTraceSchema.py +++ b/lldb/test/API/commands/trace/TestTraceSchema.py @@ -20,3 +20,15 @@ def testSchema(self): def testInvalidPluginSchema(self): self.expect("trace schema invalid-plugin", error=True, substrs=['error: no trace plug-in matches the specified type: "invalid-plugin"']) + + def testAllSchemas(self): + self.expect("trace schema all", substrs=['''{ + "trace": { + "type": "intel-pt", + "pt_cpu": { + "vendor": "intel" | "unknown", + "family": integer, + "model": integer, + "stepping": integer + } + },''']) _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits