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