blob: 3968beca4187f16fc00948ff5f6faaddbaa09b9c [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01002//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14package com.google.devtools.build.lib.runtime;
15
16import com.google.common.base.Joiner;
twerth178615d2020-11-30 07:29:38 -080017import com.google.common.collect.ImmutableMap;
ulfjackdf004162018-11-13 07:13:42 -080018import com.google.common.eventbus.AllowConcurrentEvents;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010019import com.google.common.eventbus.EventBus;
20import com.google.common.eventbus.Subscribe;
janakrc3bcb982020-04-14 06:50:08 -070021import com.google.common.flogger.GoogleLogger;
Tobias Werthc0180512020-09-08 08:03:03 -070022import com.google.devtools.build.lib.actions.ActionCompletionEvent;
tomlu3d1a1942017-11-29 14:01:21 -080023import com.google.devtools.build.lib.actions.ActionKeyContext;
Googlera8023b72018-04-03 08:08:18 -070024import com.google.devtools.build.lib.actions.ActionResultReceivedEvent;
Googler84228ea2024-09-09 06:39:28 -070025import com.google.devtools.build.lib.actions.cache.PostableActionCacheStats;
janakr1fb90fd2017-03-30 21:07:04 +000026import com.google.devtools.build.lib.buildtool.BuildRequest;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010027import com.google.devtools.build.lib.buildtool.buildevent.BuildCompleteEvent;
28import com.google.devtools.build.lib.buildtool.buildevent.ExecutionStartingEvent;
Tobias Werth522099d2019-08-13 07:29:04 -070029import com.google.devtools.build.lib.buildtool.buildevent.ProfilerStartedEvent;
philwo3bcb9f62017-09-06 12:52:21 +020030import com.google.devtools.build.lib.clock.BlazeClock;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010031import com.google.devtools.build.lib.events.Event;
32import com.google.devtools.build.lib.events.Reporter;
janakr1fb90fd2017-03-30 21:07:04 +000033import com.google.devtools.build.lib.exec.ExecutionOptions;
34import com.google.devtools.build.lib.exec.ExecutorBuilder;
Yannic Bonenberger986863a2023-04-06 04:37:45 -070035import com.google.devtools.build.lib.metrics.criticalpath.AggregatedCriticalPath;
36import com.google.devtools.build.lib.metrics.criticalpath.CriticalPathComponent;
37import com.google.devtools.build.lib.metrics.criticalpath.CriticalPathComputer;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010038import com.google.devtools.build.lib.profiler.Profiler;
39import com.google.devtools.build.lib.profiler.ProfilerTask;
ulfjack4cf2ebd2018-06-11 06:00:36 -070040import com.google.devtools.build.lib.profiler.SilentCloseable;
janakrac58ca82019-02-14 09:34:20 -080041import com.google.devtools.build.lib.skyframe.ExecutionFinishedEvent;
Googleraeb53722022-08-26 07:01:58 -070042import com.google.devtools.build.lib.skyframe.TopLevelStatusEvents.TopLevelTargetPendingExecutionEvent;
Tobias Werth522099d2019-08-13 07:29:04 -070043import java.io.IOException;
ulfjack54c8fcb2018-11-19 05:36:37 -080044import java.nio.charset.StandardCharsets;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010045import java.util.ArrayList;
46import java.util.List;
Googleraeb53722022-08-26 07:01:58 -070047import java.util.concurrent.atomic.AtomicBoolean;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010048
49/**
50 * Blaze module for the build summary message that reports various stats to the user.
51 */
52public class BuildSummaryStatsModule extends BlazeModule {
53
janakrc3bcb982020-04-14 06:50:08 -070054 private static final GoogleLogger logger = GoogleLogger.forEnclosingClass();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010055
tomlu3d1a1942017-11-29 14:01:21 -080056 private ActionKeyContext actionKeyContext;
ulfjack3b0d72a2018-09-20 06:17:16 -070057 private CriticalPathComputer criticalPathComputer;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010058 private EventBus eventBus;
59 private Reporter reporter;
janakr1fb90fd2017-03-30 21:07:04 +000060 private boolean enabled;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010061
ulfjack97a3ee42018-11-20 05:40:35 -080062 private boolean statsSummary;
63 private long commandStartMillis;
64 private long executionStartMillis;
65 private long executionEndMillis;
Googlera8023b72018-04-03 08:08:18 -070066 private SpawnStats spawnStats;
Googler09dafdd2024-08-02 05:14:57 -070067 private ProfilerStartedEvent profileEvent;
Googleraeb53722022-08-26 07:01:58 -070068 private AtomicBoolean executionStarted;
Googlera8023b72018-04-03 08:08:18 -070069
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010070 @Override
ulfjacka6a99102017-06-13 17:15:45 +020071 public void beforeCommand(CommandEnvironment env) {
Ulf Adams633f5392015-09-15 11:13:08 +000072 this.reporter = env.getReporter();
73 this.eventBus = env.getEventBus();
tomlu3d1a1942017-11-29 14:01:21 -080074 this.actionKeyContext = env.getSkyframeExecutor().getActionKeyContext();
ulfjack97a3ee42018-11-20 05:40:35 -080075 commandStartMillis = env.getCommandStartTime();
Googlera8023b72018-04-03 08:08:18 -070076 this.spawnStats = new SpawnStats();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010077 eventBus.register(this);
Googleraeb53722022-08-26 07:01:58 -070078 executionStarted = new AtomicBoolean(false);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010079 }
80
Ulf Adams10efbf22016-04-18 11:35:25 +000081 @Override
82 public void afterCommand() {
83 this.criticalPathComputer = null;
84 this.eventBus = null;
85 this.reporter = null;
Googlera8023b72018-04-03 08:08:18 -070086 this.spawnStats = null;
Googleraeb53722022-08-26 07:01:58 -070087 executionStarted.set(false);
Ulf Adams10efbf22016-04-18 11:35:25 +000088 }
89
janakr1fb90fd2017-03-30 21:07:04 +000090 @Override
91 public void executorInit(CommandEnvironment env, BuildRequest request, ExecutorBuilder builder) {
92 enabled = env.getOptions().getOptions(ExecutionOptions.class).enableCriticalPathProfiling;
Tobias Werthb3981192020-06-05 06:13:30 -070093 statsSummary = env.getOptions().getOptions(ExecutionOptions.class).statsSummary;
Googler7e9e5032022-08-26 08:53:41 -070094 if (enabled) {
Googlere92249e2023-06-14 06:11:44 -070095 criticalPathComputer = new CriticalPathComputer(actionKeyContext);
Googler7e9e5032022-08-26 08:53:41 -070096 eventBus.register(criticalPathComputer);
97 }
janakr1fb90fd2017-03-30 21:07:04 +000098 }
99
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100100 @Subscribe
101 public void executionPhaseStarting(ExecutionStartingEvent event) {
Googleraeb53722022-08-26 07:01:58 -0700102 markExecutionPhaseStarted();
103 }
104
105 /**
106 * Skymeld-specific marking of the start of execution. Multiple instances of this event might be
107 * fired during the build, but we make sure to only mark the start of the execution phase when the
108 * first one is received.
109 */
110 @Subscribe
111 public void executionPhaseStarting(
112 @SuppressWarnings("unused") TopLevelTargetPendingExecutionEvent event) {
113 if (executionStarted.compareAndSet(/*expectedValue=*/ false, /*newValue=*/ true)) {
114 markExecutionPhaseStarted();
115 }
116 }
117
118 private void markExecutionPhaseStarted() {
ulfjack97a3ee42018-11-20 05:40:35 -0800119 // TODO(ulfjack): Make sure to use the same clock as for commandStartMillis.
120 executionStartMillis = BlazeClock.instance().currentTimeMillis();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100121 }
122
123 @Subscribe
Tobias Werth522099d2019-08-13 07:29:04 -0700124 public void profileStarting(ProfilerStartedEvent event) {
Googler09dafdd2024-08-02 05:14:57 -0700125 this.profileEvent = event;
Tobias Werth522099d2019-08-13 07:29:04 -0700126 }
127
128 @Subscribe
ulfjack97a3ee42018-11-20 05:40:35 -0800129 public void executionPhaseFinish(@SuppressWarnings("unused") ExecutionFinishedEvent event) {
130 executionEndMillis = BlazeClock.instance().currentTimeMillis();
131 }
132
133 @Subscribe
ulfjackdf004162018-11-13 07:13:42 -0800134 @AllowConcurrentEvents
Googlera8023b72018-04-03 08:08:18 -0700135 public void actionResultReceived(ActionResultReceivedEvent event) {
136 spawnStats.countActionResult(event.getActionResult());
137 }
138
139 @Subscribe
Tobias Werthc0180512020-09-08 08:03:03 -0700140 @AllowConcurrentEvents
141 public void actionCompletion(ActionCompletionEvent event) {
142 spawnStats.incrementActionCount();
143 }
144
145 @Subscribe
Googler84228ea2024-09-09 06:39:28 -0700146 public void actionCacheStats(PostableActionCacheStats event) {
147 spawnStats.recordActionCacheStats(event.asProto());
148 }
149
150 @Subscribe
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100151 public void buildComplete(BuildCompleteEvent event) {
152 try {
153 // We might want to make this conditional on a flag; it can sometimes be a bit of a nuisance.
154 List<String> items = new ArrayList<>();
155 items.add(String.format("Elapsed time: %.3fs", event.getResult().getElapsedSeconds()));
ulfjack54c8fcb2018-11-19 05:36:37 -0800156 event.getResult().getBuildToolLogCollection()
157 .addDirectValue(
158 "elapsed time",
159 String.format(
160 "%f", event.getResult().getElapsedSeconds()).getBytes(StandardCharsets.UTF_8));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100161
ulfjack97a3ee42018-11-20 05:40:35 -0800162 AggregatedCriticalPath criticalPath = AggregatedCriticalPath.EMPTY;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100163 if (criticalPathComputer != null) {
ulfjack4cf2ebd2018-06-11 06:00:36 -0700164 try (SilentCloseable c =
165 Profiler.instance().profile(ProfilerTask.CRITICAL_PATH, "Critical path")) {
ulfjack97a3ee42018-11-20 05:40:35 -0800166 criticalPath = criticalPathComputer.aggregate();
Googlerbed7aba2018-10-18 14:19:11 -0700167 items.add(criticalPath.toStringSummaryNoRemote());
ulfjack54c8fcb2018-11-19 05:36:37 -0800168 event.getResult().getBuildToolLogCollection()
169 .addDirectValue(
170 "critical path", criticalPath.toString().getBytes(StandardCharsets.UTF_8));
Googler1987af22021-12-06 10:27:42 -0800171 logger.atInfo().log("%s", criticalPath);
janakrc3bcb982020-04-14 06:50:08 -0700172 logger.atInfo().log(
173 "Slowest actions:\n %s",
174 Joiner.on("\n ").join(criticalPathComputer.getSlowestComponents()));
ulfjack4cf2ebd2018-06-11 06:00:36 -0700175 // We reverse the critical path because the profiler expect events ordered by the time
176 // when the actions were executed while critical path computation is stored in the reverse
177 // way.
ulfjack8b7aad42018-09-19 04:05:59 -0700178 for (CriticalPathComponent stat : criticalPath.components().reverse()) {
ulfjack4cf2ebd2018-06-11 06:00:36 -0700179 Profiler.instance()
180 .logSimpleTaskDuration(
ulfjack1af71e52018-07-24 00:31:37 -0700181 stat.getStartTimeNanos(),
182 stat.getElapsedTime(),
ulfjack4cf2ebd2018-06-11 06:00:36 -0700183 ProfilerTask.CRITICAL_PATH_COMPONENT,
184 stat.prettyPrintAction());
185 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100186 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100187 }
Googlercda1a282024-08-15 08:30:32 -0700188 if (profileEvent != null && profileEvent.getProfile() != null) {
Tobias Werth522099d2019-08-13 07:29:04 -0700189 // This leads to missing the afterCommand profiles of the other modules in the profile.
190 // Since the BEP currently shuts down at the BuildCompleteEvent, we cannot just move posting
191 // the BuildToolLogs to afterCommand of this module.
192 try {
193 Profiler.instance().stop();
Googlercda1a282024-08-15 08:30:32 -0700194 profileEvent.getProfile().publish(event.getResult().getBuildToolLogCollection());
Tobias Werth522099d2019-08-13 07:29:04 -0700195 } catch (IOException e) {
196 reporter.handle(Event.error("Error while writing profile file: " + e.getMessage()));
197 }
198 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100199
twerth178615d2020-11-30 07:29:38 -0800200 ImmutableMap<String, Integer> spawnSummary = spawnStats.getSummary();
201 String spawnSummaryString = SpawnStats.convertSummaryToString(spawnSummary);
ulfjack97a3ee42018-11-20 05:40:35 -0800202 if (statsSummary) {
twerth178615d2020-11-30 07:29:38 -0800203 reporter.handle(Event.info(spawnSummaryString));
ulfjack97a3ee42018-11-20 05:40:35 -0800204 reporter.handle(
205 Event.info(
206 String.format(
207 "Total action wall time %.2fs", spawnStats.getTotalWallTimeMillis() / 1000.0)));
208 if (criticalPath != AggregatedCriticalPath.EMPTY) {
209 reporter.handle(Event.info(criticalPath.getNewStringSummary()));
210 }
211 long now = event.getResult().getStopTime();
212 long executionTime = executionEndMillis - executionStartMillis;
213 long overheadTime = now - commandStartMillis - executionTime;
214 reporter.handle(
215 Event.info(
216 String.format(
217 "Elapsed time %.2fs (preparation %.2fs, execution %.2fs)",
218 (now - commandStartMillis) / 1000.0,
219 overheadTime / 1000.0,
220 executionTime / 1000.0)));
Googler2d6807c2024-09-04 01:23:31 -0700221 logger.atInfo().log("Stats summary: %s", Joiner.on(", ").join(items));
ulfjack97a3ee42018-11-20 05:40:35 -0800222 } else {
223 reporter.handle(Event.info(Joiner.on(", ").join(items)));
twerth178615d2020-11-30 07:29:38 -0800224 reporter.handle(Event.info(spawnSummaryString));
ulfjack97a3ee42018-11-20 05:40:35 -0800225 }
Googlera8023b72018-04-03 08:08:18 -0700226
twerth178615d2020-11-30 07:29:38 -0800227 event
228 .getResult()
229 .getBuildToolLogCollection()
230 .addDirectValue("process stats", spawnSummaryString.getBytes(StandardCharsets.UTF_8));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100231 } finally {
Tobias Werth9db98722019-08-13 08:55:13 -0700232 if (criticalPathComputer != null) {
233 eventBus.unregister(criticalPathComputer);
234 criticalPathComputer = null;
235 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100236 }
237 }
238}