Skip to content

Commit 610b56f

Browse files
meisterTcopybara-github
authored andcommitted
Add local action cache hits to the chrome trace profile as stacked chart.
In a future change, we can combine other series of the JSON trace profile, but this is already adding coverage for a former blind spot. PiperOrigin-RevId: 497144454 Change-Id: Ia9ed67bde49c02a5427380202ff17c904eaccab0
1 parent d31dd09 commit 610b56f

File tree

6 files changed

+164
-69
lines changed

6 files changed

+164
-69
lines changed

src/main/java/com/google/devtools/build/lib/profiler/CollectLocalResourceUsage.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import com.google.common.base.Preconditions;
1919
import com.google.common.base.Stopwatch;
2020
import com.google.common.collect.ImmutableList;
21+
import com.google.common.collect.ImmutableMap;
2122
import com.google.devtools.build.lib.actions.ResourceEstimator;
2223
import com.google.devtools.build.lib.bugreport.BugReporter;
2324
import com.google.devtools.build.lib.profiler.NetworkMetricsCollector.SystemNetworkUsages;
@@ -265,7 +266,9 @@ synchronized void logCollectedData() {
265266

266267
for (ProfilerTask task : timeSeries.keySet()) {
267268
profiler.logCounters(
268-
task, timeSeries.get(task).toDoubleArray(len), profileStart, BUCKET_DURATION);
269+
ImmutableMap.ofEntries(Map.entry(task, timeSeries.get(task).toDoubleArray(len))),
270+
profileStart,
271+
BUCKET_DURATION);
269272
}
270273
timeSeries = null;
271274
}

src/main/java/com/google/devtools/build/lib/profiler/CounterSeriesTraceData.java

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -20,38 +20,53 @@
2020
import com.google.gson.stream.JsonWriter;
2121
import java.io.IOException;
2222
import java.time.Duration;
23+
import java.util.Map;
2324
import java.util.concurrent.TimeUnit;
2425
import javax.annotation.Nullable;
2526

