Path: blob/main/contrib/llvm-project/lldb/source/Commands/CommandObjectLog.cpp
39587 views
//===-- CommandObjectLog.cpp ----------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "CommandObjectLog.h"9#include "lldb/Core/Debugger.h"10#include "lldb/Host/OptionParser.h"11#include "lldb/Interpreter/CommandOptionArgumentTable.h"12#include "lldb/Interpreter/CommandReturnObject.h"13#include "lldb/Interpreter/OptionArgParser.h"14#include "lldb/Interpreter/OptionValueEnumeration.h"15#include "lldb/Interpreter/OptionValueUInt64.h"16#include "lldb/Interpreter/Options.h"17#include "lldb/Utility/Args.h"18#include "lldb/Utility/FileSpec.h"19#include "lldb/Utility/Log.h"20#include "lldb/Utility/Stream.h"21#include "lldb/Utility/Timer.h"2223using namespace lldb;24using namespace lldb_private;2526#define LLDB_OPTIONS_log_enable27#include "CommandOptions.inc"2829#define LLDB_OPTIONS_log_dump30#include "CommandOptions.inc"3132/// Common completion logic for log enable/disable.33static void CompleteEnableDisable(CompletionRequest &request) {34size_t arg_index = request.GetCursorIndex();35if (arg_index == 0) { // We got: log enable/disable x[tab]36for (llvm::StringRef channel : Log::ListChannels())37request.TryCompleteCurrentArg(channel);38} else if (arg_index >= 1) { // We got: log enable/disable channel x[tab]39llvm::StringRef channel = request.GetParsedLine().GetArgumentAtIndex(0);40Log::ForEachChannelCategory(41channel, [&request](llvm::StringRef name, llvm::StringRef desc) {42request.TryCompleteCurrentArg(name, desc);43});44}45}4647class CommandObjectLogEnable : public CommandObjectParsed {48public:49// Constructors and Destructors50CommandObjectLogEnable(CommandInterpreter &interpreter)51: CommandObjectParsed(interpreter, "log enable",52"Enable logging for a single log channel.",53nullptr) {54CommandArgumentEntry arg1;55CommandArgumentEntry arg2;56CommandArgumentData channel_arg;57CommandArgumentData category_arg;5859// Define the first (and only) variant of this arg.60channel_arg.arg_type = eArgTypeLogChannel;61channel_arg.arg_repetition = eArgRepeatPlain;6263// There is only one variant this argument could be; put it into the64// argument entry.65arg1.push_back(channel_arg);6667category_arg.arg_type = eArgTypeLogCategory;68category_arg.arg_repetition = eArgRepeatPlus;6970arg2.push_back(category_arg);7172// Push the data for the first argument into the m_arguments vector.73m_arguments.push_back(arg1);74m_arguments.push_back(arg2);75}7677~CommandObjectLogEnable() override = default;7879Options *GetOptions() override { return &m_options; }8081class CommandOptions : public Options {82public:83CommandOptions() = default;8485~CommandOptions() override = default;8687Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,88ExecutionContext *execution_context) override {89Status error;90const int short_option = m_getopt_table[option_idx].val;9192switch (short_option) {93case 'f':94log_file.SetFile(option_arg, FileSpec::Style::native);95FileSystem::Instance().Resolve(log_file);96break;97case 'h':98handler = (LogHandlerKind)OptionArgParser::ToOptionEnum(99option_arg, GetDefinitions()[option_idx].enum_values, 0, error);100if (!error.Success())101error.SetErrorStringWithFormat(102"unrecognized value for log handler '%s'",103option_arg.str().c_str());104break;105case 'b':106error =107buffer_size.SetValueFromString(option_arg, eVarSetOperationAssign);108break;109case 'v':110log_options |= LLDB_LOG_OPTION_VERBOSE;111break;112case 's':113log_options |= LLDB_LOG_OPTION_PREPEND_SEQUENCE;114break;115case 'T':116log_options |= LLDB_LOG_OPTION_PREPEND_TIMESTAMP;117break;118case 'p':119log_options |= LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD;120break;121case 'n':122log_options |= LLDB_LOG_OPTION_PREPEND_THREAD_NAME;123break;124case 'S':125log_options |= LLDB_LOG_OPTION_BACKTRACE;126break;127case 'a':128log_options |= LLDB_LOG_OPTION_APPEND;129break;130case 'F':131log_options |= LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION;132break;133default:134llvm_unreachable("Unimplemented option");135}136137return error;138}139140void OptionParsingStarting(ExecutionContext *execution_context) override {141log_file.Clear();142buffer_size.Clear();143handler = eLogHandlerStream;144log_options = 0;145}146147llvm::ArrayRef<OptionDefinition> GetDefinitions() override {148return llvm::ArrayRef(g_log_enable_options);149}150151FileSpec log_file;152OptionValueUInt64 buffer_size;153LogHandlerKind handler = eLogHandlerStream;154uint32_t log_options = 0;155};156157void158HandleArgumentCompletion(CompletionRequest &request,159OptionElementVector &opt_element_vector) override {160CompleteEnableDisable(request);161}162163protected:164void DoExecute(Args &args, CommandReturnObject &result) override {165if (args.GetArgumentCount() < 2) {166result.AppendErrorWithFormat(167"%s takes a log channel and one or more log types.\n",168m_cmd_name.c_str());169return;170}171172if (m_options.handler == eLogHandlerCircular &&173m_options.buffer_size.GetCurrentValue() == 0) {174result.AppendError(175"the circular buffer handler requires a non-zero buffer size.\n");176return;177}178179if ((m_options.handler != eLogHandlerCircular &&180m_options.handler != eLogHandlerStream) &&181m_options.buffer_size.GetCurrentValue() != 0) {182result.AppendError("a buffer size can only be specified for the circular "183"and stream buffer handler.\n");184return;185}186187if (m_options.handler != eLogHandlerStream && m_options.log_file) {188result.AppendError(189"a file name can only be specified for the stream handler.\n");190return;191}192193// Store into a std::string since we're about to shift the channel off.194const std::string channel = std::string(args[0].ref());195args.Shift(); // Shift off the channel196char log_file[PATH_MAX];197if (m_options.log_file)198m_options.log_file.GetPath(log_file, sizeof(log_file));199else200log_file[0] = '\0';201202std::string error;203llvm::raw_string_ostream error_stream(error);204bool success = GetDebugger().EnableLog(205channel, args.GetArgumentArrayRef(), log_file, m_options.log_options,206m_options.buffer_size.GetCurrentValue(), m_options.handler,207error_stream);208result.GetErrorStream() << error_stream.str();209210if (success)211result.SetStatus(eReturnStatusSuccessFinishNoResult);212else213result.SetStatus(eReturnStatusFailed);214}215216CommandOptions m_options;217};218219class CommandObjectLogDisable : public CommandObjectParsed {220public:221// Constructors and Destructors222CommandObjectLogDisable(CommandInterpreter &interpreter)223: CommandObjectParsed(interpreter, "log disable",224"Disable one or more log channel categories.",225nullptr) {226CommandArgumentEntry arg1;227CommandArgumentEntry arg2;228CommandArgumentData channel_arg;229CommandArgumentData category_arg;230231// Define the first (and only) variant of this arg.232channel_arg.arg_type = eArgTypeLogChannel;233channel_arg.arg_repetition = eArgRepeatPlain;234235// There is only one variant this argument could be; put it into the236// argument entry.237arg1.push_back(channel_arg);238239category_arg.arg_type = eArgTypeLogCategory;240category_arg.arg_repetition = eArgRepeatPlus;241242arg2.push_back(category_arg);243244// Push the data for the first argument into the m_arguments vector.245m_arguments.push_back(arg1);246m_arguments.push_back(arg2);247}248249~CommandObjectLogDisable() override = default;250251void252HandleArgumentCompletion(CompletionRequest &request,253OptionElementVector &opt_element_vector) override {254CompleteEnableDisable(request);255}256257protected:258void DoExecute(Args &args, CommandReturnObject &result) override {259if (args.empty()) {260result.AppendErrorWithFormat(261"%s takes a log channel and one or more log types.\n",262m_cmd_name.c_str());263return;264}265266const std::string channel = std::string(args[0].ref());267args.Shift(); // Shift off the channel268if (channel == "all") {269Log::DisableAllLogChannels();270result.SetStatus(eReturnStatusSuccessFinishNoResult);271} else {272std::string error;273llvm::raw_string_ostream error_stream(error);274if (Log::DisableLogChannel(channel, args.GetArgumentArrayRef(),275error_stream))276result.SetStatus(eReturnStatusSuccessFinishNoResult);277result.GetErrorStream() << error_stream.str();278}279}280};281282class CommandObjectLogList : public CommandObjectParsed {283public:284// Constructors and Destructors285CommandObjectLogList(CommandInterpreter &interpreter)286: CommandObjectParsed(interpreter, "log list",287"List the log categories for one or more log "288"channels. If none specified, lists them all.",289nullptr) {290AddSimpleArgumentList(eArgTypeLogChannel, eArgRepeatStar);291}292293~CommandObjectLogList() override = default;294295void296HandleArgumentCompletion(CompletionRequest &request,297OptionElementVector &opt_element_vector) override {298for (llvm::StringRef channel : Log::ListChannels())299request.TryCompleteCurrentArg(channel);300}301302protected:303void DoExecute(Args &args, CommandReturnObject &result) override {304std::string output;305llvm::raw_string_ostream output_stream(output);306if (args.empty()) {307Log::ListAllLogChannels(output_stream);308result.SetStatus(eReturnStatusSuccessFinishResult);309} else {310bool success = true;311for (const auto &entry : args.entries())312success =313success && Log::ListChannelCategories(entry.ref(), output_stream);314if (success)315result.SetStatus(eReturnStatusSuccessFinishResult);316}317result.GetOutputStream() << output_stream.str();318}319};320class CommandObjectLogDump : public CommandObjectParsed {321public:322CommandObjectLogDump(CommandInterpreter &interpreter)323: CommandObjectParsed(interpreter, "log dump",324"dump circular buffer logs", nullptr) {325AddSimpleArgumentList(eArgTypeLogChannel);326}327328~CommandObjectLogDump() override = default;329330Options *GetOptions() override { return &m_options; }331332class CommandOptions : public Options {333public:334CommandOptions() = default;335336~CommandOptions() override = default;337338Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,339ExecutionContext *execution_context) override {340Status error;341const int short_option = m_getopt_table[option_idx].val;342343switch (short_option) {344case 'f':345log_file.SetFile(option_arg, FileSpec::Style::native);346FileSystem::Instance().Resolve(log_file);347break;348default:349llvm_unreachable("Unimplemented option");350}351352return error;353}354355void OptionParsingStarting(ExecutionContext *execution_context) override {356log_file.Clear();357}358359llvm::ArrayRef<OptionDefinition> GetDefinitions() override {360return llvm::ArrayRef(g_log_dump_options);361}362363FileSpec log_file;364};365366void367HandleArgumentCompletion(CompletionRequest &request,368OptionElementVector &opt_element_vector) override {369CompleteEnableDisable(request);370}371372protected:373void DoExecute(Args &args, CommandReturnObject &result) override {374if (args.empty()) {375result.AppendErrorWithFormat(376"%s takes a log channel and one or more log types.\n",377m_cmd_name.c_str());378return;379}380381std::unique_ptr<llvm::raw_ostream> stream_up;382if (m_options.log_file) {383const File::OpenOptions flags = File::eOpenOptionWriteOnly |384File::eOpenOptionCanCreate |385File::eOpenOptionTruncate;386llvm::Expected<FileUP> file = FileSystem::Instance().Open(387m_options.log_file, flags, lldb::eFilePermissionsFileDefault, false);388if (!file) {389result.AppendErrorWithFormat("Unable to open log file '%s': %s",390m_options.log_file.GetPath().c_str(),391llvm::toString(file.takeError()).c_str());392return;393}394stream_up = std::make_unique<llvm::raw_fd_ostream>(395(*file)->GetDescriptor(), /*shouldClose=*/true);396} else {397stream_up = std::make_unique<llvm::raw_fd_ostream>(398GetDebugger().GetOutputFile().GetDescriptor(), /*shouldClose=*/false);399}400401const std::string channel = std::string(args[0].ref());402std::string error;403llvm::raw_string_ostream error_stream(error);404if (Log::DumpLogChannel(channel, *stream_up, error_stream)) {405result.SetStatus(eReturnStatusSuccessFinishNoResult);406} else {407result.SetStatus(eReturnStatusFailed);408result.GetErrorStream() << error_stream.str();409}410}411412CommandOptions m_options;413};414415class CommandObjectLogTimerEnable : public CommandObjectParsed {416public:417// Constructors and Destructors418CommandObjectLogTimerEnable(CommandInterpreter &interpreter)419: CommandObjectParsed(interpreter, "log timers enable",420"enable LLDB internal performance timers",421"log timers enable <depth>") {422AddSimpleArgumentList(eArgTypeCount, eArgRepeatOptional);423}424425~CommandObjectLogTimerEnable() override = default;426427protected:428void DoExecute(Args &args, CommandReturnObject &result) override {429result.SetStatus(eReturnStatusFailed);430431if (args.GetArgumentCount() == 0) {432Timer::SetDisplayDepth(UINT32_MAX);433result.SetStatus(eReturnStatusSuccessFinishNoResult);434} else if (args.GetArgumentCount() == 1) {435uint32_t depth;436if (args[0].ref().consumeInteger(0, depth)) {437result.AppendError(438"Could not convert enable depth to an unsigned integer.");439} else {440Timer::SetDisplayDepth(depth);441result.SetStatus(eReturnStatusSuccessFinishNoResult);442}443}444445if (!result.Succeeded()) {446result.AppendError("Missing subcommand");447result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str());448}449}450};451452class CommandObjectLogTimerDisable : public CommandObjectParsed {453public:454// Constructors and Destructors455CommandObjectLogTimerDisable(CommandInterpreter &interpreter)456: CommandObjectParsed(interpreter, "log timers disable",457"disable LLDB internal performance timers",458nullptr) {}459460~CommandObjectLogTimerDisable() override = default;461462protected:463void DoExecute(Args &args, CommandReturnObject &result) override {464Timer::DumpCategoryTimes(result.GetOutputStream());465Timer::SetDisplayDepth(0);466result.SetStatus(eReturnStatusSuccessFinishResult);467468if (!result.Succeeded()) {469result.AppendError("Missing subcommand");470result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str());471}472}473};474475class CommandObjectLogTimerDump : public CommandObjectParsed {476public:477// Constructors and Destructors478CommandObjectLogTimerDump(CommandInterpreter &interpreter)479: CommandObjectParsed(interpreter, "log timers dump",480"dump LLDB internal performance timers", nullptr) {}481482~CommandObjectLogTimerDump() override = default;483484protected:485void DoExecute(Args &args, CommandReturnObject &result) override {486Timer::DumpCategoryTimes(result.GetOutputStream());487result.SetStatus(eReturnStatusSuccessFinishResult);488489if (!result.Succeeded()) {490result.AppendError("Missing subcommand");491result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str());492}493}494};495496class CommandObjectLogTimerReset : public CommandObjectParsed {497public:498// Constructors and Destructors499CommandObjectLogTimerReset(CommandInterpreter &interpreter)500: CommandObjectParsed(interpreter, "log timers reset",501"reset LLDB internal performance timers", nullptr) {502}503504~CommandObjectLogTimerReset() override = default;505506protected:507void DoExecute(Args &args, CommandReturnObject &result) override {508Timer::ResetCategoryTimes();509result.SetStatus(eReturnStatusSuccessFinishResult);510511if (!result.Succeeded()) {512result.AppendError("Missing subcommand");513result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str());514}515}516};517518class CommandObjectLogTimerIncrement : public CommandObjectParsed {519public:520// Constructors and Destructors521CommandObjectLogTimerIncrement(CommandInterpreter &interpreter)522: CommandObjectParsed(interpreter, "log timers increment",523"increment LLDB internal performance timers",524"log timers increment <bool>") {525AddSimpleArgumentList(eArgTypeBoolean);526}527528~CommandObjectLogTimerIncrement() override = default;529530void531HandleArgumentCompletion(CompletionRequest &request,532OptionElementVector &opt_element_vector) override {533request.TryCompleteCurrentArg("true");534request.TryCompleteCurrentArg("false");535}536537protected:538void DoExecute(Args &args, CommandReturnObject &result) override {539result.SetStatus(eReturnStatusFailed);540541if (args.GetArgumentCount() == 1) {542bool success;543bool increment =544OptionArgParser::ToBoolean(args[0].ref(), false, &success);545546if (success) {547Timer::SetQuiet(!increment);548result.SetStatus(eReturnStatusSuccessFinishNoResult);549} else550result.AppendError("Could not convert increment value to boolean.");551}552553if (!result.Succeeded()) {554result.AppendError("Missing subcommand");555result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str());556}557}558};559560class CommandObjectLogTimer : public CommandObjectMultiword {561public:562CommandObjectLogTimer(CommandInterpreter &interpreter)563: CommandObjectMultiword(interpreter, "log timers",564"Enable, disable, dump, and reset LLDB internal "565"performance timers.",566"log timers < enable <depth> | disable | dump | "567"increment <bool> | reset >") {568LoadSubCommand("enable", CommandObjectSP(569new CommandObjectLogTimerEnable(interpreter)));570LoadSubCommand("disable", CommandObjectSP(new CommandObjectLogTimerDisable(571interpreter)));572LoadSubCommand("dump",573CommandObjectSP(new CommandObjectLogTimerDump(interpreter)));574LoadSubCommand(575"reset", CommandObjectSP(new CommandObjectLogTimerReset(interpreter)));576LoadSubCommand(577"increment",578CommandObjectSP(new CommandObjectLogTimerIncrement(interpreter)));579}580581~CommandObjectLogTimer() override = default;582};583584CommandObjectLog::CommandObjectLog(CommandInterpreter &interpreter)585: CommandObjectMultiword(interpreter, "log",586"Commands controlling LLDB internal logging.",587"log <subcommand> [<command-options>]") {588LoadSubCommand("enable",589CommandObjectSP(new CommandObjectLogEnable(interpreter)));590LoadSubCommand("disable",591CommandObjectSP(new CommandObjectLogDisable(interpreter)));592LoadSubCommand("list",593CommandObjectSP(new CommandObjectLogList(interpreter)));594LoadSubCommand("dump",595CommandObjectSP(new CommandObjectLogDump(interpreter)));596LoadSubCommand("timers",597CommandObjectSP(new CommandObjectLogTimer(interpreter)));598}599600CommandObjectLog::~CommandObjectLog() = default;601602603