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