2627
/**
27-
* Used to inject a whole counter series in one go in the JSON trace profile; these could be used to
28-
* represent CPU or memory usages over the course of an invocations (as opposed to individual tasks
29-
* such as executing an action).
28+
* Used to inject a whole counter series (or even multiple) in one go in the JSON trace profile;
29+
* these could be used to represent CPU or memory usages over the course of an invocation (as
30+
* opposed to individual tasks such as executing an action).
3031
*/
3132
final class CounterSeriesTraceData implements TraceData {
32-
private final String shortName;
33-
private final String readableName;
34-
private final double[] counterValues;
33+
private final Map<ProfilerTask, double[]> counterSeriesMap;
3534
private final Duration profileStart;
3635
private final Duration bucketDuration;
37-
@Nullable private final String colorName;
36+
private final int len;
37+
private String displayName;
38+
@Nullable private String colorName;
3839

40+
/**
41+
* If multiple series are passed: - they will be rendered as stacked chart; - we assume they all
42+
* have the same length; - display name and color are picked from the first profile task in the
43+
* map. However, colors the remaining series are picked arbitrarily by the Trace renderer.
44+
*/
3945
CounterSeriesTraceData(
40-
ProfilerTask profilerTask,
41-
double[] counterValues,
46+
Map<ProfilerTask, double[]> counterSeriesMap,
4247
Duration profileStart,
4348
Duration bucketDuration) {
44-
Preconditions.checkArgument(COUNTER_TASK_TO_SERIES_NAME.containsKey(profilerTask));
45-
this.shortName = profilerTask.description;
46-
this.readableName = COUNTER_TASK_TO_SERIES_NAME.get(profilerTask);
47-
this.counterValues = counterValues;
49+
Integer len = null;
50+
for (ProfilerTask profilerTask : counterSeriesMap.keySet()) {
51+
Preconditions.checkState(COUNTER_TASK_TO_SERIES_NAME.containsKey(profilerTask));
52+
if (len == null) {
53+
len = counterSeriesMap.get(profilerTask).length;
54+
55+
this.displayName = profilerTask.description;
56+
57+
// Pick acceptable counter colors manually, unfortunately we have to pick from these
58+
// weird reserved names from
59+
// https://github.com/catapult-project/catapult/blob/master/tracing/tracing/base/color_scheme.html
60+
this.colorName = COUNTER_TASK_TO_COLOR.get(profilerTask);
61+
} else {
62+
Preconditions.checkState(len.equals(counterSeriesMap.get(profilerTask).length));
63+
}
64+
}
65+
this.len = len;
66+
this.counterSeriesMap = counterSeriesMap;
4867
this.profileStart = profileStart;
4968
this.bucketDuration = bucketDuration;
5069

51-
// Pick acceptable counter colors manually, unfortunately we have to pick from these
52-
// weird reserved names from
53-
// https://github.com/catapult-project/catapult/blob/master/tracing/tracing/base/color_scheme.html
54-
this.colorName = COUNTER_TASK_TO_COLOR.get(profilerTask);
5570
}
5671

5772
// Pick acceptable counter colors manually, unfortunately we have to pick from these
@@ -73,6 +88,7 @@ final class CounterSeriesTraceData implements TraceData {
7388
private static final ImmutableMap<ProfilerTask, String> COUNTER_TASK_TO_SERIES_NAME =
7489
ImmutableMap.ofEntries(
7590
entry(ProfilerTask.ACTION_COUNTS, "action"),
91+
entry(ProfilerTask.ACTION_CACHE_COUNTS, "local action cache"),
7692
entry(ProfilerTask.LOCAL_CPU_USAGE, "cpu"),
7793
entry(ProfilerTask.SYSTEM_CPU_USAGE, "system cpu"),
7894
entry(ProfilerTask.LOCAL_MEMORY_USAGE, "memory"),
@@ -86,18 +102,15 @@ final class CounterSeriesTraceData implements TraceData {
86102

87103
@Override
88104
public void writeTraceData(JsonWriter jsonWriter, long profileStartTimeNanos) throws IOException {
89-
// See https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview#heading=h.msg3086636uq
105+
// See
106+
// https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview#heading=h.msg3086636uq
90107
// for how counter series are represented in the Chrome Trace Event format.
91-
for (int i = 0; i < counterValues.length; i++) {
108+
for (int i = 0; i < len; i++) {
92109
long timeNanos = profileStart.plus(bucketDuration.multipliedBy(i)).toNanos();
93-
// Skip counts equal to zero. They will show up as a thin line in the profile.
94-
if (Math.abs(counterValues[i]) <= 0.00001) {
95-
continue;
96-
}
97110
jsonWriter.setIndent(" ");
98111
jsonWriter.beginObject();
99112
jsonWriter.setIndent("");
100-
jsonWriter.name("name").value(shortName);
113+
jsonWriter.name("name").value(displayName);
101114
jsonWriter.name("pid").value(1);
102115
if (colorName != null) {
103116
jsonWriter.name("cname").value(colorName);
@@ -107,7 +120,13 @@ public void writeTraceData(JsonWriter jsonWriter, long profileStartTimeNanos) th
107120
jsonWriter.name("args");
108121

109122
jsonWriter.beginObject();
110-
jsonWriter.name(readableName).value(counterValues[i]);
123+
for (ProfilerTask profilerTask : counterSeriesMap.keySet()) {
124+
double value = counterSeriesMap.get(profilerTask)[i];
125+
// Skip counts equal to zero. They will show up as a thin line in the profile.
126+
if (Math.abs(value) > 0.00001) {
127+
jsonWriter.name(COUNTER_TASK_TO_SERIES_NAME.get(profilerTask)).value(value);
128+
}
129+
}
111130
jsonWriter.endObject();
112131

113132
jsonWriter.endObject();

src/main/java/com/google/devtools/build/lib/profiler/Profiler.java

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import java.time.Duration;
3838
import java.util.ArrayList;
3939
import java.util.Arrays;
40+
import java.util.LinkedHashMap;
4041
import java.util.List;
4142
import java.util.Map;
4243
import java.util.UUID;
@@ -344,6 +345,7 @@ public double getCounterValue() {
344345
private CollectLocalResourceUsage resourceUsageThread;
345346

346347
private TimeSeries actionCountTimeSeries;
348+
private TimeSeries actionCacheCountTimeSeries;
347349
private Duration actionCountStartTime;
348350
private boolean collectTaskHistograms;
349351
private boolean includePrimaryOutput;
@@ -473,6 +475,8 @@ public synchronized void start(
473475
this.clock = clock;
474476
this.actionCountStartTime = Duration.ofNanos(clock.nanoTime());
475477
this.actionCountTimeSeries = new TimeSeries(actionCountStartTime, ACTION_COUNT_BUCKET_DURATION);
478+
this.actionCacheCountTimeSeries =
479+
new TimeSeries(actionCountStartTime, ACTION_COUNT_BUCKET_DURATION);
476480
this.collectTaskHistograms = collectTaskHistograms;
477481
this.includePrimaryOutput = includePrimaryOutput;
478482
this.includeTargetLabel = includeTargetLabel;
@@ -536,17 +540,21 @@ public synchronized Iterable<SlowTask> getSlowestTasks() {
536540
}
537541

538542
private void collectActionCounts() {
543+
Duration endTime = Duration.ofNanos(clock.nanoTime());
544+
int len = (int) endTime.minus(actionCountStartTime).dividedBy(ACTION_COUNT_BUCKET_DURATION) + 1;
545+
Map<ProfilerTask, double[]> counterSeriesMap = new LinkedHashMap<>();
539546
if (actionCountTimeSeries != null) {
540-
Duration endTime = Duration.ofNanos(clock.nanoTime());
541-
Duration profileStart = actionCountStartTime;
542-
int len = (int) endTime.minus(profileStart).dividedBy(ACTION_COUNT_BUCKET_DURATION) + 1;
543547
double[] actionCountValues = actionCountTimeSeries.toDoubleArray(len);
544-
instance.logCounters(
545-
ProfilerTask.ACTION_COUNTS,
546-
actionCountValues,
547-
profileStart,
548-
ACTION_COUNT_BUCKET_DURATION);
549548
actionCountTimeSeries = null;
549+
counterSeriesMap.put(ProfilerTask.ACTION_COUNTS, actionCountValues);
550+
}
551+
if (actionCacheCountTimeSeries != null) {
552+
double[] actionCacheCountValues = actionCacheCountTimeSeries.toDoubleArray(len);
553+
actionCacheCountTimeSeries = null;
554+
counterSeriesMap.put(ProfilerTask.ACTION_CACHE_COUNTS, actionCacheCountValues);
555+
}
556+
if (!counterSeriesMap.isEmpty()) {
557+
instance.logCounters(counterSeriesMap, actionCountStartTime, ACTION_COUNT_BUCKET_DURATION);
550558
}
551559
}
552560

@@ -609,11 +617,13 @@ private boolean wasTaskSlowEnoughToRecord(ProfilerTask type, long duration) {
609617

610618
/** Adds a whole action count series to the writer bypassing histogram and subtask creation. */
611619
public void logCounters(
612-
ProfilerTask type, double[] counterValues, Duration profileStart, Duration bucketDuration) {
620+
Map<ProfilerTask, double[]> counterSeriesMap,
621+
Duration profileStart,
622+
Duration bucketDuration) {
613623
JsonTraceFileWriter currentWriter = writerRef.get();
614-
if (isActive() && isProfiling(type) && currentWriter != null) {
624+
if (isActive() && currentWriter != null) {
615625
CounterSeriesTraceData counterSeriesTraceData =
616-
new CounterSeriesTraceData(type, counterValues, profileStart, bucketDuration);
626+
new CounterSeriesTraceData(counterSeriesMap, profileStart, bucketDuration);
617627
currentWriter.enqueue(counterSeriesTraceData);
618628
}
619629
}
@@ -832,17 +842,22 @@ private void completeTask(TaskData data) {
832842
data.duration = endTime - data.startTimeNanos;
833843
boolean shouldRecordTask = wasTaskSlowEnoughToRecord(data.type, data.duration);
834844
JsonTraceFileWriter writer = writerRef.get();
835-
if (shouldRecordTask && writer != null) {
836-
writer.enqueue(data);
837-
}
838-
839845
if (shouldRecordTask) {
846+
if (writer != null) {
847+
writer.enqueue(data);
848+
}
840849
if (actionCountTimeSeries != null && countAction(data.type, data)) {
841850
synchronized (this) {
842851
actionCountTimeSeries.addRange(
843852
Duration.ofNanos(data.startTimeNanos), Duration.ofNanos(endTime));
844853
}
845854
}
855+
if (actionCacheCountTimeSeries != null && data.type == ProfilerTask.ACTION_CHECK) {
856+
synchronized (this) {
857+
actionCacheCountTimeSeries.addRange(
858+
Duration.ofNanos(data.startTimeNanos), Duration.ofNanos(endTime));
859+
}
860+
}
846861
SlowestTaskAggregator aggregator = slowestTasks[data.type.ordinal()];
847862
if (aggregator != null) {
848863
aggregator.add(data);

src/main/java/com/google/devtools/build/lib/profiler/ProfilerTask.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ public enum ProfilerTask {
6565
CRITICAL_PATH_COMPONENT("critical path component"),
6666
HANDLE_GC_NOTIFICATION("gc notification"),
6767
ACTION_COUNTS("action count"),
68+
ACTION_CACHE_COUNTS("action cache count"),
6869
LOCAL_CPU_USAGE("CPU usage (Bazel)"),
6970
SYSTEM_CPU_USAGE("CPU usage (total)"),
7071
CPU_USAGE_ESTIMATION("CPU usage estimation"),

src/main/java/com/google/devtools/build/lib/profiler/TraceEvent.java

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,21 +36,35 @@ public abstract class TraceEvent {
3636
public static TraceEvent create(
3737
@Nullable String category,
3838
String name,
39+
@Nullable String type,
3940
@Nullable Duration timestamp,
4041
@Nullable Duration duration,
4142
long threadId,
43+
@Nullable ImmutableMap<String, Object> args,
4244
@Nullable String primaryOutputPath,
4345
@Nullable String targetLabel,
4446
@Nullable String mnemonic) {
4547
return new AutoValue_TraceEvent(
46-
category, name, timestamp, duration, threadId, primaryOutputPath, targetLabel, mnemonic);
48+
category,
49+
name,
50+
type,
51+
timestamp,
52+
duration,
53+
threadId,
54+
args,
55+
primaryOutputPath,
56+
targetLabel,
57+
mnemonic);
4758
}
4859

4960
@Nullable
5061
public abstract String category();
5162

5263
public abstract String name();
5364

65+
@Nullable
66+
public abstract String type();
67+
5468
@Nullable
5569
public abstract Duration timestamp();
5670

@@ -59,6 +73,9 @@ public static TraceEvent create(
5973

6074
public abstract long threadId();
6175

76+
@Nullable
77+
public abstract ImmutableMap<String, Object> args();
78+
6279
// Only applicable to action-related TraceEvents.
6380
@Nullable
6481
public abstract String primaryOutputPath();
@@ -78,6 +95,8 @@ private static TraceEvent createFromJsonReader(JsonReader reader) throws IOExcep
7895
String primaryOutputPath = null;
7996
String targetLabel = null;
8097
String mnemonic = null;
98+
String type = null;
99+
ImmutableMap<String, Object> args = null;
81100

82101
reader.beginObject();
83102
while (reader.hasNext()) {
@@ -88,6 +107,9 @@ private static TraceEvent createFromJsonReader(JsonReader reader) throws IOExcep
88107
case "name":
89108
name = reader.nextString();
90109
break;
110+
case "ph":
111+
type = reader.nextString();
112+
break;
91113
case "ts":
92114
// Duration has no microseconds :-/.
93115
timestamp = Duration.ofNanos(reader.nextLong() * 1000);
@@ -102,7 +124,7 @@ private static TraceEvent createFromJsonReader(JsonReader reader) throws IOExcep
102124
primaryOutputPath = reader.nextString();
103125
break;
104126
case "args":
105-
ImmutableMap<String, Object> args = parseMap(reader);
127+
args = parseMap(reader);
106128
Object target = args.get("target");
107129
targetLabel = target instanceof String ? (String) target : null;
108130
Object mnemonicValue = args.get("mnemonic");
@@ -114,7 +136,16 @@ private static TraceEvent createFromJsonReader(JsonReader reader) throws IOExcep
114136
}
115137
reader.endObject();
116138
return TraceEvent.create(
117-
category, name, timestamp, duration, threadId, primaryOutputPath, targetLabel, mnemonic);
139+
category,
140+
name,
141+
type,
142+
timestamp,
143+
duration,
144+
threadId,
145+
args,
146+
primaryOutputPath,
147+
targetLabel,
148+
mnemonic);
118149
}
119150

120151
private static ImmutableMap<String, Object> parseMap(JsonReader reader) throws IOException {

0 commit comments

Comments
 (0)