1 //===-- CommandObjectLog.cpp ------------------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "CommandObjectLog.h" 10 #include "lldb/Core/Debugger.h" 11 #include "lldb/Core/Module.h" 12 #include "lldb/Core/StreamFile.h" 13 #include "lldb/Host/OptionParser.h" 14 #include "lldb/Interpreter/CommandInterpreter.h" 15 #include "lldb/Interpreter/CommandReturnObject.h" 16 #include "lldb/Interpreter/OptionArgParser.h" 17 #include "lldb/Interpreter/Options.h" 18 #include "lldb/Symbol/LineTable.h" 19 #include "lldb/Symbol/ObjectFile.h" 20 #include "lldb/Symbol/SymbolFile.h" 21 #include "lldb/Symbol/SymbolVendor.h" 22 #include "lldb/Target/Process.h" 23 #include "lldb/Target/Target.h" 24 #include "lldb/Utility/Args.h" 25 #include "lldb/Utility/FileSpec.h" 26 #include "lldb/Utility/Log.h" 27 #include "lldb/Utility/RegularExpression.h" 28 #include "lldb/Utility/Stream.h" 29 #include "lldb/Utility/Timer.h" 30 31 using namespace lldb; 32 using namespace lldb_private; 33 34 static constexpr OptionDefinition g_log_options[] = { 35 #define LLDB_OPTIONS_log 36 #include "CommandOptions.inc" 37 }; 38 39 class CommandObjectLogEnable : public CommandObjectParsed { 40 public: 41 // Constructors and Destructors 42 CommandObjectLogEnable(CommandInterpreter &interpreter) 43 : CommandObjectParsed(interpreter, "log enable", 44 "Enable logging for a single log channel.", 45 nullptr), 46 m_options() { 47 CommandArgumentEntry arg1; 48 CommandArgumentEntry arg2; 49 CommandArgumentData channel_arg; 50 CommandArgumentData category_arg; 51 52 // Define the first (and only) variant of this arg. 53 channel_arg.arg_type = eArgTypeLogChannel; 54 channel_arg.arg_repetition = eArgRepeatPlain; 55 56 // There is only one variant this argument could be; put it into the 57 // argument entry. 58 arg1.push_back(channel_arg); 59 60 category_arg.arg_type = eArgTypeLogCategory; 61 category_arg.arg_repetition = eArgRepeatPlus; 62 63 arg2.push_back(category_arg); 64 65 // Push the data for the first argument into the m_arguments vector. 66 m_arguments.push_back(arg1); 67 m_arguments.push_back(arg2); 68 } 69 70 ~CommandObjectLogEnable() override = default; 71 72 Options *GetOptions() override { return &m_options; } 73 74 class CommandOptions : public Options { 75 public: 76 CommandOptions() : Options(), log_file(), log_options(0) {} 77 78 ~CommandOptions() override = default; 79 80 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 81 ExecutionContext *execution_context) override { 82 Status error; 83 const int short_option = m_getopt_table[option_idx].val; 84 85 switch (short_option) { 86 case 'f': 87 log_file.SetFile(option_arg, FileSpec::Style::native); 88 FileSystem::Instance().Resolve(log_file); 89 break; 90 case 't': 91 log_options |= LLDB_LOG_OPTION_THREADSAFE; 92 break; 93 case 'v': 94 log_options |= LLDB_LOG_OPTION_VERBOSE; 95 break; 96 case 's': 97 log_options |= LLDB_LOG_OPTION_PREPEND_SEQUENCE; 98 break; 99 case 'T': 100 log_options |= LLDB_LOG_OPTION_PREPEND_TIMESTAMP; 101 break; 102 case 'p': 103 log_options |= LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD; 104 break; 105 case 'n': 106 log_options |= LLDB_LOG_OPTION_PREPEND_THREAD_NAME; 107 break; 108 case 'S': 109 log_options |= LLDB_LOG_OPTION_BACKTRACE; 110 break; 111 case 'a': 112 log_options |= LLDB_LOG_OPTION_APPEND; 113 break; 114 case 'F': 115 log_options |= LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION; 116 break; 117 default: 118 error.SetErrorStringWithFormat("unrecognized option '%c'", 119 short_option); 120 break; 121 } 122 123 return error; 124 } 125 126 void OptionParsingStarting(ExecutionContext *execution_context) override { 127 log_file.Clear(); 128 log_options = 0; 129 } 130 131 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 132 return llvm::makeArrayRef(g_log_options); 133 } 134 135 // Instance variables to hold the values for command options. 136 137 FileSpec log_file; 138 uint32_t log_options; 139 }; 140 141 protected: 142 bool DoExecute(Args &args, CommandReturnObject &result) override { 143 if (args.GetArgumentCount() < 2) { 144 result.AppendErrorWithFormat( 145 "%s takes a log channel and one or more log types.\n", 146 m_cmd_name.c_str()); 147 return false; 148 } 149 150 // Store into a std::string since we're about to shift the channel off. 151 const std::string channel = args[0].ref; 152 args.Shift(); // Shift off the channel 153 char log_file[PATH_MAX]; 154 if (m_options.log_file) 155 m_options.log_file.GetPath(log_file, sizeof(log_file)); 156 else 157 log_file[0] = '\0'; 158 159 std::string error; 160 llvm::raw_string_ostream error_stream(error); 161 bool success = 162 GetDebugger().EnableLog(channel, args.GetArgumentArrayRef(), log_file, 163 m_options.log_options, error_stream); 164 result.GetErrorStream() << error_stream.str(); 165 166 if (success) 167 result.SetStatus(eReturnStatusSuccessFinishNoResult); 168 else 169 result.SetStatus(eReturnStatusFailed); 170 return result.Succeeded(); 171 } 172 173 CommandOptions m_options; 174 }; 175 176 class CommandObjectLogDisable : public CommandObjectParsed { 177 public: 178 // Constructors and Destructors 179 CommandObjectLogDisable(CommandInterpreter &interpreter) 180 : CommandObjectParsed(interpreter, "log disable", 181 "Disable one or more log channel categories.", 182 nullptr) { 183 CommandArgumentEntry arg1; 184 CommandArgumentEntry arg2; 185 CommandArgumentData channel_arg; 186 CommandArgumentData category_arg; 187 188 // Define the first (and only) variant of this arg. 189 channel_arg.arg_type = eArgTypeLogChannel; 190 channel_arg.arg_repetition = eArgRepeatPlain; 191 192 // There is only one variant this argument could be; put it into the 193 // argument entry. 194 arg1.push_back(channel_arg); 195 196 category_arg.arg_type = eArgTypeLogCategory; 197 category_arg.arg_repetition = eArgRepeatPlus; 198 199 arg2.push_back(category_arg); 200 201 // Push the data for the first argument into the m_arguments vector. 202 m_arguments.push_back(arg1); 203 m_arguments.push_back(arg2); 204 } 205 206 ~CommandObjectLogDisable() override = default; 207 208 protected: 209 bool DoExecute(Args &args, CommandReturnObject &result) override { 210 if (args.empty()) { 211 result.AppendErrorWithFormat( 212 "%s takes a log channel and one or more log types.\n", 213 m_cmd_name.c_str()); 214 return false; 215 } 216 217 const std::string channel = args[0].ref; 218 args.Shift(); // Shift off the channel 219 if (channel == "all") { 220 Log::DisableAllLogChannels(); 221 result.SetStatus(eReturnStatusSuccessFinishNoResult); 222 } else { 223 std::string error; 224 llvm::raw_string_ostream error_stream(error); 225 if (Log::DisableLogChannel(channel, args.GetArgumentArrayRef(), 226 error_stream)) 227 result.SetStatus(eReturnStatusSuccessFinishNoResult); 228 result.GetErrorStream() << error_stream.str(); 229 } 230 return result.Succeeded(); 231 } 232 }; 233 234 class CommandObjectLogList : public CommandObjectParsed { 235 public: 236 // Constructors and Destructors 237 CommandObjectLogList(CommandInterpreter &interpreter) 238 : CommandObjectParsed(interpreter, "log list", 239 "List the log categories for one or more log " 240 "channels. If none specified, lists them all.", 241 nullptr) { 242 CommandArgumentEntry arg; 243 CommandArgumentData channel_arg; 244 245 // Define the first (and only) variant of this arg. 246 channel_arg.arg_type = eArgTypeLogChannel; 247 channel_arg.arg_repetition = eArgRepeatStar; 248 249 // There is only one variant this argument could be; put it into the 250 // argument entry. 251 arg.push_back(channel_arg); 252 253 // Push the data for the first argument into the m_arguments vector. 254 m_arguments.push_back(arg); 255 } 256 257 ~CommandObjectLogList() override = default; 258 259 protected: 260 bool DoExecute(Args &args, CommandReturnObject &result) override { 261 std::string output; 262 llvm::raw_string_ostream output_stream(output); 263 if (args.empty()) { 264 Log::ListAllLogChannels(output_stream); 265 result.SetStatus(eReturnStatusSuccessFinishResult); 266 } else { 267 bool success = true; 268 for (const auto &entry : args.entries()) 269 success = 270 success && Log::ListChannelCategories(entry.ref, output_stream); 271 if (success) 272 result.SetStatus(eReturnStatusSuccessFinishResult); 273 } 274 result.GetOutputStream() << output_stream.str(); 275 return result.Succeeded(); 276 } 277 }; 278 279 class CommandObjectLogTimer : public CommandObjectParsed { 280 public: 281 // Constructors and Destructors 282 CommandObjectLogTimer(CommandInterpreter &interpreter) 283 : CommandObjectParsed(interpreter, "log timers", 284 "Enable, disable, dump, and reset LLDB internal " 285 "performance timers.", 286 "log timers < enable <depth> | disable | dump | " 287 "increment <bool> | reset >") {} 288 289 ~CommandObjectLogTimer() override = default; 290 291 protected: 292 bool DoExecute(Args &args, CommandReturnObject &result) override { 293 result.SetStatus(eReturnStatusFailed); 294 295 if (args.GetArgumentCount() == 1) { 296 auto sub_command = args[0].ref; 297 298 if (sub_command.equals_lower("enable")) { 299 Timer::SetDisplayDepth(UINT32_MAX); 300 result.SetStatus(eReturnStatusSuccessFinishNoResult); 301 } else if (sub_command.equals_lower("disable")) { 302 Timer::DumpCategoryTimes(&result.GetOutputStream()); 303 Timer::SetDisplayDepth(0); 304 result.SetStatus(eReturnStatusSuccessFinishResult); 305 } else if (sub_command.equals_lower("dump")) { 306 Timer::DumpCategoryTimes(&result.GetOutputStream()); 307 result.SetStatus(eReturnStatusSuccessFinishResult); 308 } else if (sub_command.equals_lower("reset")) { 309 Timer::ResetCategoryTimes(); 310 result.SetStatus(eReturnStatusSuccessFinishResult); 311 } 312 } else if (args.GetArgumentCount() == 2) { 313 auto sub_command = args[0].ref; 314 auto param = args[1].ref; 315 316 if (sub_command.equals_lower("enable")) { 317 uint32_t depth; 318 if (param.consumeInteger(0, depth)) { 319 result.AppendError( 320 "Could not convert enable depth to an unsigned integer."); 321 } else { 322 Timer::SetDisplayDepth(depth); 323 result.SetStatus(eReturnStatusSuccessFinishNoResult); 324 } 325 } else if (sub_command.equals_lower("increment")) { 326 bool success; 327 bool increment = OptionArgParser::ToBoolean(param, false, &success); 328 if (success) { 329 Timer::SetQuiet(!increment); 330 result.SetStatus(eReturnStatusSuccessFinishNoResult); 331 } else 332 result.AppendError("Could not convert increment value to boolean."); 333 } 334 } 335 336 if (!result.Succeeded()) { 337 result.AppendError("Missing subcommand"); 338 result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str()); 339 } 340 return result.Succeeded(); 341 } 342 }; 343 344 CommandObjectLog::CommandObjectLog(CommandInterpreter &interpreter) 345 : CommandObjectMultiword(interpreter, "log", 346 "Commands controlling LLDB internal logging.", 347 "log <subcommand> [<command-options>]") { 348 LoadSubCommand("enable", 349 CommandObjectSP(new CommandObjectLogEnable(interpreter))); 350 LoadSubCommand("disable", 351 CommandObjectSP(new CommandObjectLogDisable(interpreter))); 352 LoadSubCommand("list", 353 CommandObjectSP(new CommandObjectLogList(interpreter))); 354 LoadSubCommand("timers", 355 CommandObjectSP(new CommandObjectLogTimer(interpreter))); 356 } 357 358 CommandObjectLog::~CommandObjectLog() = default; 359