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