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