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