Skip to content

Commit d7b3385

Browse files
committed
Modify "statistics dump" to dump JSON.
This patch is a smaller version of a previous patch https://reviews.llvm.org/D110804. This patch modifies the output of "statistics dump" to be able to get stats from the current target. It adds 3 new stats as well. The output of "statistics dump" is now emitted as JSON so that it can be used to track performance and statistics and the output could be used to populate a database that tracks performance. Sample output looks like: (lldb) statistics dump { "expressionEvaluation": { "failures": 0, "successes": 0 }, "firstStopTime": 0.34164492800000001, "frameVariable": { "failures": 0, "successes": 0 }, "launchOrAttachTime": 0.31969605400000001, "targetCreateTime": 0.0040863039999999998 } The top level keys are: "expressionEvaluation" which replaces the previous stats that were emitted as plain text. This dictionary contains the success and fail counts. "frameVariable" which replaces the previous stats for "frame variable" that were emitted as plain text. This dictionary contains the success and fail counts. "targetCreateTime" contains the number of seconds it took to create the target and load dependent libraries (if they were enabled) and also will contain symbol preloading times if that setting is enabled. "launchOrAttachTime" is the time it takes from when the launch/attach is initiated to when the first private stop occurs. "firstStopTime" is the time in seconds that it takes to stop at the first stop that is presented to the user via the LLDB interface. This value will only have meaning if you set a known breakpoint or stop location in your code that you want to measure as a performance test. This diff is also meant as a place to discuess what we want out of the "statistics dump" command before adding more funcionality. It is also meant to clean up the previous code that was storting statistics in a vector of numbers within the lldb_private::Target class. Differential Revision: https://reviews.llvm.org/D111686
1 parent 0472e83 commit d7b3385

File tree

15 files changed

+481
-95
lines changed

15 files changed

+481
-95
lines changed

