text: //===-- 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/CommandReturnObject.h"
  #include "lldb/Interpreter/OptionArgParser.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
  #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() {}
  
      ~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 't':
          log_options |= LLDB_LOG_OPTION_THREADSAFE;
          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();
        log_options = 0;
      }
  
      llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
        return llvm::makeArrayRef(g_log_options);
      }
  
      // Instance variables to hold the values for command options.
  
      FileSpec log_file;
      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;
      }
  
      // 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, 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 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("timers",
                   CommandObjectSP(new CommandObjectLogTimer(interpreter)));
  }
  
  CommandObjectLog::~CommandObjectLog() = default;
  