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