Move TaskData to its own class. Since it's used by both the Profiler and the JsonTraceFileWriter. It also makes the Profiler code more manageable. PiperOrigin-RevId: 828071526 Change-Id: I25ef89ba2ee9ad00544952015caf0ec73a24cc87
diff --git a/src/main/java/com/google/devtools/build/lib/profiler/BUILD b/src/main/java/com/google/devtools/build/lib/profiler/BUILD index 782c05f..ae23305 100644 --- a/src/main/java/com/google/devtools/build/lib/profiler/BUILD +++ b/src/main/java/com/google/devtools/build/lib/profiler/BUILD
@@ -54,6 +54,7 @@ "SingleStatRecorder.java", "SlowTask.java", "StatRecorder.java", + "TaskData.java", "ThreadMetadata.java", "TimeSeries.java", "TimeSeriesImpl.java",
diff --git a/src/main/java/com/google/devtools/build/lib/profiler/JsonTraceFileWriter.java b/src/main/java/com/google/devtools/build/lib/profiler/JsonTraceFileWriter.java index 8146b7c..9d3997a 100644 --- a/src/main/java/com/google/devtools/build/lib/profiler/JsonTraceFileWriter.java +++ b/src/main/java/com/google/devtools/build/lib/profiler/JsonTraceFileWriter.java
@@ -17,7 +17,6 @@ import com.google.common.base.Preconditions; import com.google.devtools.build.lib.analysis.BlazeVersionInfo; -import com.google.devtools.build.lib.profiler.Profiler.TaskData; import com.google.gson.stream.JsonWriter; import java.io.BufferedOutputStream; import java.io.IOException;
diff --git a/src/main/java/com/google/devtools/build/lib/profiler/Profiler.java b/src/main/java/com/google/devtools/build/lib/profiler/Profiler.java index 3b87223..f3c33a9 100644 --- a/src/main/java/com/google/devtools/build/lib/profiler/Profiler.java +++ b/src/main/java/com/google/devtools/build/lib/profiler/Profiler.java
@@ -27,12 +27,11 @@ import com.google.common.util.concurrent.ListenableFuture; import com.google.devtools.build.lib.clock.Clock; import com.google.devtools.build.lib.collect.Extrema; -import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadCompatible; import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe; import com.google.devtools.build.lib.profiler.PredicateBasedStatRecorder.RecorderAndPredicate; import com.google.devtools.build.lib.profiler.StatRecorder.VfsHeuristics; +import com.google.devtools.build.lib.profiler.TaskData.ActionTaskData; import com.google.errorprone.annotations.CanIgnoreReturnValue; -import com.google.gson.stream.JsonWriter; import com.sun.management.OperatingSystemMXBean; import java.io.IOException; import java.io.OutputStream; @@ -49,7 +48,6 @@ import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; @@ -94,138 +92,6 @@ JSON_TRACE_FILE_COMPRESSED_FORMAT } - /** - * Container for the single task record. - * - * <p>Class itself is not thread safe, but all access to it from Profiler methods is. - */ - @ThreadCompatible - static class TaskData implements TraceData { - final long threadId; - final long startTimeNanos; - final ProfilerTask type; - final String description; - - long durationNanos; - - TaskData( - long threadId, - long startTimeNanos, - long durationNanos, - ProfilerTask eventType, - String description) { - this.threadId = threadId; - this.startTimeNanos = startTimeNanos; - this.durationNanos = durationNanos; - this.type = eventType; - this.description = checkNotNull(description); - } - - TaskData(long threadId, long startTimeNanos, ProfilerTask eventType, String description) { - this(threadId, startTimeNanos, /* durationNanos= */ -1, eventType, description); - } - - TaskData(long threadId, long startTimeNanos, long durationNanos, String description) { - this.type = ProfilerTask.UNKNOWN; - this.threadId = threadId; - this.startTimeNanos = startTimeNanos; - this.durationNanos = durationNanos; - this.description = description; - } - - @Override - public String toString() { - return "Thread " + threadId + ", type " + type + ", " + description; - } - - @Override - public void writeTraceData(JsonWriter jsonWriter, long profileStartTimeNanos) - throws IOException { - String eventType = durationNanos == 0 ? "i" : "X"; - jsonWriter.setIndent(" "); - jsonWriter.beginObject(); - jsonWriter.setIndent(""); - if (type == null) { - jsonWriter.setIndent(" "); - } else { - jsonWriter.name("cat").value(type.description); - } - jsonWriter.name("name").value(description); - jsonWriter.name("ph").value(eventType); - jsonWriter - .name("ts") - .value(TimeUnit.NANOSECONDS.toMicros(startTimeNanos - profileStartTimeNanos)); - if (durationNanos != 0) { - jsonWriter.name("dur").value(TimeUnit.NANOSECONDS.toMicros(durationNanos)); - } - jsonWriter.name("pid").value(1); - - if (this instanceof ActionTaskData actionTaskData) { - if (actionTaskData.primaryOutputPath != null) { - // Primary outputs are non-mergeable, thus incompatible with slim profiles. - jsonWriter.name("out").value(actionTaskData.primaryOutputPath); - } - if (actionTaskData.targetLabel != null - || actionTaskData.mnemonic != null - || actionTaskData.configuration != null) { - jsonWriter.name("args"); - jsonWriter.beginObject(); - if (actionTaskData.targetLabel != null) { - jsonWriter.name("target").value(actionTaskData.targetLabel); - } - if (actionTaskData.mnemonic != null) { - jsonWriter.name("mnemonic").value(actionTaskData.mnemonic); - } - if (actionTaskData.configuration != null) { - jsonWriter.name("configuration").value(actionTaskData.configuration); - } - jsonWriter.endObject(); - } - } - if (type == ProfilerTask.CRITICAL_PATH_COMPONENT) { - jsonWriter.name("args"); - jsonWriter.beginObject(); - jsonWriter.name("tid").value(threadId); - jsonWriter.endObject(); - } - jsonWriter - .name("tid") - .value( - type == ProfilerTask.CRITICAL_PATH_COMPONENT - ? ThreadMetadata.CRITICAL_PATH_THREAD_ID - : threadId); - jsonWriter.endObject(); - } - } - - /** - * Similar to TaskData, specific for profiled actions. Depending on options, adds additional - * action specific information such as primary output path and target label. This is only meant to - * be used for ProfilerTask.ACTION. - */ - static final class ActionTaskData extends TaskData { - @Nullable final String primaryOutputPath; - @Nullable final String targetLabel; - @Nullable final String mnemonic; - @Nullable final String configuration; - - ActionTaskData( - long threadId, - long startTimeNanos, - long durationNanos, - ProfilerTask eventType, - @Nullable String mnemonic, - String description, - @Nullable String primaryOutputPath, - @Nullable String targetLabel, - @Nullable String configuration) { - super(threadId, startTimeNanos, durationNanos, eventType, description); - this.primaryOutputPath = primaryOutputPath; - this.targetLabel = targetLabel; - this.mnemonic = mnemonic; - this.configuration = configuration; - } - } /** * Aggregator class that keeps track of the slowest tasks of the specified type.
diff --git a/src/main/java/com/google/devtools/build/lib/profiler/TaskData.java b/src/main/java/com/google/devtools/build/lib/profiler/TaskData.java new file mode 100644 index 0000000..4f65fb3 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/profiler/TaskData.java
@@ -0,0 +1,154 @@ +// Copyright 2025 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.lib.profiler; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadCompatible; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import javax.annotation.Nullable; + +/** + * Container for the single task record. + * + * <p>Class itself is not thread safe, but all access to it from Profiler methods is. + */ +@ThreadCompatible +class TaskData implements TraceData { + final long threadId; + final long startTimeNanos; + final ProfilerTask type; + final String description; + + long durationNanos; + + TaskData( + long threadId, + long startTimeNanos, + long durationNanos, + ProfilerTask eventType, + String description) { + this.threadId = threadId; + this.startTimeNanos = startTimeNanos; + this.durationNanos = durationNanos; + this.type = eventType; + this.description = checkNotNull(description); + } + + TaskData(long threadId, long startTimeNanos, ProfilerTask eventType, String description) { + this(threadId, startTimeNanos, /* durationNanos= */ -1, eventType, description); + } + + TaskData(long threadId, long startTimeNanos, long durationNanos, String description) { + this.type = ProfilerTask.UNKNOWN; + this.threadId = threadId; + this.startTimeNanos = startTimeNanos; + this.durationNanos = durationNanos; + this.description = description; + } + + @Override + public String toString() { + return "Thread " + threadId + ", type " + type + ", " + description; + } + + @Override + public void writeTraceData(JsonWriter jsonWriter, long profileStartTimeNanos) throws IOException { + String eventType = durationNanos == 0 ? "i" : "X"; + jsonWriter.setIndent(" "); + jsonWriter.beginObject(); + jsonWriter.setIndent(""); + if (type == null) { + jsonWriter.setIndent(" "); + } else { + jsonWriter.name("cat").value(type.description); + } + jsonWriter.name("name").value(description); + jsonWriter.name("ph").value(eventType); + jsonWriter + .name("ts") + .value(TimeUnit.NANOSECONDS.toMicros(startTimeNanos - profileStartTimeNanos)); + if (durationNanos != 0) { + jsonWriter.name("dur").value(TimeUnit.NANOSECONDS.toMicros(durationNanos)); + } + jsonWriter.name("pid").value(1); + + if (this instanceof ActionTaskData actionTaskData) { + if (actionTaskData.primaryOutputPath != null) { + // Primary outputs are non-mergeable, thus incompatible with slim profiles. + jsonWriter.name("out").value(actionTaskData.primaryOutputPath); + } + if (actionTaskData.targetLabel != null + || actionTaskData.mnemonic != null + || actionTaskData.configuration != null) { + jsonWriter.name("args"); + jsonWriter.beginObject(); + if (actionTaskData.targetLabel != null) { + jsonWriter.name("target").value(actionTaskData.targetLabel); + } + if (actionTaskData.mnemonic != null) { + jsonWriter.name("mnemonic").value(actionTaskData.mnemonic); + } + if (actionTaskData.configuration != null) { + jsonWriter.name("configuration").value(actionTaskData.configuration); + } + jsonWriter.endObject(); + } + } + if (type == ProfilerTask.CRITICAL_PATH_COMPONENT) { + jsonWriter.name("args"); + jsonWriter.beginObject(); + jsonWriter.name("tid").value(threadId); + jsonWriter.endObject(); + } + jsonWriter + .name("tid") + .value( + type == ProfilerTask.CRITICAL_PATH_COMPONENT + ? ThreadMetadata.CRITICAL_PATH_THREAD_ID + : threadId); + jsonWriter.endObject(); + } + + /** + * Similar to TaskData, specific for profiled actions. Depending on options, adds additional + * action specific information such as primary output path and target label. This is only meant to + * be used for ProfilerTask.ACTION. + */ + static final class ActionTaskData extends TaskData { + @Nullable final String primaryOutputPath; + @Nullable final String targetLabel; + @Nullable final String mnemonic; + @Nullable final String configuration; + + ActionTaskData( + long threadId, + long startTimeNanos, + long durationNanos, + ProfilerTask eventType, + @Nullable String mnemonic, + String description, + @Nullable String primaryOutputPath, + @Nullable String targetLabel, + @Nullable String configuration) { + super(threadId, startTimeNanos, durationNanos, eventType, description); + this.primaryOutputPath = primaryOutputPath; + this.targetLabel = targetLabel; + this.mnemonic = mnemonic; + this.configuration = configuration; + } + } +}