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