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