425 lines · cpp
1//===-- CommandObjectTrace.cpp --------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "CommandObjectTrace.h"10 11#include "llvm/Support/JSON.h"12#include "llvm/Support/MemoryBuffer.h"13 14#include "lldb/Core/Debugger.h"15#include "lldb/Core/PluginManager.h"16#include "lldb/Host/OptionParser.h"17#include "lldb/Interpreter/CommandInterpreter.h"18#include "lldb/Interpreter/CommandObject.h"19#include "lldb/Interpreter/CommandOptionArgumentTable.h"20#include "lldb/Interpreter/CommandReturnObject.h"21#include "lldb/Interpreter/OptionArgParser.h"22#include "lldb/Interpreter/OptionGroupFormat.h"23#include "lldb/Interpreter/OptionValueBoolean.h"24#include "lldb/Interpreter/OptionValueLanguage.h"25#include "lldb/Interpreter/OptionValueString.h"26#include "lldb/Interpreter/Options.h"27#include "lldb/Target/Process.h"28#include "lldb/Target/Trace.h"29 30using namespace lldb;31using namespace lldb_private;32using namespace llvm;33 34// CommandObjectTraceSave35#define LLDB_OPTIONS_trace_save36#include "CommandOptions.inc"37 38#pragma mark CommandObjectTraceSave39 40class CommandObjectTraceSave : public CommandObjectParsed {41public:42 class CommandOptions : public Options {43 public:44 CommandOptions() { OptionParsingStarting(nullptr); }45 46 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,47 ExecutionContext *execution_context) override {48 Status error;49 const int short_option = m_getopt_table[option_idx].val;50 51 switch (short_option) {52 case 'c': {53 m_compact = true;54 break;55 }56 default:57 llvm_unreachable("Unimplemented option");58 }59 return error;60 }61 62 void OptionParsingStarting(ExecutionContext *execution_context) override {63 m_compact = false;64 };65 66 llvm::ArrayRef<OptionDefinition> GetDefinitions() override {67 return llvm::ArrayRef(g_trace_save_options);68 };69 70 bool m_compact;71 };72 73 Options *GetOptions() override { return &m_options; }74 75 CommandObjectTraceSave(CommandInterpreter &interpreter)76 : CommandObjectParsed(77 interpreter, "trace save",78 "Save the trace of the current target in the specified directory, "79 "which will be created if needed. "80 "This directory will contain a trace bundle, with all the "81 "necessary files the reconstruct the trace session even on a "82 "different computer. "83 "Part of this bundle is the bundle description file with the name "84 "trace.json. This file can be used by the \"trace load\" command "85 "to load this trace in LLDB."86 "Note: if the current target contains information of multiple "87 "processes or targets, they all will be included in the bundle.",88 "trace save [<cmd-options>] <bundle_directory>",89 eCommandRequiresProcess | eCommandTryTargetAPILock |90 eCommandProcessMustBeLaunched | eCommandProcessMustBePaused |91 eCommandProcessMustBeTraced) {92 AddSimpleArgumentList(eArgTypeDirectoryName);93 }94 95 void96 HandleArgumentCompletion(CompletionRequest &request,97 OptionElementVector &opt_element_vector) override {98 lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(99 GetCommandInterpreter(), lldb::eDiskFileCompletion, request, nullptr);100 }101 102 ~CommandObjectTraceSave() override = default;103 104protected:105 void DoExecute(Args &command, CommandReturnObject &result) override {106 if (command.size() != 1) {107 result.AppendError("a single path to a directory where the trace bundle "108 "will be created is required");109 return;110 }111 112 FileSpec bundle_dir(command[0].ref());113 FileSystem::Instance().Resolve(bundle_dir);114 115 ProcessSP process_sp = m_exe_ctx.GetProcessSP();116 117 TraceSP trace_sp = process_sp->GetTarget().GetTrace();118 119 if (llvm::Expected<FileSpec> desc_file =120 trace_sp->SaveToDisk(bundle_dir, m_options.m_compact)) {121 result.AppendMessageWithFormatv(122 "Trace bundle description file written to: {0}", *desc_file);123 result.SetStatus(eReturnStatusSuccessFinishResult);124 } else {125 result.AppendError(toString(desc_file.takeError()));126 }127 }128 129 CommandOptions m_options;130};131 132// CommandObjectTraceLoad133#define LLDB_OPTIONS_trace_load134#include "CommandOptions.inc"135 136#pragma mark CommandObjectTraceLoad137 138class CommandObjectTraceLoad : public CommandObjectParsed {139public:140 class CommandOptions : public Options {141 public:142 CommandOptions() { OptionParsingStarting(nullptr); }143 144 ~CommandOptions() override = default;145 146 Status SetOptionValue(uint32_t option_idx, StringRef option_arg,147 ExecutionContext *execution_context) override {148 Status error;149 const int short_option = m_getopt_table[option_idx].val;150 151 switch (short_option) {152 case 'v': {153 m_verbose = true;154 break;155 }156 default:157 llvm_unreachable("Unimplemented option");158 }159 return error;160 }161 162 void OptionParsingStarting(ExecutionContext *execution_context) override {163 m_verbose = false;164 }165 166 ArrayRef<OptionDefinition> GetDefinitions() override {167 return ArrayRef(g_trace_load_options);168 }169 170 bool m_verbose; // Enable verbose logging for debugging purposes.171 };172 173 CommandObjectTraceLoad(CommandInterpreter &interpreter)174 : CommandObjectParsed(175 interpreter, "trace load",176 "Load a post-mortem processor trace session from a trace bundle.",177 "trace load <trace_description_file>") {178 AddSimpleArgumentList(eArgTypeFilename);179 }180 181 void182 HandleArgumentCompletion(CompletionRequest &request,183 OptionElementVector &opt_element_vector) override {184 lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(185 GetCommandInterpreter(), lldb::eDiskFileCompletion, request, nullptr);186 }187 188 ~CommandObjectTraceLoad() override = default;189 190 Options *GetOptions() override { return &m_options; }191 192protected:193 void DoExecute(Args &command, CommandReturnObject &result) override {194 if (command.size() != 1) {195 result.AppendError("a single path to a JSON file containing a the "196 "description of the trace bundle is required");197 return;198 }199 200 const FileSpec trace_description_file(command[0].ref());201 202 llvm::Expected<lldb::TraceSP> trace_or_err =203 Trace::LoadPostMortemTraceFromFile(GetDebugger(),204 trace_description_file);205 206 if (!trace_or_err) {207 result.AppendErrorWithFormat(208 "%s\n", llvm::toString(trace_or_err.takeError()).c_str());209 return;210 }211 212 if (m_options.m_verbose) {213 result.AppendMessageWithFormatv("loading trace with plugin {0}\n",214 trace_or_err.get()->GetPluginName());215 }216 217 result.SetStatus(eReturnStatusSuccessFinishResult);218 }219 220 CommandOptions m_options;221};222 223// CommandObjectTraceDump224#define LLDB_OPTIONS_trace_dump225#include "CommandOptions.inc"226 227#pragma mark CommandObjectTraceDump228 229class CommandObjectTraceDump : public CommandObjectParsed {230public:231 class CommandOptions : public Options {232 public:233 CommandOptions() { OptionParsingStarting(nullptr); }234 235 ~CommandOptions() override = default;236 237 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,238 ExecutionContext *execution_context) override {239 Status error;240 const int short_option = m_getopt_table[option_idx].val;241 242 switch (short_option) {243 case 'v': {244 m_verbose = true;245 break;246 }247 default:248 llvm_unreachable("Unimplemented option");249 }250 return error;251 }252 253 void OptionParsingStarting(ExecutionContext *execution_context) override {254 m_verbose = false;255 }256 257 llvm::ArrayRef<OptionDefinition> GetDefinitions() override {258 return llvm::ArrayRef(g_trace_dump_options);259 }260 261 bool m_verbose; // Enable verbose logging for debugging purposes.262 };263 264 CommandObjectTraceDump(CommandInterpreter &interpreter)265 : CommandObjectParsed(interpreter, "trace dump",266 "Dump the loaded processor trace data.",267 "trace dump") {}268 269 ~CommandObjectTraceDump() override = default;270 271 Options *GetOptions() override { return &m_options; }272 273protected:274 void DoExecute(Args &command, CommandReturnObject &result) override {275 Status error;276 // TODO: fill in the dumping code here!277 if (error.Success()) {278 result.SetStatus(eReturnStatusSuccessFinishResult);279 } else {280 result.AppendErrorWithFormat("%s\n", error.AsCString());281 }282 }283 284 CommandOptions m_options;285};286 287// CommandObjectTraceSchema288#define LLDB_OPTIONS_trace_schema289#include "CommandOptions.inc"290 291#pragma mark CommandObjectTraceSchema292 293class CommandObjectTraceSchema : public CommandObjectParsed {294public:295 class CommandOptions : public Options {296 public:297 CommandOptions() { OptionParsingStarting(nullptr); }298 299 ~CommandOptions() override = default;300 301 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,302 ExecutionContext *execution_context) override {303 Status error;304 const int short_option = m_getopt_table[option_idx].val;305 306 switch (short_option) {307 case 'v': {308 m_verbose = true;309 break;310 }311 default:312 llvm_unreachable("Unimplemented option");313 }314 return error;315 }316 317 void OptionParsingStarting(ExecutionContext *execution_context) override {318 m_verbose = false;319 }320 321 llvm::ArrayRef<OptionDefinition> GetDefinitions() override {322 return llvm::ArrayRef(g_trace_schema_options);323 }324 325 bool m_verbose; // Enable verbose logging for debugging purposes.326 };327 328 CommandObjectTraceSchema(CommandInterpreter &interpreter)329 : CommandObjectParsed(interpreter, "trace schema",330 "Show the schema of the given trace plugin.",331 "trace schema <plug-in>. Use the plug-in name "332 "\"all\" to see all schemas.\n") {333 AddSimpleArgumentList(eArgTypeNone);334 }335 336 ~CommandObjectTraceSchema() override = default;337 338 Options *GetOptions() override { return &m_options; }339 340protected:341 void DoExecute(Args &command, CommandReturnObject &result) override {342 Status error;343 if (command.empty()) {344 result.AppendError(345 "trace schema cannot be invoked without a plug-in as argument");346 return;347 }348 349 StringRef plugin_name(command[0].c_str());350 if (plugin_name == "all") {351 size_t index = 0;352 while (true) {353 StringRef schema = PluginManager::GetTraceSchema(index++);354 if (schema.empty())355 break;356 357 result.AppendMessage(schema);358 }359 } else {360 if (Expected<StringRef> schemaOrErr =361 Trace::FindPluginSchema(plugin_name))362 result.AppendMessage(*schemaOrErr);363 else364 error = Status::FromError(schemaOrErr.takeError());365 }366 367 if (error.Success()) {368 result.SetStatus(eReturnStatusSuccessFinishResult);369 } else {370 result.AppendErrorWithFormat("%s\n", error.AsCString());371 }372 }373 374 CommandOptions m_options;375};376 377// CommandObjectTrace378 379CommandObjectTrace::CommandObjectTrace(CommandInterpreter &interpreter)380 : CommandObjectMultiword(interpreter, "trace",381 "Commands for loading and using processor "382 "trace information.",383 "trace [<sub-command-options>]") {384 LoadSubCommand("load",385 CommandObjectSP(new CommandObjectTraceLoad(interpreter)));386 LoadSubCommand("dump",387 CommandObjectSP(new CommandObjectTraceDump(interpreter)));388 LoadSubCommand("save",389 CommandObjectSP(new CommandObjectTraceSave(interpreter)));390 LoadSubCommand("schema",391 CommandObjectSP(new CommandObjectTraceSchema(interpreter)));392}393 394CommandObjectTrace::~CommandObjectTrace() = default;395 396Expected<CommandObjectSP> CommandObjectTraceProxy::DoGetProxyCommandObject() {397 ProcessSP process_sp = m_interpreter.GetExecutionContext().GetProcessSP();398 399 if (!process_sp)400 return createStringError(inconvertibleErrorCode(),401 "Process not available.");402 if (m_live_debug_session_only && !process_sp->IsLiveDebugSession())403 return createStringError(inconvertibleErrorCode(),404 "Process must be alive.");405 406 if (Expected<TraceSP> trace_sp = process_sp->GetTarget().GetTraceOrCreate())407 return GetDelegateCommand(**trace_sp);408 else409 return createStringError(inconvertibleErrorCode(),410 "Tracing is not supported. %s",411 toString(trace_sp.takeError()).c_str());412}413 414CommandObject *CommandObjectTraceProxy::GetProxyCommandObject() {415 if (Expected<CommandObjectSP> delegate = DoGetProxyCommandObject()) {416 m_delegate_sp = *delegate;417 m_delegate_error.clear();418 return m_delegate_sp.get();419 } else {420 m_delegate_sp.reset();421 m_delegate_error = toString(delegate.takeError());422 return nullptr;423 }424}425