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