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