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