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