1 //===-- CommandObjectLog.cpp ------------------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 // C Includes 11 // C++ Includes 12 // Other libraries and framework includes 13 // Project includes 14 #include "CommandObjectLog.h" 15 #include "lldb/Interpreter/Args.h" 16 #include "lldb/Core/Debugger.h" 17 #include "lldb/Host/FileSpec.h" 18 #include "lldb/Core/Log.h" 19 #include "lldb/Core/Module.h" 20 #include "lldb/Interpreter/Options.h" 21 #include "lldb/Core/RegularExpression.h" 22 #include "lldb/Core/Stream.h" 23 #include "lldb/Core/StreamFile.h" 24 #include "lldb/Core/Timer.h" 25 #include "lldb/Core/Debugger.h" 26 #include "lldb/Host/StringConvert.h" 27 #include "lldb/Interpreter/CommandInterpreter.h" 28 #include "lldb/Interpreter/CommandReturnObject.h" 29 #include "lldb/Symbol/LineTable.h" 30 #include "lldb/Symbol/ObjectFile.h" 31 #include "lldb/Symbol/SymbolFile.h" 32 #include "lldb/Symbol/SymbolVendor.h" 33 #include "lldb/Target/Process.h" 34 #include "lldb/Target/Target.h" 35 36 using namespace lldb; 37 using namespace lldb_private; 38 39 class CommandObjectLogEnable : public CommandObjectParsed 40 { 41 public: 42 //------------------------------------------------------------------ 43 // Constructors and Destructors 44 //------------------------------------------------------------------ 45 CommandObjectLogEnable(CommandInterpreter &interpreter) : 46 CommandObjectParsed(interpreter, 47 "log enable", 48 "Enable logging for a single log channel.", 49 nullptr), 50 m_options() 51 { 52 CommandArgumentEntry arg1; 53 CommandArgumentEntry arg2; 54 CommandArgumentData channel_arg; 55 CommandArgumentData category_arg; 56 57 // Define the first (and only) variant of this arg. 58 channel_arg.arg_type = eArgTypeLogChannel; 59 channel_arg.arg_repetition = eArgRepeatPlain; 60 61 // There is only one variant this argument could be; put it into the argument entry. 62 arg1.push_back (channel_arg); 63 64 category_arg.arg_type = eArgTypeLogCategory; 65 category_arg.arg_repetition = eArgRepeatPlus; 66 67 arg2.push_back (category_arg); 68 69 // Push the data for the first argument into the m_arguments vector. 70 m_arguments.push_back (arg1); 71 m_arguments.push_back (arg2); 72 } 73 74 ~CommandObjectLogEnable() override = default; 75 76 Options * 77 GetOptions () override 78 { 79 return &m_options; 80 } 81 82 // int 83 // HandleArgumentCompletion (Args &input, 84 // int &cursor_index, 85 // int &cursor_char_position, 86 // OptionElementVector &opt_element_vector, 87 // int match_start_point, 88 // int max_return_elements, 89 // bool &word_complete, 90 // StringList &matches) 91 // { 92 // std::string completion_str (input.GetArgumentAtIndex(cursor_index)); 93 // completion_str.erase (cursor_char_position); 94 // 95 // if (cursor_index == 1) 96 // { 97 // // 98 // Log::AutoCompleteChannelName (completion_str.c_str(), matches); 99 // } 100 // return matches.GetSize(); 101 // } 102 // 103 104 class CommandOptions : public Options 105 { 106 public: 107 CommandOptions() : 108 Options(), 109 log_file(), 110 log_options(0) 111 { 112 } 113 114 ~CommandOptions () override = default; 115 116 Error 117 SetOptionValue (uint32_t option_idx, const char *option_arg, 118 ExecutionContext *execution_context) override 119 { 120 Error error; 121 const int short_option = m_getopt_table[option_idx].val; 122 123 switch (short_option) 124 { 125 case 'f': log_file.SetFile(option_arg, true); break; 126 case 't': log_options |= LLDB_LOG_OPTION_THREADSAFE; break; 127 case 'v': log_options |= LLDB_LOG_OPTION_VERBOSE; break; 128 case 'g': log_options |= LLDB_LOG_OPTION_DEBUG; break; 129 case 's': log_options |= LLDB_LOG_OPTION_PREPEND_SEQUENCE; break; 130 case 'T': log_options |= LLDB_LOG_OPTION_PREPEND_TIMESTAMP; break; 131 case 'p': log_options |= LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD;break; 132 case 'n': log_options |= LLDB_LOG_OPTION_PREPEND_THREAD_NAME; break; 133 case 'S': log_options |= LLDB_LOG_OPTION_BACKTRACE; break; 134 case 'a': log_options |= LLDB_LOG_OPTION_APPEND; break; 135 default: 136 error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); 137 break; 138 } 139 140 return error; 141 } 142 143 void 144 OptionParsingStarting(ExecutionContext *execution_context) override 145 { 146 log_file.Clear(); 147 log_options = 0; 148 } 149 150 const OptionDefinition* 151 GetDefinitions () override 152 { 153 return g_option_table; 154 } 155 156 // Options table: Required for subclasses of Options. 157 158 static OptionDefinition g_option_table[]; 159 160 // Instance variables to hold the values for command options. 161 162 FileSpec log_file; 163 uint32_t log_options; 164 }; 165 166 protected: 167 bool 168 DoExecute (Args& args, 169 CommandReturnObject &result) override 170 { 171 if (args.GetArgumentCount() < 2) 172 { 173 result.AppendErrorWithFormat("%s takes a log channel and one or more log types.\n", m_cmd_name.c_str()); 174 } 175 else 176 { 177 std::string channel(args.GetArgumentAtIndex(0)); 178 args.Shift (); // Shift off the channel 179 char log_file[PATH_MAX]; 180 if (m_options.log_file) 181 m_options.log_file.GetPath(log_file, sizeof(log_file)); 182 else 183 log_file[0] = '\0'; 184 bool success = m_interpreter.GetDebugger().EnableLog (channel.c_str(), 185 args.GetConstArgumentVector(), 186 log_file, 187 m_options.log_options, 188 result.GetErrorStream()); 189 if (success) 190 result.SetStatus (eReturnStatusSuccessFinishNoResult); 191 else 192 result.SetStatus (eReturnStatusFailed); 193 } 194 return result.Succeeded(); 195 } 196 197 CommandOptions m_options; 198 }; 199 200 OptionDefinition 201 CommandObjectLogEnable::CommandOptions::g_option_table[] = 202 { 203 // clang-format off 204 {LLDB_OPT_SET_1, false, "file", 'f', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeFilename, "Set the destination file to log to."}, 205 {LLDB_OPT_SET_1, false, "threadsafe", 't', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Enable thread safe logging to avoid interweaved log lines."}, 206 {LLDB_OPT_SET_1, false, "verbose", 'v', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Enable verbose logging."}, 207 {LLDB_OPT_SET_1, false, "debug", 'g', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Enable debug logging."}, 208 {LLDB_OPT_SET_1, false, "sequence", 's', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Prepend all log lines with an increasing integer sequence id."}, 209 {LLDB_OPT_SET_1, false, "timestamp", 'T', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Prepend all log lines with a timestamp."}, 210 {LLDB_OPT_SET_1, false, "pid-tid", 'p', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Prepend all log lines with the process and thread ID that generates the log line."}, 211 {LLDB_OPT_SET_1, false, "thread-name",'n', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Prepend all log lines with the thread name for the thread that generates the log line."}, 212 {LLDB_OPT_SET_1, false, "stack", 'S', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Append a stack backtrace to each log line."}, 213 {LLDB_OPT_SET_1, false, "append", 'a', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Append to the log file instead of overwriting."}, 214 {0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr} 215 // clang-format on 216 }; 217 218 class CommandObjectLogDisable : public CommandObjectParsed 219 { 220 public: 221 //------------------------------------------------------------------ 222 // Constructors and Destructors 223 //------------------------------------------------------------------ 224 CommandObjectLogDisable(CommandInterpreter &interpreter) : 225 CommandObjectParsed(interpreter, 226 "log disable", 227 "Disable one or more log channel categories.", 228 nullptr) 229 { 230 CommandArgumentEntry arg1; 231 CommandArgumentEntry arg2; 232 CommandArgumentData channel_arg; 233 CommandArgumentData category_arg; 234 235 // Define the first (and only) variant of this arg. 236 channel_arg.arg_type = eArgTypeLogChannel; 237 channel_arg.arg_repetition = eArgRepeatPlain; 238 239 // There is only one variant this argument could be; put it into the argument entry. 240 arg1.push_back (channel_arg); 241 242 category_arg.arg_type = eArgTypeLogCategory; 243 category_arg.arg_repetition = eArgRepeatPlus; 244 245 arg2.push_back (category_arg); 246 247 // Push the data for the first argument into the m_arguments vector. 248 m_arguments.push_back (arg1); 249 m_arguments.push_back (arg2); 250 } 251 252 ~CommandObjectLogDisable() override = default; 253 254 protected: 255 bool 256 DoExecute (Args& args, 257 CommandReturnObject &result) override 258 { 259 const size_t argc = args.GetArgumentCount(); 260 if (argc == 0) 261 { 262 result.AppendErrorWithFormat("%s takes a log channel and one or more log types.\n", m_cmd_name.c_str()); 263 } 264 else 265 { 266 Log::Callbacks log_callbacks; 267 268 std::string channel(args.GetArgumentAtIndex(0)); 269 args.Shift (); // Shift off the channel 270 if (Log::GetLogChannelCallbacks (ConstString(channel.c_str()), log_callbacks)) 271 { 272 log_callbacks.disable (args.GetConstArgumentVector(), &result.GetErrorStream()); 273 result.SetStatus(eReturnStatusSuccessFinishNoResult); 274 } 275 else if (channel == "all") 276 { 277 Log::DisableAllLogChannels(&result.GetErrorStream()); 278 } 279 else 280 { 281 LogChannelSP log_channel_sp (LogChannel::FindPlugin(channel.c_str())); 282 if (log_channel_sp) 283 { 284 log_channel_sp->Disable(args.GetConstArgumentVector(), &result.GetErrorStream()); 285 result.SetStatus(eReturnStatusSuccessFinishNoResult); 286 } 287 else 288 result.AppendErrorWithFormat("Invalid log channel '%s'.\n", args.GetArgumentAtIndex(0)); 289 } 290 } 291 return result.Succeeded(); 292 } 293 }; 294 295 class CommandObjectLogList : public CommandObjectParsed 296 { 297 public: 298 //------------------------------------------------------------------ 299 // Constructors and Destructors 300 //------------------------------------------------------------------ 301 CommandObjectLogList(CommandInterpreter &interpreter) : 302 CommandObjectParsed(interpreter, 303 "log list", 304 "List the log categories for one or more log channels. If none specified, lists them all.", 305 nullptr) 306 { 307 CommandArgumentEntry arg; 308 CommandArgumentData channel_arg; 309 310 // Define the first (and only) variant of this arg. 311 channel_arg.arg_type = eArgTypeLogChannel; 312 channel_arg.arg_repetition = eArgRepeatStar; 313 314 // There is only one variant this argument could be; put it into the argument entry. 315 arg.push_back (channel_arg); 316 317 // Push the data for the first argument into the m_arguments vector. 318 m_arguments.push_back (arg); 319 } 320 321 ~CommandObjectLogList() override = default; 322 323 protected: 324 bool 325 DoExecute (Args& args, 326 CommandReturnObject &result) override 327 { 328 const size_t argc = args.GetArgumentCount(); 329 if (argc == 0) 330 { 331 Log::ListAllLogChannels (&result.GetOutputStream()); 332 result.SetStatus(eReturnStatusSuccessFinishResult); 333 } 334 else 335 { 336 for (size_t i=0; i<argc; ++i) 337 { 338 Log::Callbacks log_callbacks; 339 340 std::string channel(args.GetArgumentAtIndex(i)); 341 if (Log::GetLogChannelCallbacks (ConstString(channel.c_str()), log_callbacks)) 342 { 343 log_callbacks.list_categories (&result.GetOutputStream()); 344 result.SetStatus(eReturnStatusSuccessFinishResult); 345 } 346 else if (channel == "all") 347 { 348 Log::ListAllLogChannels (&result.GetOutputStream()); 349 result.SetStatus(eReturnStatusSuccessFinishResult); 350 } 351 else 352 { 353 LogChannelSP log_channel_sp (LogChannel::FindPlugin(channel.c_str())); 354 if (log_channel_sp) 355 { 356 log_channel_sp->ListCategories(&result.GetOutputStream()); 357 result.SetStatus(eReturnStatusSuccessFinishNoResult); 358 } 359 else 360 result.AppendErrorWithFormat("Invalid log channel '%s'.\n", args.GetArgumentAtIndex(0)); 361 } 362 } 363 } 364 return result.Succeeded(); 365 } 366 }; 367 368 class CommandObjectLogTimer : public CommandObjectParsed 369 { 370 public: 371 //------------------------------------------------------------------ 372 // Constructors and Destructors 373 //------------------------------------------------------------------ 374 CommandObjectLogTimer(CommandInterpreter &interpreter) : 375 CommandObjectParsed (interpreter, 376 "log timers", 377 "Enable, disable, dump, and reset LLDB internal performance timers.", 378 "log timers < enable <depth> | disable | dump | increment <bool> | reset >") 379 { 380 } 381 382 ~CommandObjectLogTimer() override = default; 383 384 protected: 385 bool 386 DoExecute (Args& args, 387 CommandReturnObject &result) override 388 { 389 const size_t argc = args.GetArgumentCount(); 390 result.SetStatus(eReturnStatusFailed); 391 392 if (argc == 1) 393 { 394 const char *sub_command = args.GetArgumentAtIndex(0); 395 396 if (strcasecmp(sub_command, "enable") == 0) 397 { 398 Timer::SetDisplayDepth (UINT32_MAX); 399 result.SetStatus(eReturnStatusSuccessFinishNoResult); 400 } 401 else if (strcasecmp(sub_command, "disable") == 0) 402 { 403 Timer::DumpCategoryTimes (&result.GetOutputStream()); 404 Timer::SetDisplayDepth (0); 405 result.SetStatus(eReturnStatusSuccessFinishResult); 406 } 407 else if (strcasecmp(sub_command, "dump") == 0) 408 { 409 Timer::DumpCategoryTimes (&result.GetOutputStream()); 410 result.SetStatus(eReturnStatusSuccessFinishResult); 411 } 412 else if (strcasecmp(sub_command, "reset") == 0) 413 { 414 Timer::ResetCategoryTimes (); 415 result.SetStatus(eReturnStatusSuccessFinishResult); 416 } 417 } 418 else if (argc == 2) 419 { 420 const char *sub_command = args.GetArgumentAtIndex(0); 421 422 if (strcasecmp(sub_command, "enable") == 0) 423 { 424 bool success; 425 uint32_t depth = StringConvert::ToUInt32(args.GetArgumentAtIndex(1), 0, 0, &success); 426 if (success) 427 { 428 Timer::SetDisplayDepth (depth); 429 result.SetStatus(eReturnStatusSuccessFinishNoResult); 430 } 431 else 432 result.AppendError("Could not convert enable depth to an unsigned integer."); 433 } 434 if (strcasecmp(sub_command, "increment") == 0) 435 { 436 bool success; 437 bool increment = Args::StringToBoolean(args.GetArgumentAtIndex(1), false, &success); 438 if (success) 439 { 440 Timer::SetQuiet (!increment); 441 result.SetStatus(eReturnStatusSuccessFinishNoResult); 442 } 443 else 444 result.AppendError("Could not convert increment value to boolean."); 445 } 446 } 447 448 if (!result.Succeeded()) 449 { 450 result.AppendError("Missing subcommand"); 451 result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str()); 452 } 453 return result.Succeeded(); 454 } 455 }; 456 457 CommandObjectLog::CommandObjectLog(CommandInterpreter &interpreter) 458 : CommandObjectMultiword(interpreter, "log", "Commands controlling LLDB internal logging.", 459 "log <subcommand> [<command-options>]") 460 { 461 LoadSubCommand ("enable", CommandObjectSP (new CommandObjectLogEnable (interpreter))); 462 LoadSubCommand ("disable", CommandObjectSP (new CommandObjectLogDisable (interpreter))); 463 LoadSubCommand ("list", CommandObjectSP (new CommandObjectLogList (interpreter))); 464 LoadSubCommand ("timers", CommandObjectSP (new CommandObjectLogTimer (interpreter))); 465 } 466 467 CommandObjectLog::~CommandObjectLog() = default; 468