| //===-- CommandObjectLog.cpp ----------------------------------------------===// | 
 | // | 
 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
 | // See https://llvm.org/LICENSE.txt for license information. | 
 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | #include "CommandObjectLog.h" | 
 | #include "lldb/Core/Debugger.h" | 
 | #include "lldb/Host/OptionParser.h" | 
 | #include "lldb/Interpreter/CommandOptionArgumentTable.h" | 
 | #include "lldb/Interpreter/CommandReturnObject.h" | 
 | #include "lldb/Interpreter/OptionArgParser.h" | 
 | #include "lldb/Interpreter/OptionValueEnumeration.h" | 
 | #include "lldb/Interpreter/OptionValueUInt64.h" | 
 | #include "lldb/Interpreter/Options.h" | 
 | #include "lldb/Utility/Args.h" | 
 | #include "lldb/Utility/FileSpec.h" | 
 | #include "lldb/Utility/Log.h" | 
 | #include "lldb/Utility/Stream.h" | 
 | #include "lldb/Utility/Timer.h" | 
 |  | 
 | using namespace lldb; | 
 | using namespace lldb_private; | 
 |  | 
 | #define LLDB_OPTIONS_log_enable | 
 | #include "CommandOptions.inc" | 
 |  | 
 | #define LLDB_OPTIONS_log_dump | 
 | #include "CommandOptions.inc" | 
 |  | 
 | /// Common completion logic for log enable/disable. | 
 | static void CompleteEnableDisable(CompletionRequest &request) { | 
 |   size_t arg_index = request.GetCursorIndex(); | 
 |   if (arg_index == 0) { // We got: log enable/disable x[tab] | 
 |     for (llvm::StringRef channel : Log::ListChannels()) | 
 |       request.TryCompleteCurrentArg(channel); | 
 |   } else if (arg_index >= 1) { // We got: log enable/disable channel x[tab] | 
 |     llvm::StringRef channel = request.GetParsedLine().GetArgumentAtIndex(0); | 
 |     Log::ForEachChannelCategory( | 
 |         channel, [&request](llvm::StringRef name, llvm::StringRef desc) { | 
 |           request.TryCompleteCurrentArg(name, desc); | 
 |         }); | 
 |   } | 
 | } | 
 |  | 
 | class CommandObjectLogEnable : public CommandObjectParsed { | 
 | public: | 
 |   // Constructors and Destructors | 
 |   CommandObjectLogEnable(CommandInterpreter &interpreter) | 
 |       : CommandObjectParsed(interpreter, "log enable", | 
 |                             "Enable logging for a single log channel.", | 
 |                             nullptr) { | 
 |     CommandArgumentEntry arg1; | 
 |     CommandArgumentEntry arg2; | 
 |     CommandArgumentData channel_arg; | 
 |     CommandArgumentData category_arg; | 
 |  | 
 |     // Define the first (and only) variant of this arg. | 
 |     channel_arg.arg_type = eArgTypeLogChannel; | 
 |     channel_arg.arg_repetition = eArgRepeatPlain; | 
 |  | 
 |     // There is only one variant this argument could be; put it into the | 
 |     // argument entry. | 
 |     arg1.push_back(channel_arg); | 
 |  | 
 |     category_arg.arg_type = eArgTypeLogCategory; | 
 |     category_arg.arg_repetition = eArgRepeatPlus; | 
 |  | 
 |     arg2.push_back(category_arg); | 
 |  | 
 |     // Push the data for the first argument into the m_arguments vector. | 
 |     m_arguments.push_back(arg1); | 
 |     m_arguments.push_back(arg2); | 
 |   } | 
 |  | 
 |   ~CommandObjectLogEnable() override = default; | 
 |  | 
 |   Options *GetOptions() override { return &m_options; } | 
 |  | 
 |   class CommandOptions : public Options { | 
 |   public: | 
 |     CommandOptions() = default; | 
 |  | 
 |     ~CommandOptions() override = default; | 
 |  | 
 |     Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, | 
 |                           ExecutionContext *execution_context) override { | 
 |       Status error; | 
 |       const int short_option = m_getopt_table[option_idx].val; | 
 |  | 
 |       switch (short_option) { | 
 |       case 'f': | 
 |         log_file.SetFile(option_arg, FileSpec::Style::native); | 
 |         FileSystem::Instance().Resolve(log_file); | 
 |         break; | 
 |       case 'h': | 
 |         handler = (LogHandlerKind)OptionArgParser::ToOptionEnum( | 
 |             option_arg, GetDefinitions()[option_idx].enum_values, 0, error); | 
 |         if (!error.Success()) | 
 |           error.SetErrorStringWithFormat( | 
 |               "unrecognized value for log handler '%s'", | 
 |               option_arg.str().c_str()); | 
 |         break; | 
 |       case 'b': | 
 |         error = | 
 |             buffer_size.SetValueFromString(option_arg, eVarSetOperationAssign); | 
 |         break; | 
 |       case 'v': | 
 |         log_options |= LLDB_LOG_OPTION_VERBOSE; | 
 |         break; | 
 |       case 's': | 
 |         log_options |= LLDB_LOG_OPTION_PREPEND_SEQUENCE; | 
 |         break; | 
 |       case 'T': | 
 |         log_options |= LLDB_LOG_OPTION_PREPEND_TIMESTAMP; | 
 |         break; | 
 |       case 'p': | 
 |         log_options |= LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD; | 
 |         break; | 
 |       case 'n': | 
 |         log_options |= LLDB_LOG_OPTION_PREPEND_THREAD_NAME; | 
 |         break; | 
 |       case 'S': | 
 |         log_options |= LLDB_LOG_OPTION_BACKTRACE; | 
 |         break; | 
 |       case 'a': | 
 |         log_options |= LLDB_LOG_OPTION_APPEND; | 
 |         break; | 
 |       case 'F': | 
 |         log_options |= LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION; | 
 |         break; | 
 |       default: | 
 |         llvm_unreachable("Unimplemented option"); | 
 |       } | 
 |  | 
 |       return error; | 
 |     } | 
 |  | 
 |     void OptionParsingStarting(ExecutionContext *execution_context) override { | 
 |       log_file.Clear(); | 
 |       buffer_size.Clear(); | 
 |       handler = eLogHandlerStream; | 
 |       log_options = 0; | 
 |     } | 
 |  | 
 |     llvm::ArrayRef<OptionDefinition> GetDefinitions() override { | 
 |       return llvm::ArrayRef(g_log_enable_options); | 
 |     } | 
 |  | 
 |     FileSpec log_file; | 
 |     OptionValueUInt64 buffer_size; | 
 |     LogHandlerKind handler = eLogHandlerStream; | 
 |     uint32_t log_options = 0; | 
 |   }; | 
 |  | 
 |   void | 
 |   HandleArgumentCompletion(CompletionRequest &request, | 
 |                            OptionElementVector &opt_element_vector) override { | 
 |     CompleteEnableDisable(request); | 
 |   } | 
 |  | 
 | protected: | 
 |   bool DoExecute(Args &args, CommandReturnObject &result) override { | 
 |     if (args.GetArgumentCount() < 2) { | 
 |       result.AppendErrorWithFormat( | 
 |           "%s takes a log channel and one or more log types.\n", | 
 |           m_cmd_name.c_str()); | 
 |       return false; | 
 |     } | 
 |  | 
 |     if (m_options.handler == eLogHandlerCircular && | 
 |         m_options.buffer_size.GetCurrentValue() == 0) { | 
 |       result.AppendError( | 
 |           "the circular buffer handler requires a non-zero buffer size.\n"); | 
 |       return false; | 
 |     } | 
 |  | 
 |     if ((m_options.handler != eLogHandlerCircular && | 
 |          m_options.handler != eLogHandlerStream) && | 
 |         m_options.buffer_size.GetCurrentValue() != 0) { | 
 |       result.AppendError("a buffer size can only be specified for the circular " | 
 |                          "and stream buffer handler.\n"); | 
 |       return false; | 
 |     } | 
 |  | 
 |     if (m_options.handler != eLogHandlerStream && m_options.log_file) { | 
 |       result.AppendError( | 
 |           "a file name can only be specified for the stream handler.\n"); | 
 |       return false; | 
 |     } | 
 |  | 
 |     // Store into a std::string since we're about to shift the channel off. | 
 |     const std::string channel = std::string(args[0].ref()); | 
 |     args.Shift(); // Shift off the channel | 
 |     char log_file[PATH_MAX]; | 
 |     if (m_options.log_file) | 
 |       m_options.log_file.GetPath(log_file, sizeof(log_file)); | 
 |     else | 
 |       log_file[0] = '\0'; | 
 |  | 
 |     std::string error; | 
 |     llvm::raw_string_ostream error_stream(error); | 
 |     bool success = GetDebugger().EnableLog( | 
 |         channel, args.GetArgumentArrayRef(), log_file, m_options.log_options, | 
 |         m_options.buffer_size.GetCurrentValue(), m_options.handler, | 
 |         error_stream); | 
 |     result.GetErrorStream() << error_stream.str(); | 
 |  | 
 |     if (success) | 
 |       result.SetStatus(eReturnStatusSuccessFinishNoResult); | 
 |     else | 
 |       result.SetStatus(eReturnStatusFailed); | 
 |     return result.Succeeded(); | 
 |   } | 
 |  | 
 |   CommandOptions m_options; | 
 | }; | 
 |  | 
 | class CommandObjectLogDisable : public CommandObjectParsed { | 
 | public: | 
 |   // Constructors and Destructors | 
 |   CommandObjectLogDisable(CommandInterpreter &interpreter) | 
 |       : CommandObjectParsed(interpreter, "log disable", | 
 |                             "Disable one or more log channel categories.", | 
 |                             nullptr) { | 
 |     CommandArgumentEntry arg1; | 
 |     CommandArgumentEntry arg2; | 
 |     CommandArgumentData channel_arg; | 
 |     CommandArgumentData category_arg; | 
 |  | 
 |     // Define the first (and only) variant of this arg. | 
 |     channel_arg.arg_type = eArgTypeLogChannel; | 
 |     channel_arg.arg_repetition = eArgRepeatPlain; | 
 |  | 
 |     // There is only one variant this argument could be; put it into the | 
 |     // argument entry. | 
 |     arg1.push_back(channel_arg); | 
 |  | 
 |     category_arg.arg_type = eArgTypeLogCategory; | 
 |     category_arg.arg_repetition = eArgRepeatPlus; | 
 |  | 
 |     arg2.push_back(category_arg); | 
 |  | 
 |     // Push the data for the first argument into the m_arguments vector. | 
 |     m_arguments.push_back(arg1); | 
 |     m_arguments.push_back(arg2); | 
 |   } | 
 |  | 
 |   ~CommandObjectLogDisable() override = default; | 
 |  | 
 |   void | 
 |   HandleArgumentCompletion(CompletionRequest &request, | 
 |                            OptionElementVector &opt_element_vector) override { | 
 |     CompleteEnableDisable(request); | 
 |   } | 
 |  | 
 | protected: | 
 |   bool DoExecute(Args &args, CommandReturnObject &result) override { | 
 |     if (args.empty()) { | 
 |       result.AppendErrorWithFormat( | 
 |           "%s takes a log channel and one or more log types.\n", | 
 |           m_cmd_name.c_str()); | 
 |       return false; | 
 |     } | 
 |  | 
 |     const std::string channel = std::string(args[0].ref()); | 
 |     args.Shift(); // Shift off the channel | 
 |     if (channel == "all") { | 
 |       Log::DisableAllLogChannels(); | 
 |       result.SetStatus(eReturnStatusSuccessFinishNoResult); | 
 |     } else { | 
 |       std::string error; | 
 |       llvm::raw_string_ostream error_stream(error); | 
 |       if (Log::DisableLogChannel(channel, args.GetArgumentArrayRef(), | 
 |                                  error_stream)) | 
 |         result.SetStatus(eReturnStatusSuccessFinishNoResult); | 
 |       result.GetErrorStream() << error_stream.str(); | 
 |     } | 
 |     return result.Succeeded(); | 
 |   } | 
 | }; | 
 |  | 
 | class CommandObjectLogList : public CommandObjectParsed { | 
 | public: | 
 |   // Constructors and Destructors | 
 |   CommandObjectLogList(CommandInterpreter &interpreter) | 
 |       : CommandObjectParsed(interpreter, "log list", | 
 |                             "List the log categories for one or more log " | 
 |                             "channels.  If none specified, lists them all.", | 
 |                             nullptr) { | 
 |     CommandArgumentEntry arg; | 
 |     CommandArgumentData channel_arg; | 
 |  | 
 |     // Define the first (and only) variant of this arg. | 
 |     channel_arg.arg_type = eArgTypeLogChannel; | 
 |     channel_arg.arg_repetition = eArgRepeatStar; | 
 |  | 
 |     // There is only one variant this argument could be; put it into the | 
 |     // argument entry. | 
 |     arg.push_back(channel_arg); | 
 |  | 
 |     // Push the data for the first argument into the m_arguments vector. | 
 |     m_arguments.push_back(arg); | 
 |   } | 
 |  | 
 |   ~CommandObjectLogList() override = default; | 
 |  | 
 |   void | 
 |   HandleArgumentCompletion(CompletionRequest &request, | 
 |                            OptionElementVector &opt_element_vector) override { | 
 |     for (llvm::StringRef channel : Log::ListChannels()) | 
 |       request.TryCompleteCurrentArg(channel); | 
 |   } | 
 |  | 
 | protected: | 
 |   bool DoExecute(Args &args, CommandReturnObject &result) override { | 
 |     std::string output; | 
 |     llvm::raw_string_ostream output_stream(output); | 
 |     if (args.empty()) { | 
 |       Log::ListAllLogChannels(output_stream); | 
 |       result.SetStatus(eReturnStatusSuccessFinishResult); | 
 |     } else { | 
 |       bool success = true; | 
 |       for (const auto &entry : args.entries()) | 
 |         success = | 
 |             success && Log::ListChannelCategories(entry.ref(), output_stream); | 
 |       if (success) | 
 |         result.SetStatus(eReturnStatusSuccessFinishResult); | 
 |     } | 
 |     result.GetOutputStream() << output_stream.str(); | 
 |     return result.Succeeded(); | 
 |   } | 
 | }; | 
 | class CommandObjectLogDump : public CommandObjectParsed { | 
 | public: | 
 |   CommandObjectLogDump(CommandInterpreter &interpreter) | 
 |       : CommandObjectParsed(interpreter, "log dump", | 
 |                             "dump circular buffer logs", nullptr) { | 
 |     CommandArgumentEntry arg1; | 
 |     CommandArgumentData channel_arg; | 
 |  | 
 |     // Define the first (and only) variant of this arg. | 
 |     channel_arg.arg_type = eArgTypeLogChannel; | 
 |     channel_arg.arg_repetition = eArgRepeatPlain; | 
 |  | 
 |     // There is only one variant this argument could be; put it into the | 
 |     // argument entry. | 
 |     arg1.push_back(channel_arg); | 
 |  | 
 |     // Push the data for the first argument into the m_arguments vector. | 
 |     m_arguments.push_back(arg1); | 
 |   } | 
 |  | 
 |   ~CommandObjectLogDump() override = default; | 
 |  | 
 |   Options *GetOptions() override { return &m_options; } | 
 |  | 
 |   class CommandOptions : public Options { | 
 |   public: | 
 |     CommandOptions() = default; | 
 |  | 
 |     ~CommandOptions() override = default; | 
 |  | 
 |     Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, | 
 |                           ExecutionContext *execution_context) override { | 
 |       Status error; | 
 |       const int short_option = m_getopt_table[option_idx].val; | 
 |  | 
 |       switch (short_option) { | 
 |       case 'f': | 
 |         log_file.SetFile(option_arg, FileSpec::Style::native); | 
 |         FileSystem::Instance().Resolve(log_file); | 
 |         break; | 
 |       default: | 
 |         llvm_unreachable("Unimplemented option"); | 
 |       } | 
 |  | 
 |       return error; | 
 |     } | 
 |  | 
 |     void OptionParsingStarting(ExecutionContext *execution_context) override { | 
 |       log_file.Clear(); | 
 |     } | 
 |  | 
 |     llvm::ArrayRef<OptionDefinition> GetDefinitions() override { | 
 |       return llvm::ArrayRef(g_log_dump_options); | 
 |     } | 
 |  | 
 |     FileSpec log_file; | 
 |   }; | 
 |  | 
 |   void | 
 |   HandleArgumentCompletion(CompletionRequest &request, | 
 |                            OptionElementVector &opt_element_vector) override { | 
 |     CompleteEnableDisable(request); | 
 |   } | 
 |  | 
 | protected: | 
 |   bool DoExecute(Args &args, CommandReturnObject &result) override { | 
 |     if (args.empty()) { | 
 |       result.AppendErrorWithFormat( | 
 |           "%s takes a log channel and one or more log types.\n", | 
 |           m_cmd_name.c_str()); | 
 |       return false; | 
 |     } | 
 |  | 
 |     std::unique_ptr<llvm::raw_ostream> stream_up; | 
 |     if (m_options.log_file) { | 
 |       const File::OpenOptions flags = File::eOpenOptionWriteOnly | | 
 |                                       File::eOpenOptionCanCreate | | 
 |                                       File::eOpenOptionTruncate; | 
 |       llvm::Expected<FileUP> file = FileSystem::Instance().Open( | 
 |           m_options.log_file, flags, lldb::eFilePermissionsFileDefault, false); | 
 |       if (!file) { | 
 |         result.AppendErrorWithFormat("Unable to open log file '%s': %s", | 
 |                                      m_options.log_file.GetPath().c_str(), | 
 |                                      llvm::toString(file.takeError()).c_str()); | 
 |         return false; | 
 |       } | 
 |       stream_up = std::make_unique<llvm::raw_fd_ostream>( | 
 |           (*file)->GetDescriptor(), /*shouldClose=*/true); | 
 |     } else { | 
 |       stream_up = std::make_unique<llvm::raw_fd_ostream>( | 
 |           GetDebugger().GetOutputFile().GetDescriptor(), /*shouldClose=*/false); | 
 |     } | 
 |  | 
 |     const std::string channel = std::string(args[0].ref()); | 
 |     std::string error; | 
 |     llvm::raw_string_ostream error_stream(error); | 
 |     if (Log::DumpLogChannel(channel, *stream_up, error_stream)) { | 
 |       result.SetStatus(eReturnStatusSuccessFinishNoResult); | 
 |     } else { | 
 |       result.SetStatus(eReturnStatusFailed); | 
 |       result.GetErrorStream() << error_stream.str(); | 
 |     } | 
 |  | 
 |     return result.Succeeded(); | 
 |   } | 
 |  | 
 |   CommandOptions m_options; | 
 | }; | 
 |  | 
 | class CommandObjectLogTimerEnable : public CommandObjectParsed { | 
 | public: | 
 |   // Constructors and Destructors | 
 |   CommandObjectLogTimerEnable(CommandInterpreter &interpreter) | 
 |       : CommandObjectParsed(interpreter, "log timers enable", | 
 |                             "enable LLDB internal performance timers", | 
 |                             "log timers enable <depth>") { | 
 |     CommandArgumentEntry arg; | 
 |     CommandArgumentData depth_arg; | 
 |  | 
 |     // Define the first (and only) variant of this arg. | 
 |     depth_arg.arg_type = eArgTypeCount; | 
 |     depth_arg.arg_repetition = eArgRepeatOptional; | 
 |  | 
 |     // There is only one variant this argument could be; put it into the | 
 |     // argument entry. | 
 |     arg.push_back(depth_arg); | 
 |  | 
 |     // Push the data for the first argument into the m_arguments vector. | 
 |     m_arguments.push_back(arg); | 
 |   } | 
 |  | 
 |   ~CommandObjectLogTimerEnable() override = default; | 
 |  | 
 | protected: | 
 |   bool DoExecute(Args &args, CommandReturnObject &result) override { | 
 |     result.SetStatus(eReturnStatusFailed); | 
 |  | 
 |     if (args.GetArgumentCount() == 0) { | 
 |       Timer::SetDisplayDepth(UINT32_MAX); | 
 |       result.SetStatus(eReturnStatusSuccessFinishNoResult); | 
 |     } else if (args.GetArgumentCount() == 1) { | 
 |       uint32_t depth; | 
 |       if (args[0].ref().consumeInteger(0, depth)) { | 
 |         result.AppendError( | 
 |             "Could not convert enable depth to an unsigned integer."); | 
 |       } else { | 
 |         Timer::SetDisplayDepth(depth); | 
 |         result.SetStatus(eReturnStatusSuccessFinishNoResult); | 
 |       } | 
 |     } | 
 |  | 
 |     if (!result.Succeeded()) { | 
 |       result.AppendError("Missing subcommand"); | 
 |       result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str()); | 
 |     } | 
 |     return result.Succeeded(); | 
 |   } | 
 | }; | 
 |  | 
 | class CommandObjectLogTimerDisable : public CommandObjectParsed { | 
 | public: | 
 |   // Constructors and Destructors | 
 |   CommandObjectLogTimerDisable(CommandInterpreter &interpreter) | 
 |       : CommandObjectParsed(interpreter, "log timers disable", | 
 |                             "disable LLDB internal performance timers", | 
 |                             nullptr) {} | 
 |  | 
 |   ~CommandObjectLogTimerDisable() override = default; | 
 |  | 
 | protected: | 
 |   bool DoExecute(Args &args, CommandReturnObject &result) override { | 
 |     Timer::DumpCategoryTimes(result.GetOutputStream()); | 
 |     Timer::SetDisplayDepth(0); | 
 |     result.SetStatus(eReturnStatusSuccessFinishResult); | 
 |  | 
 |     if (!result.Succeeded()) { | 
 |       result.AppendError("Missing subcommand"); | 
 |       result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str()); | 
 |     } | 
 |     return result.Succeeded(); | 
 |   } | 
 | }; | 
 |  | 
 | class CommandObjectLogTimerDump : public CommandObjectParsed { | 
 | public: | 
 |   // Constructors and Destructors | 
 |   CommandObjectLogTimerDump(CommandInterpreter &interpreter) | 
 |       : CommandObjectParsed(interpreter, "log timers dump", | 
 |                             "dump LLDB internal performance timers", nullptr) {} | 
 |  | 
 |   ~CommandObjectLogTimerDump() override = default; | 
 |  | 
 | protected: | 
 |   bool DoExecute(Args &args, CommandReturnObject &result) override { | 
 |     Timer::DumpCategoryTimes(result.GetOutputStream()); | 
 |     result.SetStatus(eReturnStatusSuccessFinishResult); | 
 |  | 
 |     if (!result.Succeeded()) { | 
 |       result.AppendError("Missing subcommand"); | 
 |       result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str()); | 
 |     } | 
 |     return result.Succeeded(); | 
 |   } | 
 | }; | 
 |  | 
 | class CommandObjectLogTimerReset : public CommandObjectParsed { | 
 | public: | 
 |   // Constructors and Destructors | 
 |   CommandObjectLogTimerReset(CommandInterpreter &interpreter) | 
 |       : CommandObjectParsed(interpreter, "log timers reset", | 
 |                             "reset LLDB internal performance timers", nullptr) { | 
 |   } | 
 |  | 
 |   ~CommandObjectLogTimerReset() override = default; | 
 |  | 
 | protected: | 
 |   bool DoExecute(Args &args, CommandReturnObject &result) override { | 
 |     Timer::ResetCategoryTimes(); | 
 |     result.SetStatus(eReturnStatusSuccessFinishResult); | 
 |  | 
 |     if (!result.Succeeded()) { | 
 |       result.AppendError("Missing subcommand"); | 
 |       result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str()); | 
 |     } | 
 |     return result.Succeeded(); | 
 |   } | 
 | }; | 
 |  | 
 | class CommandObjectLogTimerIncrement : public CommandObjectParsed { | 
 | public: | 
 |   // Constructors and Destructors | 
 |   CommandObjectLogTimerIncrement(CommandInterpreter &interpreter) | 
 |       : CommandObjectParsed(interpreter, "log timers increment", | 
 |                             "increment LLDB internal performance timers", | 
 |                             "log timers increment <bool>") { | 
 |     CommandArgumentEntry arg; | 
 |     CommandArgumentData bool_arg; | 
 |  | 
 |     // Define the first (and only) variant of this arg. | 
 |     bool_arg.arg_type = eArgTypeBoolean; | 
 |     bool_arg.arg_repetition = eArgRepeatPlain; | 
 |  | 
 |     // There is only one variant this argument could be; put it into the | 
 |     // argument entry. | 
 |     arg.push_back(bool_arg); | 
 |  | 
 |     // Push the data for the first argument into the m_arguments vector. | 
 |     m_arguments.push_back(arg); | 
 |   } | 
 |  | 
 |   ~CommandObjectLogTimerIncrement() override = default; | 
 |  | 
 |   void | 
 |   HandleArgumentCompletion(CompletionRequest &request, | 
 |                            OptionElementVector &opt_element_vector) override { | 
 |     request.TryCompleteCurrentArg("true"); | 
 |     request.TryCompleteCurrentArg("false"); | 
 |   } | 
 |  | 
 | protected: | 
 |   bool DoExecute(Args &args, CommandReturnObject &result) override { | 
 |     result.SetStatus(eReturnStatusFailed); | 
 |  | 
 |     if (args.GetArgumentCount() == 1) { | 
 |       bool success; | 
 |       bool increment = | 
 |           OptionArgParser::ToBoolean(args[0].ref(), false, &success); | 
 |  | 
 |       if (success) { | 
 |         Timer::SetQuiet(!increment); | 
 |         result.SetStatus(eReturnStatusSuccessFinishNoResult); | 
 |       } else | 
 |         result.AppendError("Could not convert increment value to boolean."); | 
 |     } | 
 |  | 
 |     if (!result.Succeeded()) { | 
 |       result.AppendError("Missing subcommand"); | 
 |       result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str()); | 
 |     } | 
 |     return result.Succeeded(); | 
 |   } | 
 | }; | 
 |  | 
 | class CommandObjectLogTimer : public CommandObjectMultiword { | 
 | public: | 
 |   CommandObjectLogTimer(CommandInterpreter &interpreter) | 
 |       : CommandObjectMultiword(interpreter, "log timers", | 
 |                                "Enable, disable, dump, and reset LLDB internal " | 
 |                                "performance timers.", | 
 |                                "log timers < enable <depth> | disable | dump | " | 
 |                                "increment <bool> | reset >") { | 
 |     LoadSubCommand("enable", CommandObjectSP( | 
 |                                  new CommandObjectLogTimerEnable(interpreter))); | 
 |     LoadSubCommand("disable", CommandObjectSP(new CommandObjectLogTimerDisable( | 
 |                                   interpreter))); | 
 |     LoadSubCommand("dump", | 
 |                    CommandObjectSP(new CommandObjectLogTimerDump(interpreter))); | 
 |     LoadSubCommand( | 
 |         "reset", CommandObjectSP(new CommandObjectLogTimerReset(interpreter))); | 
 |     LoadSubCommand( | 
 |         "increment", | 
 |         CommandObjectSP(new CommandObjectLogTimerIncrement(interpreter))); | 
 |   } | 
 |  | 
 |   ~CommandObjectLogTimer() override = default; | 
 | }; | 
 |  | 
 | CommandObjectLog::CommandObjectLog(CommandInterpreter &interpreter) | 
 |     : CommandObjectMultiword(interpreter, "log", | 
 |                              "Commands controlling LLDB internal logging.", | 
 |                              "log <subcommand> [<command-options>]") { | 
 |   LoadSubCommand("enable", | 
 |                  CommandObjectSP(new CommandObjectLogEnable(interpreter))); | 
 |   LoadSubCommand("disable", | 
 |                  CommandObjectSP(new CommandObjectLogDisable(interpreter))); | 
 |   LoadSubCommand("list", | 
 |                  CommandObjectSP(new CommandObjectLogList(interpreter))); | 
 |   LoadSubCommand("dump", | 
 |                  CommandObjectSP(new CommandObjectLogDump(interpreter))); | 
 |   LoadSubCommand("timers", | 
 |                  CommandObjectSP(new CommandObjectLogTimer(interpreter))); | 
 | } | 
 |  | 
 | CommandObjectLog::~CommandObjectLog() = default; |