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