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