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