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 // CommandObjectTraceLoad 34 #define LLDB_OPTIONS_trace_load 35 #include "CommandOptions.inc" 36 37 #pragma mark CommandObjectTraceLoad 38 39 class CommandObjectTraceLoad : public CommandObjectParsed { 40 public: 41 class CommandOptions : public Options { 42 public: 43 CommandOptions() { OptionParsingStarting(nullptr); } 44 45 ~CommandOptions() override = default; 46 47 Status SetOptionValue(uint32_t option_idx, StringRef option_arg, 48 ExecutionContext *execution_context) override { 49 Status error; 50 const int short_option = m_getopt_table[option_idx].val; 51 52 switch (short_option) { 53 case 'v': { 54 m_verbose = true; 55 break; 56 } 57 default: 58 llvm_unreachable("Unimplemented option"); 59 } 60 return error; 61 } 62 63 void OptionParsingStarting(ExecutionContext *execution_context) override { 64 m_verbose = false; 65 } 66 67 ArrayRef<OptionDefinition> GetDefinitions() override { 68 return makeArrayRef(g_trace_load_options); 69 } 70 71 bool m_verbose; // Enable verbose logging for debugging purposes. 72 }; 73 74 CommandObjectTraceLoad(CommandInterpreter &interpreter) 75 : CommandObjectParsed( 76 interpreter, "trace load", 77 "Load a post-mortem processor trace session from a trace bundle.", 78 "trace load") { 79 CommandArgumentData session_file_arg{eArgTypePath, eArgRepeatPlain}; 80 m_arguments.push_back({session_file_arg}); 81 } 82 83 ~CommandObjectTraceLoad() override = default; 84 85 Options *GetOptions() override { return &m_options; } 86 87 protected: 88 bool DoExecute(Args &command, CommandReturnObject &result) override { 89 if (command.size() != 1) { 90 result.AppendError("a single path to a JSON file containing a the " 91 "description of the trace bundle is required"); 92 return false; 93 } 94 95 const FileSpec trace_description_file(command[0].ref()); 96 97 llvm::Expected<lldb::TraceSP> trace_or_err = 98 Trace::LoadPostMortemTraceFromFile(GetDebugger(), 99 trace_description_file); 100 101 if (!trace_or_err) { 102 result.AppendErrorWithFormat( 103 "%s\n", llvm::toString(trace_or_err.takeError()).c_str()); 104 return false; 105 } 106 107 if (m_options.m_verbose) { 108 result.AppendMessageWithFormatv("loading trace with plugin {0}\n", 109 trace_or_err.get()->GetPluginName()); 110 } 111 112 result.SetStatus(eReturnStatusSuccessFinishResult); 113 return true; 114 } 115 116 CommandOptions m_options; 117 }; 118 119 // CommandObjectTraceDump 120 #define LLDB_OPTIONS_trace_dump 121 #include "CommandOptions.inc" 122 123 #pragma mark CommandObjectTraceDump 124 125 class CommandObjectTraceDump : public CommandObjectParsed { 126 public: 127 class CommandOptions : public Options { 128 public: 129 CommandOptions() { OptionParsingStarting(nullptr); } 130 131 ~CommandOptions() override = default; 132 133 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 134 ExecutionContext *execution_context) override { 135 Status error; 136 const int short_option = m_getopt_table[option_idx].val; 137 138 switch (short_option) { 139 case 'v': { 140 m_verbose = true; 141 break; 142 } 143 default: 144 llvm_unreachable("Unimplemented option"); 145 } 146 return error; 147 } 148 149 void OptionParsingStarting(ExecutionContext *execution_context) override { 150 m_verbose = false; 151 } 152 153 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 154 return llvm::makeArrayRef(g_trace_dump_options); 155 } 156 157 bool m_verbose; // Enable verbose logging for debugging purposes. 158 }; 159 160 CommandObjectTraceDump(CommandInterpreter &interpreter) 161 : CommandObjectParsed(interpreter, "trace dump", 162 "Dump the loaded processor trace data.", 163 "trace dump") {} 164 165 ~CommandObjectTraceDump() override = default; 166 167 Options *GetOptions() override { return &m_options; } 168 169 protected: 170 bool DoExecute(Args &command, CommandReturnObject &result) override { 171 Status error; 172 // TODO: fill in the dumping code here! 173 if (error.Success()) { 174 result.SetStatus(eReturnStatusSuccessFinishResult); 175 } else { 176 result.AppendErrorWithFormat("%s\n", error.AsCString()); 177 } 178 return result.Succeeded(); 179 } 180 181 CommandOptions m_options; 182 }; 183 184 // CommandObjectTraceSchema 185 #define LLDB_OPTIONS_trace_schema 186 #include "CommandOptions.inc" 187 188 #pragma mark CommandObjectTraceSchema 189 190 class CommandObjectTraceSchema : public CommandObjectParsed { 191 public: 192 class CommandOptions : public Options { 193 public: 194 CommandOptions() { OptionParsingStarting(nullptr); } 195 196 ~CommandOptions() override = default; 197 198 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 199 ExecutionContext *execution_context) override { 200 Status error; 201 const int short_option = m_getopt_table[option_idx].val; 202 203 switch (short_option) { 204 case 'v': { 205 m_verbose = true; 206 break; 207 } 208 default: 209 llvm_unreachable("Unimplemented option"); 210 } 211 return error; 212 } 213 214 void OptionParsingStarting(ExecutionContext *execution_context) override { 215 m_verbose = false; 216 } 217 218 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 219 return llvm::makeArrayRef(g_trace_schema_options); 220 } 221 222 bool m_verbose; // Enable verbose logging for debugging purposes. 223 }; 224 225 CommandObjectTraceSchema(CommandInterpreter &interpreter) 226 : CommandObjectParsed(interpreter, "trace schema", 227 "Show the schema of the given trace plugin.", 228 "trace schema <plug-in>. Use the plug-in name " 229 "\"all\" to see all schemas.\n") { 230 CommandArgumentData plugin_arg{eArgTypeNone, eArgRepeatPlain}; 231 m_arguments.push_back({plugin_arg}); 232 } 233 234 ~CommandObjectTraceSchema() override = default; 235 236 Options *GetOptions() override { return &m_options; } 237 238 protected: 239 bool DoExecute(Args &command, CommandReturnObject &result) override { 240 Status error; 241 if (command.empty()) { 242 result.AppendError( 243 "trace schema cannot be invoked without a plug-in as argument"); 244 return false; 245 } 246 247 StringRef plugin_name(command[0].c_str()); 248 if (plugin_name == "all") { 249 size_t index = 0; 250 while (true) { 251 StringRef schema = PluginManager::GetTraceSchema(index++); 252 if (schema.empty()) 253 break; 254 255 result.AppendMessage(schema); 256 } 257 } else { 258 if (Expected<StringRef> schemaOrErr = 259 Trace::FindPluginSchema(plugin_name)) 260 result.AppendMessage(*schemaOrErr); 261 else 262 error = schemaOrErr.takeError(); 263 } 264 265 if (error.Success()) { 266 result.SetStatus(eReturnStatusSuccessFinishResult); 267 } else { 268 result.AppendErrorWithFormat("%s\n", error.AsCString()); 269 } 270 return result.Succeeded(); 271 } 272 273 CommandOptions m_options; 274 }; 275 276 // CommandObjectTrace 277 278 CommandObjectTrace::CommandObjectTrace(CommandInterpreter &interpreter) 279 : CommandObjectMultiword(interpreter, "trace", 280 "Commands for loading and using processor " 281 "trace information.", 282 "trace [<sub-command-options>]") { 283 LoadSubCommand("load", 284 CommandObjectSP(new CommandObjectTraceLoad(interpreter))); 285 LoadSubCommand("dump", 286 CommandObjectSP(new CommandObjectTraceDump(interpreter))); 287 LoadSubCommand("schema", 288 CommandObjectSP(new CommandObjectTraceSchema(interpreter))); 289 } 290 291 CommandObjectTrace::~CommandObjectTrace() = default; 292 293 Expected<CommandObjectSP> CommandObjectTraceProxy::DoGetProxyCommandObject() { 294 ProcessSP process_sp = m_interpreter.GetExecutionContext().GetProcessSP(); 295 296 if (!process_sp) 297 return createStringError(inconvertibleErrorCode(), 298 "Process not available."); 299 if (m_live_debug_session_only && !process_sp->IsLiveDebugSession()) 300 return createStringError(inconvertibleErrorCode(), 301 "Process must be alive."); 302 303 if (Expected<TraceSP> trace_sp = process_sp->GetTarget().GetTraceOrCreate()) 304 return GetDelegateCommand(**trace_sp); 305 else 306 return createStringError(inconvertibleErrorCode(), 307 "Tracing is not supported. %s", 308 toString(trace_sp.takeError()).c_str()); 309 } 310 311 CommandObject *CommandObjectTraceProxy::GetProxyCommandObject() { 312 if (Expected<CommandObjectSP> delegate = DoGetProxyCommandObject()) { 313 m_delegate_sp = *delegate; 314 m_delegate_error.clear(); 315 return m_delegate_sp.get(); 316 } else { 317 m_delegate_sp.reset(); 318 m_delegate_error = toString(delegate.takeError()); 319 return nullptr; 320 } 321 } 322