1 //===-- CommandObjectLog.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 "CommandObjectLog.h" 10 #include "lldb/Core/Debugger.h" 11 #include "lldb/Host/OptionParser.h" 12 #include "lldb/Interpreter/CommandReturnObject.h" 13 #include "lldb/Interpreter/OptionArgParser.h" 14 #include "lldb/Interpreter/OptionValueUInt64.h" 15 #include "lldb/Interpreter/Options.h" 16 #include "lldb/Utility/Args.h" 17 #include "lldb/Utility/FileSpec.h" 18 #include "lldb/Utility/Log.h" 19 #include "lldb/Utility/Stream.h" 20 #include "lldb/Utility/Timer.h" 21 22 using namespace lldb; 23 using namespace lldb_private; 24 25 #define LLDB_OPTIONS_log_enable 26 #include "CommandOptions.inc" 27 28 /// Common completion logic for log enable/disable. 29 static void CompleteEnableDisable(CompletionRequest &request) { 30 size_t arg_index = request.GetCursorIndex(); 31 if (arg_index == 0) { // We got: log enable/disable x[tab] 32 for (llvm::StringRef channel : Log::ListChannels()) 33 request.TryCompleteCurrentArg(channel); 34 } else if (arg_index >= 1) { // We got: log enable/disable channel x[tab] 35 llvm::StringRef channel = request.GetParsedLine().GetArgumentAtIndex(0); 36 Log::ForEachChannelCategory( 37 channel, [&request](llvm::StringRef name, llvm::StringRef desc) { 38 request.TryCompleteCurrentArg(name, desc); 39 }); 40 } 41 } 42 43 class CommandObjectLogEnable : public CommandObjectParsed { 44 public: 45 // Constructors and Destructors 46 CommandObjectLogEnable(CommandInterpreter &interpreter) 47 : CommandObjectParsed(interpreter, "log enable", 48 "Enable logging for a single log channel.", 49 nullptr) { 50 CommandArgumentEntry arg1; 51 CommandArgumentEntry arg2; 52 CommandArgumentData channel_arg; 53 CommandArgumentData category_arg; 54 55 // Define the first (and only) variant of this arg. 56 channel_arg.arg_type = eArgTypeLogChannel; 57 channel_arg.arg_repetition = eArgRepeatPlain; 58 59 // There is only one variant this argument could be; put it into the 60 // argument entry. 61 arg1.push_back(channel_arg); 62 63 category_arg.arg_type = eArgTypeLogCategory; 64 category_arg.arg_repetition = eArgRepeatPlus; 65 66 arg2.push_back(category_arg); 67 68 // Push the data for the first argument into the m_arguments vector. 69 m_arguments.push_back(arg1); 70 m_arguments.push_back(arg2); 71 } 72 73 ~CommandObjectLogEnable() override = default; 74 75 Options *GetOptions() override { return &m_options; } 76 77 class CommandOptions : public Options { 78 public: 79 CommandOptions() = default; 80 81 ~CommandOptions() override = default; 82 83 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 84 ExecutionContext *execution_context) override { 85 Status error; 86 const int short_option = m_getopt_table[option_idx].val; 87 88 switch (short_option) { 89 case 'f': 90 log_file.SetFile(option_arg, FileSpec::Style::native); 91 FileSystem::Instance().Resolve(log_file); 92 break; 93 case 'b': 94 error = 95 buffer_size.SetValueFromString(option_arg, eVarSetOperationAssign); 96 break; 97 case 't': 98 log_options |= LLDB_LOG_OPTION_THREADSAFE; 99 break; 100 case 'v': 101 log_options |= LLDB_LOG_OPTION_VERBOSE; 102 break; 103 case 's': 104 log_options |= LLDB_LOG_OPTION_PREPEND_SEQUENCE; 105 break; 106 case 'T': 107 log_options |= LLDB_LOG_OPTION_PREPEND_TIMESTAMP; 108 break; 109 case 'p': 110 log_options |= LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD; 111 break; 112 case 'n': 113 log_options |= LLDB_LOG_OPTION_PREPEND_THREAD_NAME; 114 break; 115 case 'S': 116 log_options |= LLDB_LOG_OPTION_BACKTRACE; 117 break; 118 case 'a': 119 log_options |= LLDB_LOG_OPTION_APPEND; 120 break; 121 case 'F': 122 log_options |= LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION; 123 break; 124 default: 125 llvm_unreachable("Unimplemented option"); 126 } 127 128 return error; 129 } 130 131 void OptionParsingStarting(ExecutionContext *execution_context) override { 132 log_file.Clear(); 133 buffer_size.Clear(); 134 log_options = 0; 135 } 136 137 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 138 return llvm::makeArrayRef(g_log_enable_options); 139 } 140 141 FileSpec log_file; 142 OptionValueUInt64 buffer_size; 143 uint32_t log_options = 0; 144 }; 145 146 void 147 HandleArgumentCompletion(CompletionRequest &request, 148 OptionElementVector &opt_element_vector) override { 149 CompleteEnableDisable(request); 150 } 151 152 protected: 153 bool DoExecute(Args &args, CommandReturnObject &result) override { 154 if (args.GetArgumentCount() < 2) { 155 result.AppendErrorWithFormat( 156 "%s takes a log channel and one or more log types.\n", 157 m_cmd_name.c_str()); 158 return false; 159 } 160 161 // Store into a std::string since we're about to shift the channel off. 162 const std::string channel = std::string(args[0].ref()); 163 args.Shift(); // Shift off the channel 164 char log_file[PATH_MAX]; 165 if (m_options.log_file) 166 m_options.log_file.GetPath(log_file, sizeof(log_file)); 167 else 168 log_file[0] = '\0'; 169 170 std::string error; 171 llvm::raw_string_ostream error_stream(error); 172 bool success = GetDebugger().EnableLog( 173 channel, args.GetArgumentArrayRef(), log_file, m_options.log_options, 174 m_options.buffer_size.GetCurrentValue(), error_stream); 175 result.GetErrorStream() << error_stream.str(); 176 177 if (success) 178 result.SetStatus(eReturnStatusSuccessFinishNoResult); 179 else 180 result.SetStatus(eReturnStatusFailed); 181 return result.Succeeded(); 182 } 183 184 CommandOptions m_options; 185 }; 186 187 class CommandObjectLogDisable : public CommandObjectParsed { 188 public: 189 // Constructors and Destructors 190 CommandObjectLogDisable(CommandInterpreter &interpreter) 191 : CommandObjectParsed(interpreter, "log disable", 192 "Disable one or more log channel categories.", 193 nullptr) { 194 CommandArgumentEntry arg1; 195 CommandArgumentEntry arg2; 196 CommandArgumentData channel_arg; 197 CommandArgumentData category_arg; 198 199 // Define the first (and only) variant of this arg. 200 channel_arg.arg_type = eArgTypeLogChannel; 201 channel_arg.arg_repetition = eArgRepeatPlain; 202 203 // There is only one variant this argument could be; put it into the 204 // argument entry. 205 arg1.push_back(channel_arg); 206 207 category_arg.arg_type = eArgTypeLogCategory; 208 category_arg.arg_repetition = eArgRepeatPlus; 209 210 arg2.push_back(category_arg); 211 212 // Push the data for the first argument into the m_arguments vector. 213 m_arguments.push_back(arg1); 214 m_arguments.push_back(arg2); 215 } 216 217 ~CommandObjectLogDisable() override = default; 218 219 void 220 HandleArgumentCompletion(CompletionRequest &request, 221 OptionElementVector &opt_element_vector) override { 222 CompleteEnableDisable(request); 223 } 224 225 protected: 226 bool DoExecute(Args &args, CommandReturnObject &result) override { 227 if (args.empty()) { 228 result.AppendErrorWithFormat( 229 "%s takes a log channel and one or more log types.\n", 230 m_cmd_name.c_str()); 231 return false; 232 } 233 234 const std::string channel = std::string(args[0].ref()); 235 args.Shift(); // Shift off the channel 236 if (channel == "all") { 237 Log::DisableAllLogChannels(); 238 result.SetStatus(eReturnStatusSuccessFinishNoResult); 239 } else { 240 std::string error; 241 llvm::raw_string_ostream error_stream(error); 242 if (Log::DisableLogChannel(channel, args.GetArgumentArrayRef(), 243 error_stream)) 244 result.SetStatus(eReturnStatusSuccessFinishNoResult); 245 result.GetErrorStream() << error_stream.str(); 246 } 247 return result.Succeeded(); 248 } 249 }; 250 251 class CommandObjectLogList : public CommandObjectParsed { 252 public: 253 // Constructors and Destructors 254 CommandObjectLogList(CommandInterpreter &interpreter) 255 : CommandObjectParsed(interpreter, "log list", 256 "List the log categories for one or more log " 257 "channels. If none specified, lists them all.", 258 nullptr) { 259 CommandArgumentEntry arg; 260 CommandArgumentData channel_arg; 261 262 // Define the first (and only) variant of this arg. 263 channel_arg.arg_type = eArgTypeLogChannel; 264 channel_arg.arg_repetition = eArgRepeatStar; 265 266 // There is only one variant this argument could be; put it into the 267 // argument entry. 268 arg.push_back(channel_arg); 269 270 // Push the data for the first argument into the m_arguments vector. 271 m_arguments.push_back(arg); 272 } 273 274 ~CommandObjectLogList() override = default; 275 276 void 277 HandleArgumentCompletion(CompletionRequest &request, 278 OptionElementVector &opt_element_vector) override { 279 for (llvm::StringRef channel : Log::ListChannels()) 280 request.TryCompleteCurrentArg(channel); 281 } 282 283 protected: 284 bool DoExecute(Args &args, CommandReturnObject &result) override { 285 std::string output; 286 llvm::raw_string_ostream output_stream(output); 287 if (args.empty()) { 288 Log::ListAllLogChannels(output_stream); 289 result.SetStatus(eReturnStatusSuccessFinishResult); 290 } else { 291 bool success = true; 292 for (const auto &entry : args.entries()) 293 success = 294 success && Log::ListChannelCategories(entry.ref(), output_stream); 295 if (success) 296 result.SetStatus(eReturnStatusSuccessFinishResult); 297 } 298 result.GetOutputStream() << output_stream.str(); 299 return result.Succeeded(); 300 } 301 }; 302 303 class CommandObjectLogTimerEnable : public CommandObjectParsed { 304 public: 305 // Constructors and Destructors 306 CommandObjectLogTimerEnable(CommandInterpreter &interpreter) 307 : CommandObjectParsed(interpreter, "log timers enable", 308 "enable LLDB internal performance timers", 309 "log timers enable <depth>") { 310 CommandArgumentEntry arg; 311 CommandArgumentData depth_arg; 312 313 // Define the first (and only) variant of this arg. 314 depth_arg.arg_type = eArgTypeCount; 315 depth_arg.arg_repetition = eArgRepeatOptional; 316 317 // There is only one variant this argument could be; put it into the 318 // argument entry. 319 arg.push_back(depth_arg); 320 321 // Push the data for the first argument into the m_arguments vector. 322 m_arguments.push_back(arg); 323 } 324 325 ~CommandObjectLogTimerEnable() override = default; 326 327 protected: 328 bool DoExecute(Args &args, CommandReturnObject &result) override { 329 result.SetStatus(eReturnStatusFailed); 330 331 if (args.GetArgumentCount() == 0) { 332 Timer::SetDisplayDepth(UINT32_MAX); 333 result.SetStatus(eReturnStatusSuccessFinishNoResult); 334 } else if (args.GetArgumentCount() == 1) { 335 uint32_t depth; 336 if (args[0].ref().consumeInteger(0, depth)) { 337 result.AppendError( 338 "Could not convert enable depth to an unsigned integer."); 339 } else { 340 Timer::SetDisplayDepth(depth); 341 result.SetStatus(eReturnStatusSuccessFinishNoResult); 342 } 343 } 344 345 if (!result.Succeeded()) { 346 result.AppendError("Missing subcommand"); 347 result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str()); 348 } 349 return result.Succeeded(); 350 } 351 }; 352 353 class CommandObjectLogTimerDisable : public CommandObjectParsed { 354 public: 355 // Constructors and Destructors 356 CommandObjectLogTimerDisable(CommandInterpreter &interpreter) 357 : CommandObjectParsed(interpreter, "log timers disable", 358 "disable LLDB internal performance timers", 359 nullptr) {} 360 361 ~CommandObjectLogTimerDisable() override = default; 362 363 protected: 364 bool DoExecute(Args &args, CommandReturnObject &result) override { 365 Timer::DumpCategoryTimes(&result.GetOutputStream()); 366 Timer::SetDisplayDepth(0); 367 result.SetStatus(eReturnStatusSuccessFinishResult); 368 369 if (!result.Succeeded()) { 370 result.AppendError("Missing subcommand"); 371 result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str()); 372 } 373 return result.Succeeded(); 374 } 375 }; 376 377 class CommandObjectLogTimerDump : public CommandObjectParsed { 378 public: 379 // Constructors and Destructors 380 CommandObjectLogTimerDump(CommandInterpreter &interpreter) 381 : CommandObjectParsed(interpreter, "log timers dump", 382 "dump LLDB internal performance timers", nullptr) {} 383 384 ~CommandObjectLogTimerDump() override = default; 385 386 protected: 387 bool DoExecute(Args &args, CommandReturnObject &result) override { 388 Timer::DumpCategoryTimes(&result.GetOutputStream()); 389 result.SetStatus(eReturnStatusSuccessFinishResult); 390 391 if (!result.Succeeded()) { 392 result.AppendError("Missing subcommand"); 393 result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str()); 394 } 395 return result.Succeeded(); 396 } 397 }; 398 399 class CommandObjectLogTimerReset : public CommandObjectParsed { 400 public: 401 // Constructors and Destructors 402 CommandObjectLogTimerReset(CommandInterpreter &interpreter) 403 : CommandObjectParsed(interpreter, "log timers reset", 404 "reset LLDB internal performance timers", nullptr) { 405 } 406 407 ~CommandObjectLogTimerReset() override = default; 408 409 protected: 410 bool DoExecute(Args &args, CommandReturnObject &result) override { 411 Timer::ResetCategoryTimes(); 412 result.SetStatus(eReturnStatusSuccessFinishResult); 413 414 if (!result.Succeeded()) { 415 result.AppendError("Missing subcommand"); 416 result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str()); 417 } 418 return result.Succeeded(); 419 } 420 }; 421 422 class CommandObjectLogTimerIncrement : public CommandObjectParsed { 423 public: 424 // Constructors and Destructors 425 CommandObjectLogTimerIncrement(CommandInterpreter &interpreter) 426 : CommandObjectParsed(interpreter, "log timers increment", 427 "increment LLDB internal performance timers", 428 "log timers increment <bool>") { 429 CommandArgumentEntry arg; 430 CommandArgumentData bool_arg; 431 432 // Define the first (and only) variant of this arg. 433 bool_arg.arg_type = eArgTypeBoolean; 434 bool_arg.arg_repetition = eArgRepeatPlain; 435 436 // There is only one variant this argument could be; put it into the 437 // argument entry. 438 arg.push_back(bool_arg); 439 440 // Push the data for the first argument into the m_arguments vector. 441 m_arguments.push_back(arg); 442 } 443 444 ~CommandObjectLogTimerIncrement() override = default; 445 446 void 447 HandleArgumentCompletion(CompletionRequest &request, 448 OptionElementVector &opt_element_vector) override { 449 request.TryCompleteCurrentArg("true"); 450 request.TryCompleteCurrentArg("false"); 451 } 452 453 protected: 454 bool DoExecute(Args &args, CommandReturnObject &result) override { 455 result.SetStatus(eReturnStatusFailed); 456 457 if (args.GetArgumentCount() == 1) { 458 bool success; 459 bool increment = 460 OptionArgParser::ToBoolean(args[0].ref(), false, &success); 461 462 if (success) { 463 Timer::SetQuiet(!increment); 464 result.SetStatus(eReturnStatusSuccessFinishNoResult); 465 } else 466 result.AppendError("Could not convert increment value to boolean."); 467 } 468 469 if (!result.Succeeded()) { 470 result.AppendError("Missing subcommand"); 471 result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str()); 472 } 473 return result.Succeeded(); 474 } 475 }; 476 477 class CommandObjectLogTimer : public CommandObjectMultiword { 478 public: 479 CommandObjectLogTimer(CommandInterpreter &interpreter) 480 : CommandObjectMultiword(interpreter, "log timers", 481 "Enable, disable, dump, and reset LLDB internal " 482 "performance timers.", 483 "log timers < enable <depth> | disable | dump | " 484 "increment <bool> | reset >") { 485 LoadSubCommand("enable", CommandObjectSP( 486 new CommandObjectLogTimerEnable(interpreter))); 487 LoadSubCommand("disable", CommandObjectSP(new CommandObjectLogTimerDisable( 488 interpreter))); 489 LoadSubCommand("dump", 490 CommandObjectSP(new CommandObjectLogTimerDump(interpreter))); 491 LoadSubCommand( 492 "reset", CommandObjectSP(new CommandObjectLogTimerReset(interpreter))); 493 LoadSubCommand( 494 "increment", 495 CommandObjectSP(new CommandObjectLogTimerIncrement(interpreter))); 496 } 497 498 ~CommandObjectLogTimer() override = default; 499 }; 500 501 CommandObjectLog::CommandObjectLog(CommandInterpreter &interpreter) 502 : CommandObjectMultiword(interpreter, "log", 503 "Commands controlling LLDB internal logging.", 504 "log <subcommand> [<command-options>]") { 505 LoadSubCommand("enable", 506 CommandObjectSP(new CommandObjectLogEnable(interpreter))); 507 LoadSubCommand("disable", 508 CommandObjectSP(new CommandObjectLogDisable(interpreter))); 509 LoadSubCommand("list", 510 CommandObjectSP(new CommandObjectLogList(interpreter))); 511 LoadSubCommand("timers", 512 CommandObjectSP(new CommandObjectLogTimer(interpreter))); 513 } 514 515 CommandObjectLog::~CommandObjectLog() = default; 516