| //===-- CommandObjectTrace.cpp --------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "CommandObjectTrace.h" |
| |
| #include "llvm/Support/JSON.h" |
| #include "llvm/Support/MemoryBuffer.h" |
| |
| #include "lldb/Core/Debugger.h" |
| #include "lldb/Core/PluginManager.h" |
| #include "lldb/Host/OptionParser.h" |
| #include "lldb/Interpreter/CommandInterpreter.h" |
| #include "lldb/Interpreter/CommandObject.h" |
| #include "lldb/Interpreter/CommandReturnObject.h" |
| #include "lldb/Interpreter/OptionArgParser.h" |
| #include "lldb/Interpreter/OptionGroupFormat.h" |
| #include "lldb/Interpreter/OptionValueBoolean.h" |
| #include "lldb/Interpreter/OptionValueLanguage.h" |
| #include "lldb/Interpreter/OptionValueString.h" |
| #include "lldb/Interpreter/Options.h" |
| #include "lldb/Target/Process.h" |
| #include "lldb/Target/Trace.h" |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| using namespace llvm; |
| |
| // CommandObjectTraceLoad |
| #define LLDB_OPTIONS_trace_load |
| #include "CommandOptions.inc" |
| |
| #pragma mark CommandObjectTraceLoad |
| |
| class CommandObjectTraceLoad : public CommandObjectParsed { |
| public: |
| class CommandOptions : public Options { |
| public: |
| CommandOptions() : Options() { OptionParsingStarting(nullptr); } |
| |
| ~CommandOptions() override = default; |
| |
| Status SetOptionValue(uint32_t option_idx, StringRef option_arg, |
| ExecutionContext *execution_context) override { |
| Status error; |
| const int short_option = m_getopt_table[option_idx].val; |
| |
| switch (short_option) { |
| case 'v': { |
| m_verbose = true; |
| break; |
| } |
| default: |
| llvm_unreachable("Unimplemented option"); |
| } |
| return error; |
| } |
| |
| void OptionParsingStarting(ExecutionContext *execution_context) override { |
| m_verbose = false; |
| } |
| |
| ArrayRef<OptionDefinition> GetDefinitions() override { |
| return makeArrayRef(g_trace_load_options); |
| } |
| |
| bool m_verbose; // Enable verbose logging for debugging purposes. |
| }; |
| |
| CommandObjectTraceLoad(CommandInterpreter &interpreter) |
| : CommandObjectParsed(interpreter, "trace load", |
| "Load a processor trace session from a JSON file.", |
| "trace load"), |
| m_options() {} |
| |
| ~CommandObjectTraceLoad() override = default; |
| |
| Options *GetOptions() override { return &m_options; } |
| |
| protected: |
| bool DoExecute(Args &command, CommandReturnObject &result) override { |
| if (command.size() != 1) { |
| result.AppendError( |
| "a single path to a JSON file containing a trace session" |
| "is required"); |
| return false; |
| } |
| |
| auto end_with_failure = [&result](llvm::Error err) -> bool { |
| result.AppendErrorWithFormat("%s\n", |
| llvm::toString(std::move(err)).c_str()); |
| return false; |
| }; |
| |
| FileSpec json_file(command[0].ref()); |
| |
| auto buffer_or_error = llvm::MemoryBuffer::getFile(json_file.GetPath()); |
| if (!buffer_or_error) { |
| return end_with_failure(llvm::createStringError( |
| std::errc::invalid_argument, "could not open input file: %s - %s.", |
| json_file.GetPath().c_str(), |
| buffer_or_error.getError().message().c_str())); |
| } |
| |
| llvm::Expected<json::Value> session_file = |
| json::parse(buffer_or_error.get()->getBuffer().str()); |
| if (!session_file) |
| return end_with_failure(session_file.takeError()); |
| |
| if (Expected<lldb::TraceSP> traceOrErr = |
| Trace::FindPluginForPostMortemProcess( |
| GetDebugger(), *session_file, |
| json_file.GetDirectory().AsCString())) { |
| lldb::TraceSP trace_sp = traceOrErr.get(); |
| if (m_options.m_verbose && trace_sp) |
| result.AppendMessageWithFormatv("loading trace with plugin {0}\n", |
| trace_sp->GetPluginName()); |
| } else |
| return end_with_failure(traceOrErr.takeError()); |
| |
| result.SetStatus(eReturnStatusSuccessFinishResult); |
| return true; |
| } |
| |
| CommandOptions m_options; |
| }; |
| |
| // CommandObjectTraceDump |
| #define LLDB_OPTIONS_trace_dump |
| #include "CommandOptions.inc" |
| |
| #pragma mark CommandObjectTraceDump |
| |
| class CommandObjectTraceDump : public CommandObjectParsed { |
| public: |
| class CommandOptions : public Options { |
| public: |
| CommandOptions() : Options() { OptionParsingStarting(nullptr); } |
| |
| ~CommandOptions() override = default; |
| |
| Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, |
| ExecutionContext *execution_context) override { |
| Status error; |
| const int short_option = m_getopt_table[option_idx].val; |
| |
| switch (short_option) { |
| case 'v': { |
| m_verbose = true; |
| break; |
| } |
| default: |
| llvm_unreachable("Unimplemented option"); |
| } |
| return error; |
| } |
| |
| void OptionParsingStarting(ExecutionContext *execution_context) override { |
| m_verbose = false; |
| } |
| |
| llvm::ArrayRef<OptionDefinition> GetDefinitions() override { |
| return llvm::makeArrayRef(g_trace_dump_options); |
| } |
| |
| bool m_verbose; // Enable verbose logging for debugging purposes. |
| }; |
| |
| CommandObjectTraceDump(CommandInterpreter &interpreter) |
| : CommandObjectParsed(interpreter, "trace dump", |
| "Dump the loaded processor trace data.", |
| "trace dump"), |
| m_options() {} |
| |
| ~CommandObjectTraceDump() override = default; |
| |
| Options *GetOptions() override { return &m_options; } |
| |
| protected: |
| bool DoExecute(Args &command, CommandReturnObject &result) override { |
| Status error; |
| // TODO: fill in the dumping code here! |
| if (error.Success()) { |
| result.SetStatus(eReturnStatusSuccessFinishResult); |
| } else { |
| result.AppendErrorWithFormat("%s\n", error.AsCString()); |
| } |
| return result.Succeeded(); |
| } |
| |
| CommandOptions m_options; |
| }; |
| |
| // CommandObjectTraceSchema |
| #define LLDB_OPTIONS_trace_schema |
| #include "CommandOptions.inc" |
| |
| #pragma mark CommandObjectTraceSchema |
| |
| class CommandObjectTraceSchema : public CommandObjectParsed { |
| public: |
| class CommandOptions : public Options { |
| public: |
| CommandOptions() : Options() { OptionParsingStarting(nullptr); } |
| |
| ~CommandOptions() override = default; |
| |
| Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, |
| ExecutionContext *execution_context) override { |
| Status error; |
| const int short_option = m_getopt_table[option_idx].val; |
| |
| switch (short_option) { |
| case 'v': { |
| m_verbose = true; |
| break; |
| } |
| default: |
| llvm_unreachable("Unimplemented option"); |
| } |
| return error; |
| } |
| |
| void OptionParsingStarting(ExecutionContext *execution_context) override { |
| m_verbose = false; |
| } |
| |
| llvm::ArrayRef<OptionDefinition> GetDefinitions() override { |
| return llvm::makeArrayRef(g_trace_schema_options); |
| } |
| |
| bool m_verbose; // Enable verbose logging for debugging purposes. |
| }; |
| |
| CommandObjectTraceSchema(CommandInterpreter &interpreter) |
| : CommandObjectParsed(interpreter, "trace schema", |
| "Show the schema of the given trace plugin.", |
| "trace schema <plug-in>. Use the plug-in name " |
| "\"all\" to see all schemas.\n"), |
| m_options() {} |
| |
| ~CommandObjectTraceSchema() override = default; |
| |
| Options *GetOptions() override { return &m_options; } |
| |
| protected: |
| bool DoExecute(Args &command, CommandReturnObject &result) override { |
| Status error; |
| if (command.empty()) { |
| result.AppendError( |
| "trace schema cannot be invoked without a plug-in as argument"); |
| return false; |
| } |
| |
| StringRef plugin_name(command[0].c_str()); |
| if (plugin_name == "all") { |
| size_t index = 0; |
| while (true) { |
| StringRef schema = PluginManager::GetTraceSchema(index++); |
| if (schema.empty()) |
| break; |
| |
| result.AppendMessage(schema); |
| } |
| } else { |
| if (Expected<StringRef> schemaOrErr = |
| Trace::FindPluginSchema(plugin_name)) |
| result.AppendMessage(*schemaOrErr); |
| else |
| error = schemaOrErr.takeError(); |
| } |
| |
| if (error.Success()) { |
| result.SetStatus(eReturnStatusSuccessFinishResult); |
| } else { |
| result.AppendErrorWithFormat("%s\n", error.AsCString()); |
| } |
| return result.Succeeded(); |
| } |
| |
| CommandOptions m_options; |
| }; |
| |
| // CommandObjectTrace |
| |
| CommandObjectTrace::CommandObjectTrace(CommandInterpreter &interpreter) |
| : CommandObjectMultiword(interpreter, "trace", |
| "Commands for loading and using processor " |
| "trace information.", |
| "trace [<sub-command-options>]") { |
| LoadSubCommand("load", |
| CommandObjectSP(new CommandObjectTraceLoad(interpreter))); |
| LoadSubCommand("dump", |
| CommandObjectSP(new CommandObjectTraceDump(interpreter))); |
| LoadSubCommand("schema", |
| CommandObjectSP(new CommandObjectTraceSchema(interpreter))); |
| } |
| |
| CommandObjectTrace::~CommandObjectTrace() = default; |
| |
| Expected<CommandObjectSP> CommandObjectTraceProxy::DoGetProxyCommandObject() { |
| ProcessSP process_sp = m_interpreter.GetExecutionContext().GetProcessSP(); |
| |
| if (!process_sp) |
| return createStringError(inconvertibleErrorCode(), |
| "Process not available."); |
| if (m_live_debug_session_only && !process_sp->IsLiveDebugSession()) |
| return createStringError(inconvertibleErrorCode(), |
| "Process must be alive."); |
| |
| if (Expected<TraceSP> trace_sp = process_sp->GetTarget().GetTraceOrCreate()) |
| return GetDelegateCommand(**trace_sp); |
| else |
| return createStringError(inconvertibleErrorCode(), |
| "Tracing is not supported. %s", |
| toString(trace_sp.takeError()).c_str()); |
| } |
| |
| CommandObject *CommandObjectTraceProxy::GetProxyCommandObject() { |
| if (Expected<CommandObjectSP> delegate = DoGetProxyCommandObject()) { |
| m_delegate_sp = *delegate; |
| m_delegate_error.clear(); |
| return m_delegate_sp.get(); |
| } else { |
| m_delegate_sp.reset(); |
| m_delegate_error = toString(delegate.takeError()); |
| return nullptr; |
| } |
| } |