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