diff --git a/lldb/include/lldb/Core/PluginManager.h b/lldb/include/lldb/Core/PluginManager.h --- a/lldb/include/lldb/Core/PluginManager.h +++ b/lldb/include/lldb/Core/PluginManager.h @@ -332,12 +332,35 @@ // 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 --- 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 @@ /// 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 - 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 FindPlugin(llvm::StringRef plugin_name); + static llvm::Expected + 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 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> - m_thread_to_trace_file_map; - std::vector 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 --- /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 file; + JSONAddress load_address; + llvm::Optional uuid; + }; + + struct JSONThread { + int64_t tid; + std::string trace_file; + }; + + struct JSONProcess { + int64_t pid; + std::string triple; + std::vector threads; + std::vector 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 struct JSONTraceSession { + std::vector 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 +bool fromJSON( + const Value &value, + lldb_private::TraceSessionFileParser::JSONTraceSession + &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 --- 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 file; - JSONAddress load_address; - llvm::Optional uuid; - }; - - struct JSONThread { - int64_t tid; - std::string trace_file; - }; - - struct JSONProcess { - int64_t pid; - std::string triple; - std::vector threads; - std::vector modules; - }; - - struct JSONTracePluginSettings { - std::string type; - }; - - struct JSONTraceSettings { - std::vector 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> - m_thread_to_trace_file_map; - std::vector 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 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 --- a/lldb/include/lldb/lldb-forward.h +++ b/lldb/include/lldb/lldb-forward.h @@ -227,7 +227,7 @@ 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 --- 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 @@ const char *repl_options); typedef int (*ComparisonFunction)(const void *, const void *); typedef void (*DebuggerInitializeCallback)(Debugger &debugger); -typedef lldb::TraceSP (*TraceCreateInstance)(); +typedef llvm::Expected (*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 --- 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 @@ 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 @@ 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 @@ buffer_or_error.getError().message().c_str())); } - llvm::Expected settings = + llvm::Expected 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 traceOrErr = Trace::FindPlugin( - GetDebugger(), *settings, json_file.GetDirectory().AsCString())) { + if (Expected 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 @@ CommandObjectTraceSchema(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "trace schema", "Show the schema of the given trace plugin.", - "trace schema "), + "trace schema . Use the plug-in name " + "\"all\" to see all schemas.\n"), m_options() {} ~CommandObjectTraceSchema() override = default; @@ -254,12 +258,21 @@ } StringRef plugin_name(command[0].c_str()); - - if (Expected 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 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 --- a/lldb/source/Core/PluginManager.cpp +++ b/lldb/source/Core/PluginManager.cpp @@ -264,12 +264,13 @@ const std::vector &GetInstances() const { return m_instances; } std::vector &GetInstances() { return m_instances; } -private: Instance *GetInstanceAtIndex(uint32_t idx) { if (idx < m_instances.size()) return &m_instances[idx]; return nullptr; } + +private: std::vector m_instances; }; @@ -1007,7 +1008,16 @@ #pragma mark Trace -typedef PluginInstance TraceInstance; +struct TraceInstance : public PluginInstance { + TraceInstance(ConstString name, std::string description, + CallbackType create_callback, llvm::StringRef schema) + : PluginInstance(name, std::move(description), + create_callback), + schema(schema) {} + + llvm::StringRef schema; +}; + typedef PluginInstances TraceInstances; static TraceInstances &GetTraceInstances() { @@ -1016,8 +1026,10 @@ } 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 @@ 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 UnwindAssemblyInstance; diff --git a/lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt b/lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt --- a/lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt +++ b/lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt @@ -11,7 +11,8 @@ 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.h b/lldb/source/Plugins/Trace/intel-pt/ThreadIntelPT.h new file mode 100644 --- /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/ThreadIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/ThreadIntelPT.cpp new file mode 100644 --- /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 + +#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( + *this, 0, GetProcess()->GetAddressByteSize(), LLDB_INVALID_ADDRESS); +} + +bool ThreadIntelPT::CalculateStopInfo() { return false; } diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h --- 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 + 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 CreateParser() override; + TraceIntelPT(const pt_cpu &pt_cpu, const std::vector &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> 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/TraceIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp --- 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 @@ return g_name; } -std::unique_ptr -TraceIntelPT::CreateParser() { - return std::make_unique(*this); +StringRef TraceIntelPT::GetSchema() { + return TraceIntelPTSessionFileParser::GetSchema(); } //------------------------------------------------------------------ @@ -46,6 +47,10 @@ void TraceIntelPT::Dump(lldb_private::Stream *s) const {} -lldb::TraceSP TraceIntelPT::CreateInstance() { - return lldb::TraceSP(new TraceIntelPT()); +Expected +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/TraceIntelPTSessionFileParser.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h new file mode 100644 --- /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 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 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/TraceIntelPTSessionFileParser.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp new file mode 100644 --- /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(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(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(pt_cpu.family), + static_cast(pt_cpu.model), + static_cast(pt_cpu.stepping)}; +} + +Error TraceIntelPTSessionFileParser::ParseImpl() { + json::Path::Root root("traceSession"); + TraceSessionFileParser::JSONTraceSession 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 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(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/TraceIntelPTSettingsParser.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSettingsParser.h deleted file mode 100644 --- 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/Plugins/Trace/intel-pt/TraceIntelPTSettingsParser.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSettingsParser.cpp deleted file mode 100644 --- 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(pt_cpu.family), - static_cast(pt_cpu.model), - static_cast(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/Target/CMakeLists.txt b/lldb/source/Target/CMakeLists.txt --- a/lldb/source/Target/CMakeLists.txt +++ b/lldb/source/Target/CMakeLists.txt @@ -66,7 +66,7 @@ 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 --- a/lldb/source/Target/Trace.cpp +++ b/lldb/source/Target/Trace.cpp @@ -18,78 +18,61 @@ 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 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 Trace::FindPlugin(StringRef name) { - ConstString plugin_name(name); - auto create_callback = PluginManager::GetTraceCreateCallback(plugin_name); - if (create_callback) - return create_callback(); +Expected 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 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 --- /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 + +#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 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 --- 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 - -#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(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(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 --- a/lldb/test/API/commands/trace/TestTraceLoad.py +++ b/lldb/test/API/commands/trace/TestTraceLoad.py @@ -40,7 +40,7 @@ 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 @@ Schema: { - "trace": { + "trace": { "type": "intel-pt", "pt_cpu": { "vendor": "intel" | "unknown", @@ -63,32 +63,35 @@ } },''']) - # 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 --- a/lldb/test/API/commands/trace/TestTraceSchema.py +++ b/lldb/test/API/commands/trace/TestTraceSchema.py @@ -20,3 +20,15 @@ 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 + } + },'''])