lldb/include/lldb/Target/Process.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,10 +238,11 @@ class ProcessModID {
238238

239239
~ProcessModID() = default;
240240

241-
void BumpStopID() {
242-
m_stop_id++;
241+
uint32_t BumpStopID() {
242+
const uint32_t prev_stop_id = m_stop_id++;
243243
if (!IsLastResumeForUserExpression())
244244
m_last_natural_stop_id++;
245+
return prev_stop_id;
245246
}
246247

247248
void BumpMemoryID() { m_memory_id++; }

lldb/include/lldb/Target/Statistics.h

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
//===-- Statistics.h --------------------------------------------*- 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+
#ifndef LLDB_TARGET_STATISTICS_H
10+
#define LLDB_TARGET_STATISTICS_H
11+
12+
#include <chrono>
13+
#include <string>
14+
#include <vector>
15+
16+
#include "lldb/Utility/Stream.h"
17+
#include "lldb/lldb-forward.h"
18+
#include "llvm/Support/JSON.h"
19+
20+
namespace lldb_private {
21+
22+
using StatsClock = std::chrono::high_resolution_clock;
23+
using StatsDuration = std::chrono::duration<double>;
24+
using StatsTimepoint = std::chrono::time_point<StatsClock>;
25+
26+
/// A class that measures elapsed time in an exception safe way.
27+
///
28+
/// This is a RAII class is designed to help gather timing statistics within
29+
/// LLDB where objects have optional Duration variables that get updated with
30+
/// elapsed times. This helps LLDB measure statistics for many things that are
31+
/// then reported in LLDB commands.
32+
///
33+
/// Objects that need to measure elapsed times should have a variable of type
34+
/// "StatsDuration m_time_xxx;" which can then be used in the constructor of
35+
/// this class inside a scope that wants to measure something:
36+
///
37+
/// ElapsedTime elapsed(m_time_xxx);
38+
/// // Do some work
39+
///
40+
/// This class will increment the m_time_xxx variable with the elapsed time
41+
/// when the object goes out of scope. The "m_time_xxx" variable will be
42+
/// incremented when the class goes out of scope. This allows a variable to
43+
/// measure something that might happen in stages at different times, like
44+
/// resolving a breakpoint each time a new shared library is loaded.
45+
class ElapsedTime {
46+
public:
47+
/// Set to the start time when the object is created.
48+
StatsTimepoint m_start_time;
49+
/// Elapsed time in seconds to increment when this object goes out of scope.
50+
StatsDuration &m_elapsed_time;
51+
52+
public:
53+
ElapsedTime(StatsDuration &opt_time) : m_elapsed_time(opt_time) {
54+
m_start_time = StatsClock::now();
55+
}
56+
~ElapsedTime() {
57+
StatsDuration elapsed = StatsClock::now() - m_start_time;
58+
m_elapsed_time += elapsed;
59+
}
60+
};
61+
62+
/// A class to count success/fail statistics.
63+
struct StatsSuccessFail {
64+
StatsSuccessFail(llvm::StringRef n) : name(n.str()) {}
65+
66+
void NotifySuccess() { ++successes; }
67+
void NotifyFailure() { ++failures; }
68+
69+
llvm::json::Value ToJSON() const;
70+
std::string name;
71+
uint32_t successes = 0;
72+
uint32_t failures = 0;
73+
};
74+
75+
/// A class that represents statistics for a since lldb_private::Target.
76+
class TargetStats {
77+
public:
78+
llvm::json::Value ToJSON();
79+
80+
void SetLaunchOrAttachTime();
81+
void SetFirstPrivateStopTime();
82+
void SetFirstPublicStopTime();
83+
84+
StatsDuration &GetCreateTime() { return m_create_time; }
85+
StatsSuccessFail &GetExpressionStats() { return m_expr_eval; }
86+
StatsSuccessFail &GetFrameVariableStats() { return m_frame_var; }
87+
88+
protected:
89+
StatsDuration m_create_time{0.0};
90+
llvm::Optional<StatsTimepoint> m_launch_or_attach_time;
91+
llvm::Optional<StatsTimepoint> m_first_private_stop_time;
92+
llvm::Optional<StatsTimepoint> m_first_public_stop_time;
93+
StatsSuccessFail m_expr_eval{"expressionEvaluation"};
94+
StatsSuccessFail m_frame_var{"frameVariable"};
95+
};
96+
97+
class DebuggerStats {
98+
public:
99+
static void SetCollectingStats(bool enable) { g_collecting_stats = enable; }
100+
static bool GetCollectingStats() { return g_collecting_stats; }
101+
102+
/// Get metrics associated with all targets in a debugger in JSON format.
103+
///
104+
/// \return
105+
/// Returns a JSON value that contains all target metrics.
106+
static llvm::json::Value ReportStatistics(Debugger &debugger);
107+
108+
protected:
109+
// Collecting stats can be set to true to collect stats that are expensive
110+
// to collect. By default all stats that are cheap to collect are enabled.
111+
// This settings is here to maintain compatibility with "statistics enable"
112+
// and "statistics disable".
113+
static bool g_collecting_stats;
114+
};
115+
116+
} // namespace lldb_private
117+
118+
#endif // LLDB_TARGET_STATISTICS_H

lldb/include/lldb/Target/Target.h

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "lldb/Target/ExecutionContextScope.h"
2929
#include "lldb/Target/PathMappingList.h"
3030
#include "lldb/Target/SectionLoadHistory.h"
31+
#include "lldb/Target/Statistics.h"
3132
#include "lldb/Target/ThreadSpec.h"
3233
#include "lldb/Utility/ArchSpec.h"
3334
#include "lldb/Utility/Broadcaster.h"
@@ -1451,23 +1452,22 @@ class Target : public std::enable_shared_from_this<Target>,
14511452

14521453
// Utilities for `statistics` command.
14531454
private:
1454-
std::vector<uint32_t> m_stats_storage;
1455-
bool m_collecting_stats = false;
1455+
// Target metrics storage.
1456+
TargetStats m_stats;
14561457

14571458
public:
1458-
void SetCollectingStats(bool v) { m_collecting_stats = v; }
1459-
1460-
bool GetCollectingStats() { return m_collecting_stats; }
1461-
1462-
void IncrementStats(lldb_private::StatisticKind key) {
1463-
if (!GetCollectingStats())
1464-
return;
1465-
lldbassert(key < lldb_private::StatisticKind::StatisticMax &&
1466-
"invalid statistics!");
1467-
m_stats_storage[key] += 1;
1468-
}
1459+
/// Get metrics associated with this target in JSON format.
1460+
///
1461+
/// Target metrics help measure timings and information that is contained in
1462+
/// a target. These are designed to help measure performance of a debug
1463+
/// session as well as represent the current state of the target, like
1464+
/// information on the currently modules, currently set breakpoints and more.
1465+
///
1466+
/// \return
1467+
/// Returns a JSON value that contains all target metrics.
1468+
llvm::json::Value ReportStatistics();
14691469

1470-
std::vector<uint32_t> GetStatistics() { return m_stats_storage; }
1470+
TargetStats &GetStatistics() { return m_stats; }
14711471

14721472
private:
14731473
/// Construct with optional file and arch.

lldb/packages/Python/lldbsuite/test/lldbtest.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -750,10 +750,6 @@ def getBuildArtifact(self, name="a.out"):
750750
"""Return absolute path to an artifact in the test's build directory."""
751751
return os.path.join(self.getBuildDir(), name)
752752

753-
def getSourcePath(self, name):
754-
"""Return absolute path to a file in the test's source directory."""
755-
return os.path.join(self.getSourceDir(), name)
756-
757753
@classmethod
758754
def setUpCommands(cls):
759755
commands = [

lldb/source/API/SBTarget.cpp

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -213,17 +213,9 @@ SBStructuredData SBTarget::GetStatistics() {
213213
TargetSP target_sp(GetSP());
214214
if (!target_sp)
215215
return LLDB_RECORD_RESULT(data);
216-
217-
auto stats_up = std::make_unique<StructuredData::Dictionary>();
218-
int i = 0;
219-
for (auto &Entry : target_sp->GetStatistics()) {
220-
std::string Desc = lldb_private::GetStatDescription(
221-
static_cast<lldb_private::StatisticKind>(i));
222-
stats_up->AddIntegerItem(Desc, Entry);
223-
i += 1;
224-
}
225-
226-
data.m_impl_up->SetObjectSP(std::move(stats_up));
216+
std::string json_str =
217+
llvm::formatv("{0:2}", target_sp->ReportStatistics()).str();
218+
data.m_impl_up->SetObjectSP(StructuredData::ParseJSON(json_str));
227219
return LLDB_RECORD_RESULT(data);
228220
}
229221

@@ -233,7 +225,7 @@ void SBTarget::SetCollectingStats(bool v) {
233225
TargetSP target_sp(GetSP());
234226
if (!target_sp)
235227
return;
236-
return target_sp->SetCollectingStats(v);
228+
return DebuggerStats::SetCollectingStats(v);
237229
}
238230

239231
bool SBTarget::GetCollectingStats() {
@@ -242,7 +234,7 @@ bool SBTarget::GetCollectingStats() {
242234
TargetSP target_sp(GetSP());
243235
if (!target_sp)
244236
return false;
245-
return target_sp->GetCollectingStats();
237+
return DebuggerStats::GetCollectingStats();
246238
}
247239

248240
SBProcess SBTarget::LoadCore(const char *core_file) {

lldb/source/Commands/CommandObjectExpression.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -659,13 +659,8 @@ bool CommandObjectExpression::DoExecute(llvm::StringRef command,
659659
fixed_command.append(m_fixed_expression);
660660
history.AppendString(fixed_command);
661661
}
662-
// Increment statistics to record this expression evaluation success.
663-
target.IncrementStats(StatisticKind::ExpressionSuccessful);
664662
return true;
665663
}
666-
667-
// Increment statistics to record this expression evaluation failure.
668-
target.IncrementStats(StatisticKind::ExpressionFailure);
669664
result.SetStatus(eReturnStatusFailed);
670665
return false;
671666
}

lldb/source/Commands/CommandObjectFrame.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -708,11 +708,11 @@ class CommandObjectFrameVariable : public CommandObjectParsed {
708708

709709
// Increment statistics.
710710
bool res = result.Succeeded();
711-
Target &target = GetSelectedOrDummyTarget();
711+
TargetStats &target_stats = GetSelectedOrDummyTarget().GetStatistics();
712712
if (res)
713-
target.IncrementStats(StatisticKind::FrameVarSuccess);
713+
target_stats.GetFrameVariableStats().NotifySuccess();
714714
else
715-
target.IncrementStats(StatisticKind::FrameVarFailure);
715+
target_stats.GetFrameVariableStats().NotifyFailure();
716716
return res;
717717
}
718718

lldb/source/Commands/CommandObjectStats.cpp

Lines changed: 52 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "CommandObjectStats.h"
10+
#include "lldb/Core/Debugger.h"
11+
#include "lldb/Host/OptionParser.h"
1012
#include "lldb/Interpreter/CommandReturnObject.h"
1113
#include "lldb/Target/Target.h"
1214

@@ -24,14 +26,12 @@ class CommandObjectStatsEnable : public CommandObjectParsed {
2426

2527
protected:
2628
bool DoExecute(Args &command, CommandReturnObject &result) override {
27-
Target &target = GetSelectedOrDummyTarget();
28-
29-
if (target.GetCollectingStats()) {
29+
if (DebuggerStats::GetCollectingStats()) {
3030
result.AppendError("statistics already enabled");
3131
return false;
3232
}
3333

34-
target.SetCollectingStats(true);
34+
DebuggerStats::SetCollectingStats(true);
3535
result.SetStatus(eReturnStatusSuccessFinishResult);
3636
return true;
3737
}
@@ -48,44 +48,75 @@ class CommandObjectStatsDisable : public CommandObjectParsed {
4848

4949
protected:
5050
bool DoExecute(Args &command, CommandReturnObject &result) override {
51-
Target &target = GetSelectedOrDummyTarget();
52-
53-
if (!target.GetCollectingStats()) {
51+
if (!DebuggerStats::GetCollectingStats()) {
5452
result.AppendError("need to enable statistics before disabling them");
5553
return false;
5654
}
5755

58-
target.SetCollectingStats(false);
56+
DebuggerStats::SetCollectingStats(false);
5957
result.SetStatus(eReturnStatusSuccessFinishResult);
6058
return true;
6159
}
6260
};
6361

62+
#define LLDB_OPTIONS_statistics_dump
63+
#include "CommandOptions.inc"
64+
6465
class CommandObjectStatsDump : public CommandObjectParsed {
66+
class CommandOptions : public Options {
67+
public:
68+
CommandOptions() : Options() { OptionParsingStarting(nullptr); }
69+
70+
Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
71+
ExecutionContext *execution_context) override {
72+
Status error;
73+
const int short_option = m_getopt_table[option_idx].val;
74+
75+
switch (short_option) {
76+
case 'a':
77+
m_all_targets = true;
78+
break;
79+
default:
80+
llvm_unreachable("Unimplemented option");
81+
}
82+
return error;
83+
}
84+
85+
void OptionParsingStarting(ExecutionContext *execution_context) override {
86+
m_all_targets = false;
87+
}
88+
89+
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
90+
return llvm::makeArrayRef(g_statistics_dump_options);
91+
}
92+
93+
bool m_all_targets = false;
94+
};
95+
6596
public:
6697
CommandObjectStatsDump(CommandInterpreter &interpreter)
67-
: CommandObjectParsed(interpreter, "dump", "Dump statistics results",
68-
nullptr, eCommandProcessMustBePaused) {}
98+
: CommandObjectParsed(
99+
interpreter, "statistics dump", "Dump metrics in JSON format",
100+
"statistics dump [<options>]", eCommandRequiresTarget) {}
69101

70102
~CommandObjectStatsDump() override = default;
71103

104+
Options *GetOptions() override { return &m_options; }
105+
72106
protected:
73107
bool DoExecute(Args &command, CommandReturnObject &result) override {
74-
Target &target = GetSelectedOrDummyTarget();
75-
76-
uint32_t i = 0;
77-
for (auto &stat : target.GetStatistics()) {
78-
result.AppendMessageWithFormat(
79-
"%s : %u\n",
80-
lldb_private::GetStatDescription(
81-
static_cast<lldb_private::StatisticKind>(i))
82-
.c_str(),
83-
stat);
84-
i += 1;
108+
if (m_options.m_all_targets) {
109+
result.AppendMessageWithFormatv(
110+
"{0:2}", DebuggerStats::ReportStatistics(GetDebugger()));
111+
} else {
112+
Target &target = m_exe_ctx.GetTargetRef();
113+
result.AppendMessageWithFormatv("{0:2}", target.ReportStatistics());
85114
}
86115
result.SetStatus(eReturnStatusSuccessFinishResult);
87116
return true;
88117
}
118+
119+
CommandOptions m_options;
89120
};
90121

91122
CommandObjectStats::CommandObjectStats(CommandInterpreter &interpreter)

lldb/source/Commands/Options.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1305,3 +1305,8 @@ let Command = "trace schema" in {
13051305
def trace_schema_verbose : Option<"verbose", "v">, Group<1>,
13061306
Desc<"Show verbose trace schema logging for debugging the plug-in.">;
13071307
}
1308+
1309+
let Command = "statistics dump" in {
1310+
def statistics_dump_all: Option<"all-targets", "a">, Group<1>,
1311+
Desc<"Include statistics for all targets.">;
1312+
}

lldb/source/Target/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ add_lldb_library(lldbTarget
3939
StackFrameList.cpp
4040
StackFrameRecognizer.cpp
4141
StackID.cpp
42+
Statistics.cpp
4243
StopInfo.cpp
4344
StructuredDataPlugin.cpp
4445
SystemRuntime.cpp

0 commit comments

Comments
 (0)