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