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 void 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; 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; 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; 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; 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 } 216 217 CommandOptions m_options; 218 }; 219 220 class CommandObjectLogDisable : public CommandObjectParsed { 221 public: 222 // Constructors and Destructors 223 CommandObjectLogDisable(CommandInterpreter &interpreter) 224 : CommandObjectParsed(interpreter, "log disable", 225 "Disable one or more log channel categories.", 226 nullptr) { 227 CommandArgumentEntry arg1; 228 CommandArgumentEntry arg2; 229 CommandArgumentData channel_arg; 230 CommandArgumentData category_arg; 231 232 // Define the first (and only) variant of this arg. 233 channel_arg.arg_type = eArgTypeLogChannel; 234 channel_arg.arg_repetition = eArgRepeatPlain; 235 236 // There is only one variant this argument could be; put it into the 237 // argument entry. 238 arg1.push_back(channel_arg); 239 240 category_arg.arg_type = eArgTypeLogCategory; 241 category_arg.arg_repetition = eArgRepeatPlus; 242 243 arg2.push_back(category_arg); 244 245 // Push the data for the first argument into the m_arguments vector. 246 m_arguments.push_back(arg1); 247 m_arguments.push_back(arg2); 248 } 249 250 ~CommandObjectLogDisable() override = default; 251 252 void 253 HandleArgumentCompletion(CompletionRequest &request, 254 OptionElementVector &opt_element_vector) override { 255 CompleteEnableDisable(request); 256 } 257 258 protected: 259 void DoExecute(Args &args, CommandReturnObject &result) override { 260 if (args.empty()) { 261 result.AppendErrorWithFormat( 262 "%s takes a log channel and one or more log types.\n", 263 m_cmd_name.c_str()); 264 return; 265 } 266 267 const std::string channel = std::string(args[0].ref()); 268 args.Shift(); // Shift off the channel 269 if (channel == "all") { 270 Log::DisableAllLogChannels(); 271 result.SetStatus(eReturnStatusSuccessFinishNoResult); 272 } else { 273 std::string error; 274 llvm::raw_string_ostream error_stream(error); 275 if (Log::DisableLogChannel(channel, args.GetArgumentArrayRef(), 276 error_stream)) 277 result.SetStatus(eReturnStatusSuccessFinishNoResult); 278 result.GetErrorStream() << error_stream.str(); 279 } 280 } 281 }; 282 283 class CommandObjectLogList : public CommandObjectParsed { 284 public: 285 // Constructors and Destructors 286 CommandObjectLogList(CommandInterpreter &interpreter) 287 : CommandObjectParsed(interpreter, "log list", 288 "List the log categories for one or more log " 289 "channels. If none specified, lists them all.", 290 nullptr) { 291 CommandArgumentEntry arg; 292 CommandArgumentData channel_arg; 293 294 // Define the first (and only) variant of this arg. 295 channel_arg.arg_type = eArgTypeLogChannel; 296 channel_arg.arg_repetition = eArgRepeatStar; 297 298 // There is only one variant this argument could be; put it into the 299 // argument entry. 300 arg.push_back(channel_arg); 301 302 // Push the data for the first argument into the m_arguments vector. 303 m_arguments.push_back(arg); 304 } 305 306 ~CommandObjectLogList() override = default; 307 308 void 309 HandleArgumentCompletion(CompletionRequest &request, 310 OptionElementVector &opt_element_vector) override { 311 for (llvm::StringRef channel : Log::ListChannels()) 312 request.TryCompleteCurrentArg(channel); 313 } 314 315 protected: 316 void DoExecute(Args &args, CommandReturnObject &result) override { 317 std::string output; 318 llvm::raw_string_ostream output_stream(output); 319 if (args.empty()) { 320 Log::ListAllLogChannels(output_stream); 321 result.SetStatus(eReturnStatusSuccessFinishResult); 322 } else { 323 bool success = true; 324 for (const auto &entry : args.entries()) 325 success = 326 success && Log::ListChannelCategories(entry.ref(), output_stream); 327 if (success) 328 result.SetStatus(eReturnStatusSuccessFinishResult); 329 } 330 result.GetOutputStream() << output_stream.str(); 331 } 332 }; 333 class CommandObjectLogDump : public CommandObjectParsed { 334 public: 335 CommandObjectLogDump(CommandInterpreter &interpreter) 336 : CommandObjectParsed(interpreter, "log dump", 337 "dump circular buffer logs", nullptr) { 338 CommandArgumentEntry arg1; 339 CommandArgumentData channel_arg; 340 341 // Define the first (and only) variant of this arg. 342 channel_arg.arg_type = eArgTypeLogChannel; 343 channel_arg.arg_repetition = eArgRepeatPlain; 344 345 // There is only one variant this argument could be; put it into the 346 // argument entry. 347 arg1.push_back(channel_arg); 348 349 // Push the data for the first argument into the m_arguments vector. 350 m_arguments.push_back(arg1); 351 } 352 353 ~CommandObjectLogDump() override = default; 354 355 Options *GetOptions() override { return &m_options; } 356 357 class CommandOptions : public Options { 358 public: 359 CommandOptions() = default; 360 361 ~CommandOptions() override = default; 362 363 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 364 ExecutionContext *execution_context) override { 365 Status error; 366 const int short_option = m_getopt_table[option_idx].val; 367 368 switch (short_option) { 369 case 'f': 370 log_file.SetFile(option_arg, FileSpec::Style::native); 371 FileSystem::Instance().Resolve(log_file); 372 break; 373 default: 374 llvm_unreachable("Unimplemented option"); 375 } 376 377 return error; 378 } 379 380 void OptionParsingStarting(ExecutionContext *execution_context) override { 381 log_file.Clear(); 382 } 383 384 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 385 return llvm::ArrayRef(g_log_dump_options); 386 } 387 388 FileSpec log_file; 389 }; 390 391 void 392 HandleArgumentCompletion(CompletionRequest &request, 393 OptionElementVector &opt_element_vector) override { 394 CompleteEnableDisable(request); 395 } 396 397 protected: 398 void DoExecute(Args &args, CommandReturnObject &result) override { 399 if (args.empty()) { 400 result.AppendErrorWithFormat( 401 "%s takes a log channel and one or more log types.\n", 402 m_cmd_name.c_str()); 403 return; 404 } 405 406 std::unique_ptr<llvm::raw_ostream> stream_up; 407 if (m_options.log_file) { 408 const File::OpenOptions flags = File::eOpenOptionWriteOnly | 409 File::eOpenOptionCanCreate | 410 File::eOpenOptionTruncate; 411 llvm::Expected<FileUP> file = FileSystem::Instance().Open( 412 m_options.log_file, flags, lldb::eFilePermissionsFileDefault, false); 413 if (!file) { 414 result.AppendErrorWithFormat("Unable to open log file '%s': %s", 415 m_options.log_file.GetPath().c_str(), 416 llvm::toString(file.takeError()).c_str()); 417 return; 418 } 419 stream_up = std::make_unique<llvm::raw_fd_ostream>( 420 (*file)->GetDescriptor(), /*shouldClose=*/true); 421 } else { 422 stream_up = std::make_unique<llvm::raw_fd_ostream>( 423 GetDebugger().GetOutputFile().GetDescriptor(), /*shouldClose=*/false); 424 } 425 426 const std::string channel = std::string(args[0].ref()); 427 std::string error; 428 llvm::raw_string_ostream error_stream(error); 429 if (Log::DumpLogChannel(channel, *stream_up, error_stream)) { 430 result.SetStatus(eReturnStatusSuccessFinishNoResult); 431 } else { 432 result.SetStatus(eReturnStatusFailed); 433 result.GetErrorStream() << error_stream.str(); 434 } 435 } 436 437 CommandOptions m_options; 438 }; 439 440 class CommandObjectLogTimerEnable : public CommandObjectParsed { 441 public: 442 // Constructors and Destructors 443 CommandObjectLogTimerEnable(CommandInterpreter &interpreter) 444 : CommandObjectParsed(interpreter, "log timers enable", 445 "enable LLDB internal performance timers", 446 "log timers enable <depth>") { 447 CommandArgumentEntry arg; 448 CommandArgumentData depth_arg; 449 450 // Define the first (and only) variant of this arg. 451 depth_arg.arg_type = eArgTypeCount; 452 depth_arg.arg_repetition = eArgRepeatOptional; 453 454 // There is only one variant this argument could be; put it into the 455 // argument entry. 456 arg.push_back(depth_arg); 457 458 // Push the data for the first argument into the m_arguments vector. 459 m_arguments.push_back(arg); 460 } 461 462 ~CommandObjectLogTimerEnable() override = default; 463 464 protected: 465 void DoExecute(Args &args, CommandReturnObject &result) override { 466 result.SetStatus(eReturnStatusFailed); 467 468 if (args.GetArgumentCount() == 0) { 469 Timer::SetDisplayDepth(UINT32_MAX); 470 result.SetStatus(eReturnStatusSuccessFinishNoResult); 471 } else if (args.GetArgumentCount() == 1) { 472 uint32_t depth; 473 if (args[0].ref().consumeInteger(0, depth)) { 474 result.AppendError( 475 "Could not convert enable depth to an unsigned integer."); 476 } else { 477 Timer::SetDisplayDepth(depth); 478 result.SetStatus(eReturnStatusSuccessFinishNoResult); 479 } 480 } 481 482 if (!result.Succeeded()) { 483 result.AppendError("Missing subcommand"); 484 result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str()); 485 } 486 } 487 }; 488 489 class CommandObjectLogTimerDisable : public CommandObjectParsed { 490 public: 491 // Constructors and Destructors 492 CommandObjectLogTimerDisable(CommandInterpreter &interpreter) 493 : CommandObjectParsed(interpreter, "log timers disable", 494 "disable LLDB internal performance timers", 495 nullptr) {} 496 497 ~CommandObjectLogTimerDisable() override = default; 498 499 protected: 500 void DoExecute(Args &args, CommandReturnObject &result) override { 501 Timer::DumpCategoryTimes(result.GetOutputStream()); 502 Timer::SetDisplayDepth(0); 503 result.SetStatus(eReturnStatusSuccessFinishResult); 504 505 if (!result.Succeeded()) { 506 result.AppendError("Missing subcommand"); 507 result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str()); 508 } 509 } 510 }; 511 512 class CommandObjectLogTimerDump : public CommandObjectParsed { 513 public: 514 // Constructors and Destructors 515 CommandObjectLogTimerDump(CommandInterpreter &interpreter) 516 : CommandObjectParsed(interpreter, "log timers dump", 517 "dump LLDB internal performance timers", nullptr) {} 518 519 ~CommandObjectLogTimerDump() override = default; 520 521 protected: 522 void DoExecute(Args &args, CommandReturnObject &result) override { 523 Timer::DumpCategoryTimes(result.GetOutputStream()); 524 result.SetStatus(eReturnStatusSuccessFinishResult); 525 526 if (!result.Succeeded()) { 527 result.AppendError("Missing subcommand"); 528 result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str()); 529 } 530 } 531 }; 532 533 class CommandObjectLogTimerReset : public CommandObjectParsed { 534 public: 535 // Constructors and Destructors 536 CommandObjectLogTimerReset(CommandInterpreter &interpreter) 537 : CommandObjectParsed(interpreter, "log timers reset", 538 "reset LLDB internal performance timers", nullptr) { 539 } 540 541 ~CommandObjectLogTimerReset() override = default; 542 543 protected: 544 void DoExecute(Args &args, CommandReturnObject &result) override { 545 Timer::ResetCategoryTimes(); 546 result.SetStatus(eReturnStatusSuccessFinishResult); 547 548 if (!result.Succeeded()) { 549 result.AppendError("Missing subcommand"); 550 result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str()); 551 } 552 } 553 }; 554 555 class CommandObjectLogTimerIncrement : public CommandObjectParsed { 556 public: 557 // Constructors and Destructors 558 CommandObjectLogTimerIncrement(CommandInterpreter &interpreter) 559 : CommandObjectParsed(interpreter, "log timers increment", 560 "increment LLDB internal performance timers", 561 "log timers increment <bool>") { 562 CommandArgumentEntry arg; 563 CommandArgumentData bool_arg; 564 565 // Define the first (and only) variant of this arg. 566 bool_arg.arg_type = eArgTypeBoolean; 567 bool_arg.arg_repetition = eArgRepeatPlain; 568 569 // There is only one variant this argument could be; put it into the 570 // argument entry. 571 arg.push_back(bool_arg); 572 573 // Push the data for the first argument into the m_arguments vector. 574 m_arguments.push_back(arg); 575 } 576 577 ~CommandObjectLogTimerIncrement() override = default; 578 579 void 580 HandleArgumentCompletion(CompletionRequest &request, 581 OptionElementVector &opt_element_vector) override { 582 request.TryCompleteCurrentArg("true"); 583 request.TryCompleteCurrentArg("false"); 584 } 585 586 protected: 587 void DoExecute(Args &args, CommandReturnObject &result) override { 588 result.SetStatus(eReturnStatusFailed); 589 590 if (args.GetArgumentCount() == 1) { 591 bool success; 592 bool increment = 593 OptionArgParser::ToBoolean(args[0].ref(), false, &success); 594 595 if (success) { 596 Timer::SetQuiet(!increment); 597 result.SetStatus(eReturnStatusSuccessFinishNoResult); 598 } else 599 result.AppendError("Could not convert increment value to boolean."); 600 } 601 602 if (!result.Succeeded()) { 603 result.AppendError("Missing subcommand"); 604 result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str()); 605 } 606 } 607 }; 608 609 class CommandObjectLogTimer : public CommandObjectMultiword { 610 public: 611 CommandObjectLogTimer(CommandInterpreter &interpreter) 612 : CommandObjectMultiword(interpreter, "log timers", 613 "Enable, disable, dump, and reset LLDB internal " 614 "performance timers.", 615 "log timers < enable <depth> | disable | dump | " 616 "increment <bool> | reset >") { 617 LoadSubCommand("enable", CommandObjectSP( 618 new CommandObjectLogTimerEnable(interpreter))); 619 LoadSubCommand("disable", CommandObjectSP(new CommandObjectLogTimerDisable( 620 interpreter))); 621 LoadSubCommand("dump", 622 CommandObjectSP(new CommandObjectLogTimerDump(interpreter))); 623 LoadSubCommand( 624 "reset", CommandObjectSP(new CommandObjectLogTimerReset(interpreter))); 625 LoadSubCommand( 626 "increment", 627 CommandObjectSP(new CommandObjectLogTimerIncrement(interpreter))); 628 } 629 630 ~CommandObjectLogTimer() override = default; 631 }; 632 633 CommandObjectLog::CommandObjectLog(CommandInterpreter &interpreter) 634 : CommandObjectMultiword(interpreter, "log", 635 "Commands controlling LLDB internal logging.", 636 "log <subcommand> [<command-options>]") { 637 LoadSubCommand("enable", 638 CommandObjectSP(new CommandObjectLogEnable(interpreter))); 639 LoadSubCommand("disable", 640 CommandObjectSP(new CommandObjectLogDisable(interpreter))); 641 LoadSubCommand("list", 642 CommandObjectSP(new CommandObjectLogList(interpreter))); 643 LoadSubCommand("dump", 644 CommandObjectSP(new CommandObjectLogDump(interpreter))); 645 LoadSubCommand("timers", 646 CommandObjectSP(new CommandObjectLogTimer(interpreter))); 647 } 648 649 CommandObjectLog::~CommandObjectLog() = default; 650