Skip to content

Commit 4f60160

Browse files
committed
Extract showing Debugger stats in the browser to a separate class
1 parent 73edc1d commit 4f60160

File tree

2 files changed

+121
-82
lines changed

2 files changed

+121
-82
lines changed

fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/debug/EventState.java

Lines changed: 0 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -252,88 +252,6 @@ StatsMaps getStatsMaps() {
252252
return new StatsMaps(eventClassStatsMap, plannerRuleClassStatsMap);
253253
}
254254

255-
public String showStats() {
256-
StringBuilder tableBuilder = new StringBuilder();
257-
tableBuilder.append("<table class=\"table\">");
258-
tableHeader(tableBuilder, "Event");
259-
final ImmutableMap<String, MutableStats> eventStatsMap =
260-
eventClassStatsMap.entrySet()
261-
.stream()
262-
.map(entry -> Pair.of(entry.getKey().getSimpleName(), entry.getValue()))
263-
.sorted(Map.Entry.comparingByKey())
264-
.collect(ImmutableMap.toImmutableMap(Pair::getKey, Pair::getValue));
265-
tableBody(tableBuilder, eventStatsMap);
266-
tableBuilder.append("</table>");
267-
268-
final String eventProfilingString = tableBuilder.toString();
269-
270-
tableBuilder = new StringBuilder();
271-
tableBuilder.append("<table class=\"table\">");
272-
tableHeader(tableBuilder, "Planner Rule");
273-
final ImmutableMap<String, MutableStats> plannerRuleStatsMap =
274-
plannerRuleClassStatsMap.entrySet()
275-
.stream()
276-
.map(entry -> Pair.of(entry.getKey().getSimpleName(), entry.getValue()))
277-
.sorted(Map.Entry.comparingByKey())
278-
.collect(ImmutableMap.toImmutableMap(Pair::getKey, Pair::getValue));
279-
tableBody(tableBuilder, plannerRuleStatsMap);
280-
tableBuilder.append("</table>");
281-
282-
final String plannerRuleProfilingString = tableBuilder.toString();
283-
284-
return BrowserHelper.browse("/showProfilingReport.html",
285-
ImmutableMap.of("$EVENT_PROFILING", eventProfilingString,
286-
"$PLANNER_RULE_PROFILING", plannerRuleProfilingString));
287-
}
288-
289-
private void tableHeader(@Nonnull final StringBuilder stringBuilder, @Nonnull final String category) {
290-
stringBuilder.append("<thead>");
291-
stringBuilder.append("<tr>");
292-
stringBuilder.append("<th scope=\"col\">").append(category).append("</th>");
293-
stringBuilder.append("<th scope=\"col\">Location</th>");
294-
stringBuilder.append("<th scope=\"col\">Count</th>");
295-
stringBuilder.append("<th scope=\"col\">Total Time (micros)</th>");
296-
stringBuilder.append("<th scope=\"col\">Average Time (micros)</th>");
297-
stringBuilder.append("<th scope=\"col\">Total Own Time (micros)</th>");
298-
stringBuilder.append("<th scope=\"col\">Average Own Time (micros)</th>");
299-
stringBuilder.append("</tr>");
300-
stringBuilder.append("</thead>");
301-
}
302-
303-
private void tableBody(@Nonnull final StringBuilder stringBuilder, @Nonnull final Map<String, MutableStats> statsMap) {
304-
stringBuilder.append("<tbody class=\"table-group-divider\">");
305-
for (final Map.Entry<String, MutableStats> entry : statsMap.entrySet()) {
306-
final MutableStats mutableStats = entry.getValue();
307-
for (final var locationEntry : mutableStats.getLocationCountMap().entrySet()) {
308-
stringBuilder.append("<tr>");
309-
stringBuilder.append("<td>").append(entry.getKey()).append("</td>");
310-
if (locationEntry.getKey() == Debugger.Location.BEGIN) {
311-
stringBuilder.append("<td></td>");
312-
} else {
313-
stringBuilder.append("<td>").append(locationEntry.getKey().name()).append("</td>");
314-
}
315-
stringBuilder.append("<td class=\"text-end\">").append(locationEntry.getValue()).append("</td>");
316-
if (locationEntry.getKey() == Debugger.Location.BEGIN) {
317-
stringBuilder.append("<td class=\"text-end\">").append(formatNsInMicros(mutableStats.getTotalTimeInNs())).append("</td>");
318-
stringBuilder.append("<td class=\"text-end\">").append(formatNsInMicros(mutableStats.getTotalTimeInNs() / mutableStats.getCount(Debugger.Location.BEGIN))).append("</td>");
319-
stringBuilder.append("<td class=\"text-end\">").append(formatNsInMicros(mutableStats.getOwnTimeInNs())).append("</td>");
320-
stringBuilder.append("<td class=\"text-end\">").append(formatNsInMicros(mutableStats.getOwnTimeInNs() / mutableStats.getCount(Debugger.Location.BEGIN))).append("</td>");
321-
} else {
322-
stringBuilder.append("<td></td>");
323-
stringBuilder.append("<td></td>");
324-
}
325-
stringBuilder.append("</tr>");
326-
}
327-
}
328-
stringBuilder.append("</tbody>");
329-
}
330-
331-
@Nonnull
332-
private String formatNsInMicros(final long ns) {
333-
final long micros = TimeUnit.NANOSECONDS.toMicros(ns);
334-
return String.format(Locale.ROOT, "%,d", micros);
335-
}
336-
337255
private static class MutableStats extends Stats {
338256
public MutableStats() {
339257
super(Maps.newLinkedHashMap(), 0L, 0L);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/*
2+
* StatsViewer.java
3+
*
4+
* This source file is part of the FoundationDB open source project
5+
*
6+
* Copyright 2015-2025 Apple Inc. and the FoundationDB project authors
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*/
20+
21+
package com.apple.foundationdb.record.query.plan.cascades.debug;
22+
23+
import com.apple.foundationdb.record.util.pair.Pair;
24+
import com.google.common.collect.ImmutableMap;
25+
26+
import javax.annotation.Nonnull;
27+
import java.util.Locale;
28+
import java.util.Map;
29+
import java.util.concurrent.TimeUnit;
30+
31+
/** Only used by debuggers to display stats.*/
32+
@SuppressWarnings("unused")
33+
public class StatsViewer {
34+
public static String showStats(@Nonnull final StatsDebugger statsDebugger) {
35+
final var statsMaps = statsDebugger.getStatsMaps();
36+
37+
if (statsMaps.isEmpty()) {
38+
return "no stats available";
39+
}
40+
41+
StringBuilder tableBuilder = new StringBuilder();
42+
tableBuilder.append("<table class=\"table\">");
43+
tableHeader(tableBuilder, "Event");
44+
final ImmutableMap<String, Stats> eventStatsMap =
45+
statsMaps.get().getEventClassStatsMap().entrySet()
46+
.stream()
47+
.map(entry -> Pair.of(entry.getKey().getSimpleName(), entry.getValue()))
48+
.sorted(Map.Entry.comparingByKey())
49+
.collect(ImmutableMap.toImmutableMap(Pair::getKey, Pair::getValue));
50+
tableBody(tableBuilder, eventStatsMap);
51+
tableBuilder.append("</table>");
52+
53+
final String eventProfilingString = tableBuilder.toString();
54+
55+
tableBuilder = new StringBuilder();
56+
tableBuilder.append("<table class=\"table\">");
57+
tableHeader(tableBuilder, "Planner Rule");
58+
final ImmutableMap<String, Stats> plannerRuleStatsMap =
59+
statsMaps.get().getPlannerRuleClassStatsMap().entrySet()
60+
.stream()
61+
.map(entry -> Pair.of(entry.getKey().getSimpleName(), entry.getValue()))
62+
.sorted(Map.Entry.comparingByKey())
63+
.collect(ImmutableMap.toImmutableMap(Pair::getKey, Pair::getValue));
64+
tableBody(tableBuilder, plannerRuleStatsMap);
65+
tableBuilder.append("</table>");
66+
67+
final String plannerRuleProfilingString = tableBuilder.toString();
68+
69+
return BrowserHelper.browse("/showProfilingReport.html",
70+
ImmutableMap.of("$EVENT_PROFILING", eventProfilingString,
71+
"$PLANNER_RULE_PROFILING", plannerRuleProfilingString));
72+
}
73+
74+
private static void tableHeader(@Nonnull final StringBuilder stringBuilder, @Nonnull final String category) {
75+
stringBuilder.append("<thead>");
76+
stringBuilder.append("<tr>");
77+
stringBuilder.append("<th scope=\"col\">").append(category).append("</th>");
78+
stringBuilder.append("<th scope=\"col\">Location</th>");
79+
stringBuilder.append("<th scope=\"col\">Count</th>");
80+
stringBuilder.append("<th scope=\"col\">Total Time (micros)</th>");
81+
stringBuilder.append("<th scope=\"col\">Average Time (micros)</th>");
82+
stringBuilder.append("<th scope=\"col\">Total Own Time (micros)</th>");
83+
stringBuilder.append("<th scope=\"col\">Average Own Time (micros)</th>");
84+
stringBuilder.append("</tr>");
85+
stringBuilder.append("</thead>");
86+
}
87+
88+
private static void tableBody(@Nonnull final StringBuilder stringBuilder, @Nonnull final Map<String, Stats> statsMap) {
89+
stringBuilder.append("<tbody class=\"table-group-divider\">");
90+
for (final Map.Entry<String, Stats> entry : statsMap.entrySet()) {
91+
final Stats stats = entry.getValue();
92+
for (final var locationEntry : stats.getLocationCountMap().entrySet()) {
93+
stringBuilder.append("<tr>");
94+
stringBuilder.append("<td>").append(entry.getKey()).append("</td>");
95+
if (locationEntry.getKey() == Debugger.Location.BEGIN) {
96+
stringBuilder.append("<td></td>");
97+
} else {
98+
stringBuilder.append("<td>").append(locationEntry.getKey().name()).append("</td>");
99+
}
100+
stringBuilder.append("<td class=\"text-end\">").append(locationEntry.getValue()).append("</td>");
101+
if (locationEntry.getKey() == Debugger.Location.BEGIN) {
102+
stringBuilder.append("<td class=\"text-end\">").append(formatNsInMicros(stats.getTotalTimeInNs())).append("</td>");
103+
stringBuilder.append("<td class=\"text-end\">").append(formatNsInMicros(stats.getTotalTimeInNs() / stats.getCount(Debugger.Location.BEGIN))).append("</td>");
104+
stringBuilder.append("<td class=\"text-end\">").append(formatNsInMicros(stats.getOwnTimeInNs())).append("</td>");
105+
stringBuilder.append("<td class=\"text-end\">").append(formatNsInMicros(stats.getOwnTimeInNs() / stats.getCount(Debugger.Location.BEGIN))).append("</td>");
106+
} else {
107+
stringBuilder.append("<td></td>");
108+
stringBuilder.append("<td></td>");
109+
}
110+
stringBuilder.append("</tr>");
111+
}
112+
}
113+
stringBuilder.append("</tbody>");
114+
}
115+
116+
@Nonnull
117+
private static String formatNsInMicros(final long ns) {
118+
final long micros = TimeUnit.NANOSECONDS.toMicros(ns);
119+
return String.format(Locale.ROOT, "%,d", micros);
120+
}
121+
}

0 commit comments

Comments
 (0)