llvm-project/lldb/source/Commands/CommandObjectStats.cpp
qxy11 5c310d1ef0
Default transcript dumping in "statistics dump" to false (#145436)
### Summary
Currently, if the setting `interpreter.save-transcript` is enabled,
whenever we call "statistics dump", it'll default to reporting a huge
list of transcripts which can be a bit noisy. This is because the
current check `GetIncludeTranscript` returns `!GetSummaryOnly()` by
default if no specific transcript-setting option is given in the
statistics dump command (ie. `statistics dump --transcripts=false` or
`statistics dump --transcripts=true`). Then when
`interpreter.save-transcript` is enabled, this saves a list of
transcripts, and the transcript list ends up getting logged by default.

These changes default the option to log transcripts in the `statistics
dump` command to "false". This can still be enabled via the
`--transcripts` option if users want to see a transcript. Since
`interpreter.save-transcript` is false by default, the main delta is
that if `interpreter.save-transcript` is true and summary mode is false,
we now disable saving the transcript.

This also adds a warning to 'statistics dump --transcript=true' when
interpreter.save-transcript is disabled, which should help users
understand
why transcript data is empty.

### Testing

#### Manual testing
Tested with `settings set interpreter.save-transcript true` enabled at
startup on a toy hello-world program:
```
(lldb) settings set interpreter.save-transcript true
(lldb) target create "/home/qxy11/hello-world/a.out"
Current executable set to '/home/qxy11/hello-world/a.out' (x86_64).
(lldb) statistics dump
{
  /* no transcript */
}
(lldb) statistics dump --transcript=true
{
"transcript": [
    {
      "command": "statistics dump",
      "commandArguments": "",
      "commandName": "statistics dump",
      "durationInSeconds": 0.0019650000000000002,
      "error": "",
      "output": "{...
    },
    {
      "command": "statistics dump --transcript=true",
      "commandArguments": "--transcript=true",
      "commandName": "statistics dump",
      "timestampInEpochSeconds": 1750720021
    }
  ]
}
```
Without `settings set interpreter.save-transcript true`:
```
(lldb) target create "/home/qxy11/hello-world/a.out"
Current executable set to '/home/qxy11/hello-world/a.out' (x86_64).
(lldb) statistics dump
{
  /* no transcript */
}
(lldb) statistics dump --transcript=true
{
  /* no transcript */
}
warning: transcript requested but none was saved. Enable with 'settings set interpreter.save-transcript true'
```

#### Unit tests
Changed unit tests to account for new expected default behavior to
`false`, and added a couple new tests around expected behavior with
`--transcript=true`.
```
lldb-dotest -p TestStats ~/llvm-sand/external/llvm-project/lldb/test/API/commands/statistics/basic/
```
2025-06-26 10:16:05 -07:00

182 lines
6.3 KiB
C++

//===-- CommandObjectStats.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 "CommandObjectStats.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Host/OptionParser.h"
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Interpreter/CommandOptionArgumentTable.h"
#include "lldb/Interpreter/CommandReturnObject.h"
#include "lldb/Interpreter/OptionArgParser.h"
#include "lldb/Target/Target.h"
using namespace lldb;
using namespace lldb_private;
class CommandObjectStatsEnable : public CommandObjectParsed {
public:
CommandObjectStatsEnable(CommandInterpreter &interpreter)
: CommandObjectParsed(interpreter, "enable",
"Enable statistics collection", nullptr,
eCommandProcessMustBePaused) {}
~CommandObjectStatsEnable() override = default;
protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
if (DebuggerStats::GetCollectingStats()) {
result.AppendError("statistics already enabled");
return;
}
DebuggerStats::SetCollectingStats(true);
result.SetStatus(eReturnStatusSuccessFinishResult);
}
};
class CommandObjectStatsDisable : public CommandObjectParsed {
public:
CommandObjectStatsDisable(CommandInterpreter &interpreter)
: CommandObjectParsed(interpreter, "disable",
"Disable statistics collection", nullptr,
eCommandProcessMustBePaused) {}
~CommandObjectStatsDisable() override = default;
protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
if (!DebuggerStats::GetCollectingStats()) {
result.AppendError("need to enable statistics before disabling them");
return;
}
DebuggerStats::SetCollectingStats(false);
result.SetStatus(eReturnStatusSuccessFinishResult);
}
};
#define LLDB_OPTIONS_statistics_dump
#include "CommandOptions.inc"
class CommandObjectStatsDump : public CommandObjectParsed {
class CommandOptions : public Options {
public:
CommandOptions() { OptionParsingStarting(nullptr); }
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 'a':
m_all_targets = true;
break;
case 's':
m_stats_options.SetSummaryOnly(true);
break;
case 'f':
m_stats_options.SetLoadAllDebugInfo(true);
break;
case 'r':
if (llvm::Expected<bool> bool_or_error =
OptionArgParser::ToBoolean("--targets", option_arg))
m_stats_options.SetIncludeTargets(*bool_or_error);
else
error = Status::FromError(bool_or_error.takeError());
break;
case 'm':
if (llvm::Expected<bool> bool_or_error =
OptionArgParser::ToBoolean("--modules", option_arg))
m_stats_options.SetIncludeModules(*bool_or_error);
else
error = Status::FromError(bool_or_error.takeError());
break;
case 't':
if (llvm::Expected<bool> bool_or_error =
OptionArgParser::ToBoolean("--transcript", option_arg))
m_stats_options.SetIncludeTranscript(*bool_or_error);
else
error = Status::FromError(bool_or_error.takeError());
break;
case 'p':
if (llvm::Expected<bool> bool_or_error =
OptionArgParser::ToBoolean("--plugins", option_arg))
m_stats_options.SetIncludePlugins(*bool_or_error);
else
error = Status::FromError(bool_or_error.takeError());
break;
default:
llvm_unreachable("Unimplemented option");
}
return error;
}
void OptionParsingStarting(ExecutionContext *execution_context) override {
m_all_targets = false;
m_stats_options = StatisticsOptions();
}
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
return llvm::ArrayRef(g_statistics_dump_options);
}
const StatisticsOptions &GetStatisticsOptions() { return m_stats_options; }
bool m_all_targets = false;
StatisticsOptions m_stats_options = StatisticsOptions();
};
public:
CommandObjectStatsDump(CommandInterpreter &interpreter)
: CommandObjectParsed(
interpreter, "statistics dump", "Dump metrics in JSON format",
"statistics dump [<options>]", eCommandRequiresTarget) {}
~CommandObjectStatsDump() override = default;
Options *GetOptions() override { return &m_options; }
protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
Target *target = nullptr;
if (!m_options.m_all_targets)
target = m_exe_ctx.GetTargetPtr();
// Check if transcript is requested but transcript saving is disabled
const StatisticsOptions &stats_options = m_options.GetStatisticsOptions();
if (stats_options.GetIncludeTranscript() &&
!GetDebugger().GetCommandInterpreter().GetSaveTranscript()) {
result.AppendWarning(
"transcript requested but none was saved. Enable with "
"'settings set interpreter.save-transcript true'");
}
result.AppendMessageWithFormatv(
"{0:2}",
DebuggerStats::ReportStatistics(GetDebugger(), target, stats_options));
result.SetStatus(eReturnStatusSuccessFinishResult);
}
CommandOptions m_options;
};
CommandObjectStats::CommandObjectStats(CommandInterpreter &interpreter)
: CommandObjectMultiword(interpreter, "statistics",
"Print statistics about a debugging session",
"statistics <subcommand> [<subcommand-options>]") {
LoadSubCommand("enable",
CommandObjectSP(new CommandObjectStatsEnable(interpreter)));
LoadSubCommand("disable",
CommandObjectSP(new CommandObjectStatsDisable(interpreter)));
LoadSubCommand("dump",
CommandObjectSP(new CommandObjectStatsDump(interpreter)));
}
CommandObjectStats::~CommandObjectStats() = default;