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