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