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-exception 6// 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// CommandObjectTraceSave 35#define LLDB_OPTIONS_trace_save 36#include "CommandOptions.inc" 37 38#pragma mark CommandObjectTraceSave 39 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 CommandArgumentData bundle_dir{eArgTypeDirectoryName, eArgRepeatPlain}; 93 m_arguments.push_back({bundle_dir}); 94 } 95 96 void 97 HandleArgumentCompletion(CompletionRequest &request, 98 OptionElementVector &opt_element_vector) override { 99 lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( 100 GetCommandInterpreter(), lldb::eDiskFileCompletion, request, nullptr); 101 } 102 103 ~CommandObjectTraceSave() override = default; 104 105protected: 106 void DoExecute(Args &command, CommandReturnObject &result) override { 107 if (command.size() != 1) { 108 result.AppendError("a single path to a directory where the trace bundle " 109 "will be created is required"); 110 return; 111 } 112 113 FileSpec bundle_dir(command[0].ref()); 114 FileSystem::Instance().Resolve(bundle_dir); 115 116 ProcessSP process_sp = m_exe_ctx.GetProcessSP(); 117 118 TraceSP trace_sp = process_sp->GetTarget().GetTrace(); 119 120 if (llvm::Expected<FileSpec> desc_file = 121 trace_sp->SaveToDisk(bundle_dir, m_options.m_compact)) { 122 result.AppendMessageWithFormatv( 123 "Trace bundle description file written to: {0}", *desc_file); 124 result.SetStatus(eReturnStatusSuccessFinishResult); 125 } else { 126 result.AppendError(toString(desc_file.takeError())); 127 } 128 } 129 130 CommandOptions m_options; 131}; 132 133// CommandObjectTraceLoad 134#define LLDB_OPTIONS_trace_load 135#include "CommandOptions.inc" 136 137#pragma mark CommandObjectTraceLoad 138 139class CommandObjectTraceLoad : public CommandObjectParsed { 140public: 141 class CommandOptions : public Options { 142 public: 143 CommandOptions() { OptionParsingStarting(nullptr); } 144 145 ~CommandOptions() override = default; 146 147 Status SetOptionValue(uint32_t option_idx, StringRef option_arg, 148 ExecutionContext *execution_context) override { 149 Status error; 150 const int short_option = m_getopt_table[option_idx].val; 151 152 switch (short_option) { 153 case 'v': { 154 m_verbose = true; 155 break; 156 } 157 default: 158 llvm_unreachable("Unimplemented option"); 159 } 160 return error; 161 } 162 163 void OptionParsingStarting(ExecutionContext *execution_context) override { 164 m_verbose = false; 165 } 166 167 ArrayRef<OptionDefinition> GetDefinitions() override { 168 return ArrayRef(g_trace_load_options); 169 } 170 171 bool m_verbose; // Enable verbose logging for debugging purposes. 172 }; 173 174 CommandObjectTraceLoad(CommandInterpreter &interpreter) 175 : CommandObjectParsed( 176 interpreter, "trace load", 177 "Load a post-mortem processor trace session from a trace bundle.", 178 "trace load <trace_description_file>") { 179 CommandArgumentData session_file_arg{eArgTypeFilename, eArgRepeatPlain}; 180 m_arguments.push_back({session_file_arg}); 181 } 182 183 void 184 HandleArgumentCompletion(CompletionRequest &request, 185 OptionElementVector &opt_element_vector) override { 186 lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( 187 GetCommandInterpreter(), lldb::eDiskFileCompletion, request, nullptr); 188 } 189 190 ~CommandObjectTraceLoad() override = default; 191 192 Options *GetOptions() override { return &m_options; } 193 194protected: 195 void DoExecute(Args &command, CommandReturnObject &result) override { 196 if (command.size() != 1) { 197 result.AppendError("a single path to a JSON file containing a the " 198 "description of the trace bundle is required"); 199 return; 200 } 201 202 const FileSpec trace_description_file(command[0].ref()); 203 204 llvm::Expected<lldb::TraceSP> trace_or_err = 205 Trace::LoadPostMortemTraceFromFile(GetDebugger(), 206 trace_description_file); 207 208 if (!trace_or_err) { 209 result.AppendErrorWithFormat( 210 "%s\n", llvm::toString(trace_or_err.takeError()).c_str()); 211 return; 212 } 213 214 if (m_options.m_verbose) { 215 result.AppendMessageWithFormatv("loading trace with plugin {0}\n", 216 trace_or_err.get()->GetPluginName()); 217 } 218 219 result.SetStatus(eReturnStatusSuccessFinishResult); 220 } 221 222 CommandOptions m_options; 223}; 224 225// CommandObjectTraceDump 226#define LLDB_OPTIONS_trace_dump 227#include "CommandOptions.inc" 228 229#pragma mark CommandObjectTraceDump 230 231class CommandObjectTraceDump : public CommandObjectParsed { 232public: 233 class CommandOptions : public Options { 234 public: 235 CommandOptions() { OptionParsingStarting(nullptr); } 236 237 ~CommandOptions() override = default; 238 239 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 240 ExecutionContext *execution_context) override { 241 Status error; 242 const int short_option = m_getopt_table[option_idx].val; 243 244 switch (short_option) { 245 case 'v': { 246 m_verbose = true; 247 break; 248 } 249 default: 250 llvm_unreachable("Unimplemented option"); 251 } 252 return error; 253 } 254 255 void OptionParsingStarting(ExecutionContext *execution_context) override { 256 m_verbose = false; 257 } 258 259 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 260 return llvm::ArrayRef(g_trace_dump_options); 261 } 262 263 bool m_verbose; // Enable verbose logging for debugging purposes. 264 }; 265 266 CommandObjectTraceDump(CommandInterpreter &interpreter) 267 : CommandObjectParsed(interpreter, "trace dump", 268 "Dump the loaded processor trace data.", 269 "trace dump") {} 270 271 ~CommandObjectTraceDump() override = default; 272 273 Options *GetOptions() override { return &m_options; } 274 275protected: 276 void DoExecute(Args &command, CommandReturnObject &result) override { 277 Status error; 278 // TODO: fill in the dumping code here! 279 if (error.Success()) { 280 result.SetStatus(eReturnStatusSuccessFinishResult); 281 } else { 282 result.AppendErrorWithFormat("%s\n", error.AsCString()); 283 } 284 } 285 286 CommandOptions m_options; 287}; 288 289// CommandObjectTraceSchema 290#define LLDB_OPTIONS_trace_schema 291#include "CommandOptions.inc" 292 293#pragma mark CommandObjectTraceSchema 294 295class CommandObjectTraceSchema : public CommandObjectParsed { 296public: 297 class CommandOptions : public Options { 298 public: 299 CommandOptions() { OptionParsingStarting(nullptr); } 300 301 ~CommandOptions() override = default; 302 303 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 304 ExecutionContext *execution_context) override { 305 Status error; 306 const int short_option = m_getopt_table[option_idx].val; 307 308 switch (short_option) { 309 case 'v': { 310 m_verbose = true; 311 break; 312 } 313 default: 314 llvm_unreachable("Unimplemented option"); 315 } 316 return error; 317 } 318 319 void OptionParsingStarting(ExecutionContext *execution_context) override { 320 m_verbose = false; 321 } 322 323 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 324 return llvm::ArrayRef(g_trace_schema_options); 325 } 326 327 bool m_verbose; // Enable verbose logging for debugging purposes. 328 }; 329 330 CommandObjectTraceSchema(CommandInterpreter &interpreter) 331 : CommandObjectParsed(interpreter, "trace schema", 332 "Show the schema of the given trace plugin.", 333 "trace schema <plug-in>. Use the plug-in name " 334 "\"all\" to see all schemas.\n") { 335 CommandArgumentData plugin_arg{eArgTypeNone, eArgRepeatPlain}; 336 m_arguments.push_back({plugin_arg}); 337 } 338 339 ~CommandObjectTraceSchema() override = default; 340 341 Options *GetOptions() override { return &m_options; } 342 343protected: 344 void DoExecute(Args &command, CommandReturnObject &result) override { 345 Status error; 346 if (command.empty()) { 347 result.AppendError( 348 "trace schema cannot be invoked without a plug-in as argument"); 349 return; 350 } 351 352 StringRef plugin_name(command[0].c_str()); 353 if (plugin_name == "all") { 354 size_t index = 0; 355 while (true) { 356 StringRef schema = PluginManager::GetTraceSchema(index++); 357 if (schema.empty()) 358 break; 359 360 result.AppendMessage(schema); 361 } 362 } else { 363 if (Expected<StringRef> schemaOrErr = 364 Trace::FindPluginSchema(plugin_name)) 365 result.AppendMessage(*schemaOrErr); 366 else 367 error = schemaOrErr.takeError(); 368 } 369 370 if (error.Success()) { 371 result.SetStatus(eReturnStatusSuccessFinishResult); 372 } else { 373 result.AppendErrorWithFormat("%s\n", error.AsCString()); 374 } 375 } 376 377 CommandOptions m_options; 378}; 379 380// CommandObjectTrace 381 382CommandObjectTrace::CommandObjectTrace(CommandInterpreter &interpreter) 383 : CommandObjectMultiword(interpreter, "trace", 384 "Commands for loading and using processor " 385 "trace information.", 386 "trace [<sub-command-options>]") { 387 LoadSubCommand("load", 388 CommandObjectSP(new CommandObjectTraceLoad(interpreter))); 389 LoadSubCommand("dump", 390 CommandObjectSP(new CommandObjectTraceDump(interpreter))); 391 LoadSubCommand("save", 392 CommandObjectSP(new CommandObjectTraceSave(interpreter))); 393 LoadSubCommand("schema", 394 CommandObjectSP(new CommandObjectTraceSchema(interpreter))); 395} 396 397CommandObjectTrace::~CommandObjectTrace() = default; 398 399Expected<CommandObjectSP> CommandObjectTraceProxy::DoGetProxyCommandObject() { 400 ProcessSP process_sp = m_interpreter.GetExecutionContext().GetProcessSP(); 401 402 if (!process_sp) 403 return createStringError(inconvertibleErrorCode(), 404 "Process not available."); 405 if (m_live_debug_session_only && !process_sp->IsLiveDebugSession()) 406 return createStringError(inconvertibleErrorCode(), 407 "Process must be alive."); 408 409 if (Expected<TraceSP> trace_sp = process_sp->GetTarget().GetTraceOrCreate()) 410 return GetDelegateCommand(**trace_sp); 411 else 412 return createStringError(inconvertibleErrorCode(), 413 "Tracing is not supported. %s", 414 toString(trace_sp.takeError()).c_str()); 415} 416 417CommandObject *CommandObjectTraceProxy::GetProxyCommandObject() { 418 if (Expected<CommandObjectSP> delegate = DoGetProxyCommandObject()) { 419 m_delegate_sp = *delegate; 420 m_delegate_error.clear(); 421 return m_delegate_sp.get(); 422 } else { 423 m_delegate_sp.reset(); 424 m_delegate_error = toString(delegate.takeError()); 425 return nullptr; 426 } 427} 428