Introduce `InstrumentationOutput` and apply it to profile
`InstrumentationOutput` aims at providing a unified way to handle writing and publishing instrumentation outputs.
PiperOrigin-RevId: 663316138
Change-Id: Ie5d80acd1843a94045307442ed00ffd6a9f98105
diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/buildevent/ProfilerStartedEvent.java b/src/main/java/com/google/devtools/build/lib/buildtool/buildevent/ProfilerStartedEvent.java
index fcb2f94..6c89b12 100644
--- a/src/main/java/com/google/devtools/build/lib/buildtool/buildevent/ProfilerStartedEvent.java
+++ b/src/main/java/com/google/devtools/build/lib/buildtool/buildevent/ProfilerStartedEvent.java
@@ -13,46 +13,20 @@
// limitations under the License.
package com.google.devtools.build.lib.buildtool.buildevent;
-import com.google.devtools.build.lib.buildeventstream.BuildEventArtifactUploader.UploadContext;
import com.google.devtools.build.lib.events.ExtendedEventHandler;
-import com.google.devtools.build.lib.profiler.Profiler;
-import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.runtime.InstrumentationOutput;
import javax.annotation.Nullable;
/** This event is fired when the profiler is started. */
public class ProfilerStartedEvent implements ExtendedEventHandler.Postable {
- @Nullable private final Path profilePath;
- @Nullable private final UploadContext streamingContext;
- private final Profiler.Format format;
+ @Nullable private final InstrumentationOutput profile;
- public ProfilerStartedEvent(
- @Nullable Path profilePath,
- @Nullable UploadContext streamingContext,
- Profiler.Format format) {
- this.profilePath = profilePath;
- this.streamingContext = streamingContext;
- this.format = format;
+ public ProfilerStartedEvent(@Nullable InstrumentationOutput profile) {
+ this.profile = profile;
}
@Nullable
- public Path getProfilePath() {
- return profilePath;
- }
-
- @Nullable
- public UploadContext getStreamingContext() {
- return streamingContext;
- }
-
- public String getName() {
- switch (format) {
- case JSON_TRACE_FILE_FORMAT -> {
- return "command.profile.json";
- }
- case JSON_TRACE_FILE_COMPRESSED_FORMAT -> {
- return "command.profile.gz";
- }
- }
- throw new UnsupportedOperationException();
+ public InstrumentationOutput getProfile() {
+ return profile;
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java b/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java
index ec89a88..98af82e 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java
@@ -43,9 +43,7 @@
import com.google.devtools.build.lib.bugreport.BugReporter;
import com.google.devtools.build.lib.bugreport.Crash;
import com.google.devtools.build.lib.bugreport.CrashContext;
-import com.google.devtools.build.lib.buildeventstream.BuildEvent.LocalFile.LocalFileType;
import com.google.devtools.build.lib.buildeventstream.BuildEventArtifactUploader;
-import com.google.devtools.build.lib.buildeventstream.BuildEventArtifactUploader.UploadContext;
import com.google.devtools.build.lib.buildeventstream.BuildEventProtocolOptions;
import com.google.devtools.build.lib.buildtool.CommandPrecompleteEvent;
import com.google.devtools.build.lib.buildtool.buildevent.ProfilerStartedEvent;
@@ -324,27 +322,38 @@
ImmutableSet.Builder<ProfilerTask> profiledTasksBuilder = ImmutableSet.builder();
Profiler.Format format = Format.JSON_TRACE_FILE_FORMAT;
Path profilePath = null;
- UploadContext streamingContext = null;
+ InstrumentationOutput profile = null;
try {
if (tracerEnabled) {
if (options.profilePath == null) {
+ String profileName = "command.profile.gz";
format = Format.JSON_TRACE_FILE_COMPRESSED_FORMAT;
if (bepOptions != null && bepOptions.streamingLogFileUploads) {
BuildEventArtifactUploader buildEventArtifactUploader =
newUploader(env, bepOptions.buildEventUploadStrategy);
- streamingContext = buildEventArtifactUploader.startUpload(LocalFileType.LOG, null);
- out = streamingContext.getOutputStream();
+ profile =
+ new BuildEventArtifactInstrumentationOutput(
+ profileName, buildEventArtifactUploader);
+ out = profile.createOutputStream();
} else {
- profilePath = workspace.getOutputBase().getRelative("command.profile.gz");
- out = profilePath.getOutputStream();
+ profilePath = workspace.getOutputBase().getRelative(profileName);
+ profile = new LocalInstrumentationOutput(profileName, profilePath);
+ out = profile.createOutputStream();
}
} else {
format =
options.profilePath.toString().endsWith(".gz")
? Format.JSON_TRACE_FILE_COMPRESSED_FORMAT
: Format.JSON_TRACE_FILE_FORMAT;
+ String profileName =
+ (format == Format.JSON_TRACE_FILE_COMPRESSED_FORMAT)
+ ? "command.profile.gz"
+ : "command.profile.json";
profilePath = workspace.getWorkspace().getRelative(options.profilePath);
- out = profilePath.getOutputStream(/* append= */ false, /* internal= */ true);
+ profile = new LocalInstrumentationOutput(profileName, profilePath);
+ out =
+ ((LocalInstrumentationOutput) profile)
+ .createOutputStream(/* append= */ false, /* internal= */ true);
}
for (ProfilerTask profilerTask : ProfilerTask.values()) {
if (!profilerTask.isVfs()
@@ -445,7 +454,7 @@
} catch (IOException e) {
eventHandler.handle(Event.error("Error while creating profile file: " + e.getMessage()));
}
- return new ProfilerStartedEvent(profilePath, streamingContext, format);
+ return new ProfilerStartedEvent(profile);
}
public FileSystem getFileSystem() {
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BuildEventArtifactInstrumentationOutput.java b/src/main/java/com/google/devtools/build/lib/runtime/BuildEventArtifactInstrumentationOutput.java
new file mode 100644
index 0000000..dad4993
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/runtime/BuildEventArtifactInstrumentationOutput.java
@@ -0,0 +1,51 @@
+// Copyright 2024 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.runtime;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.devtools.build.lib.buildeventstream.BuildEvent.LocalFile.LocalFileType;
+import com.google.devtools.build.lib.buildeventstream.BuildEventArtifactUploader;
+import com.google.devtools.build.lib.buildeventstream.BuildEventArtifactUploader.UploadContext;
+import com.google.devtools.build.lib.buildtool.BuildResult.BuildToolLogCollection;
+import java.io.OutputStream;
+import javax.annotation.Nullable;
+
+/**
+ * Used when instrumentation output should be treated as a build event artifact so that it will be
+ * uploaded to a specified location.
+ */
+final class BuildEventArtifactInstrumentationOutput implements InstrumentationOutput {
+ private final String name;
+ private final BuildEventArtifactUploader buildEventArtifactUploader;
+ @Nullable private UploadContext uploadContext;
+
+ public BuildEventArtifactInstrumentationOutput(
+ String name, BuildEventArtifactUploader buildEventArtifactUploader) {
+ this.name = checkNotNull(name);
+ this.buildEventArtifactUploader = checkNotNull(buildEventArtifactUploader);
+ }
+
+ @Override
+ public void publish(BuildToolLogCollection buildToolLogCollection) {
+ checkNotNull(uploadContext, "Cannot publish to buildToolLogCollection if upload never starts.");
+ buildToolLogCollection.addUriFuture(name, uploadContext.uriFuture());
+ }
+
+ @Override
+ public OutputStream createOutputStream() {
+ uploadContext = buildEventArtifactUploader.startUpload(LocalFileType.LOG, null);
+ return uploadContext.getOutputStream();
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BuildSummaryStatsModule.java b/src/main/java/com/google/devtools/build/lib/runtime/BuildSummaryStatsModule.java
index 70fe506..8a865f7 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/BuildSummaryStatsModule.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/BuildSummaryStatsModule.java
@@ -179,16 +179,13 @@
}
}
}
- if (profileEvent != null && profileEvent.getProfilePath() != null) {
+ if (profileEvent != null && profileEvent.getProfile() != null) {
// This leads to missing the afterCommand profiles of the other modules in the profile.
// Since the BEP currently shuts down at the BuildCompleteEvent, we cannot just move posting
// the BuildToolLogs to afterCommand of this module.
try {
Profiler.instance().stop();
- event
- .getResult()
- .getBuildToolLogCollection()
- .addLocalFile(profileEvent.getName(), profileEvent.getProfilePath());
+ profileEvent.getProfile().publish(event.getResult().getBuildToolLogCollection());
} catch (IOException e) {
reporter.handle(Event.error("Error while writing profile file: " + e.getMessage()));
}
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/InstrumentationOutput.java b/src/main/java/com/google/devtools/build/lib/runtime/InstrumentationOutput.java
new file mode 100644
index 0000000..f5130e6
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/runtime/InstrumentationOutput.java
@@ -0,0 +1,28 @@
+// Copyright 2024 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.runtime;
+
+import com.google.devtools.build.lib.buildtool.BuildResult.BuildToolLogCollection;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/** Stores and publishes the instrumentation output information. */
+public interface InstrumentationOutput {
+
+ /** Creates the {@link OutputStream} for instrumentation output writes. */
+ OutputStream createOutputStream() throws IOException;
+
+ /** Publishes instrumentation output information to the {@link BuildToolLogCollection}. */
+ void publish(BuildToolLogCollection buildToolLogCollection);
+}
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/LocalInstrumentationOutput.java b/src/main/java/com/google/devtools/build/lib/runtime/LocalInstrumentationOutput.java
new file mode 100644
index 0000000..aedb96a
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/runtime/LocalInstrumentationOutput.java
@@ -0,0 +1,44 @@
+// Copyright 2024 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.runtime;
+
+import com.google.devtools.build.lib.buildtool.BuildResult.BuildToolLogCollection;
+import com.google.devtools.build.lib.vfs.Path;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/** Used when instrumentation output is written to a local file. */
+final class LocalInstrumentationOutput implements InstrumentationOutput {
+ private final Path path;
+ private final String name;
+
+ LocalInstrumentationOutput(String name, Path path) {
+ this.name = name;
+ this.path = path;
+ }
+
+ @Override
+ public void publish(BuildToolLogCollection buildToolLogCollection) {
+ buildToolLogCollection.addLocalFile(name, path);
+ }
+
+ @Override
+ public OutputStream createOutputStream() throws IOException {
+ return path.getOutputStream();
+ }
+
+ public OutputStream createOutputStream(boolean append, boolean internal) throws IOException {
+ return path.getOutputStream(append, internal);
+ }
+}