blob: b7eaaf117ef13202d9fd9552e3616e055e14994f [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;
ulfjackdf004162018-11-13 07:13:42 -080017import com.google.common.eventbus.AllowConcurrentEvents;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010018import com.google.common.eventbus.EventBus;
19import com.google.common.eventbus.Subscribe;
janakrc3bcb982020-04-14 06:50:08 -070020import com.google.common.flogger.GoogleLogger;
Tobias Werthc0180512020-09-08 08:03:03 -070021import com.google.devtools.build.lib.actions.ActionCompletionEvent;
tomlu3d1a1942017-11-29 14:01:21 -080022import com.google.devtools.build.lib.actions.ActionKeyContext;
Googlera8023b72018-04-03 08:08:18 -070023import com.google.devtools.build.lib.actions.ActionResultReceivedEvent;
janakr1fb90fd2017-03-30 21:07:04 +000024import com.google.devtools.build.lib.buildtool.BuildRequest;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010025import com.google.devtools.build.lib.buildtool.buildevent.BuildCompleteEvent;
26import com.google.devtools.build.lib.buildtool.buildevent.ExecutionStartingEvent;
Tobias Werth522099d2019-08-13 07:29:04 -070027import com.google.devtools.build.lib.buildtool.buildevent.ProfilerStartedEvent;
philwo3bcb9f62017-09-06 12:52:21 +020028import com.google.devtools.build.lib.clock.BlazeClock;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010029import com.google.devtools.build.lib.events.Event;
30import com.google.devtools.build.lib.events.Reporter;
janakr1fb90fd2017-03-30 21:07:04 +000031import com.google.devtools.build.lib.exec.ExecutionOptions;
32import com.google.devtools.build.lib.exec.ExecutorBuilder;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010033import com.google.devtools.build.lib.profiler.Profiler;
34import com.google.devtools.build.lib.profiler.ProfilerTask;
ulfjack4cf2ebd2018-06-11 06:00:36 -070035import com.google.devtools.build.lib.profiler.SilentCloseable;
janakrac58ca82019-02-14 09:34:20 -080036import com.google.devtools.build.lib.skyframe.ExecutionFinishedEvent;
Tobias Werth522099d2019-08-13 07:29:04 -070037import com.google.devtools.build.lib.vfs.Path;
38import java.io.IOException;
ulfjack54c8fcb2018-11-19 05:36:37 -080039import java.nio.charset.StandardCharsets;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010040import java.util.ArrayList;
41import java.util.List;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010042
43/**
44 * Blaze module for the build summary message that reports various stats to the user.
45 */
46public class BuildSummaryStatsModule extends BlazeModule {
47
janakrc3bcb982020-04-14 06:50:08 -070048 private static final GoogleLogger logger = GoogleLogger.forEnclosingClass();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010049
tomlu3d1a1942017-11-29 14:01:21 -080050 private ActionKeyContext actionKeyContext;
ulfjack3b0d72a2018-09-20 06:17:16 -070051 private CriticalPathComputer criticalPathComputer;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010052 private EventBus eventBus;
53 private Reporter reporter;
janakr1fb90fd2017-03-30 21:07:04 +000054 private boolean enabled;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010055
ulfjack97a3ee42018-11-20 05:40:35 -080056 private boolean statsSummary;
57 private long commandStartMillis;
58 private long executionStartMillis;
59 private long executionEndMillis;
Googlera8023b72018-04-03 08:08:18 -070060 private SpawnStats spawnStats;
Tobias Werth522099d2019-08-13 07:29:04 -070061 private Path profilePath;
Googlera8023b72018-04-03 08:08:18 -070062
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010063 @Override
ulfjacka6a99102017-06-13 17:15:45 +020064 public void beforeCommand(CommandEnvironment env) {
Ulf Adams633f5392015-09-15 11:13:08 +000065 this.reporter = env.getReporter();
66 this.eventBus = env.getEventBus();
tomlu3d1a1942017-11-29 14:01:21 -080067 this.actionKeyContext = env.getSkyframeExecutor().getActionKeyContext();
ulfjack97a3ee42018-11-20 05:40:35 -080068 commandStartMillis = env.getCommandStartTime();
Googlera8023b72018-04-03 08:08:18 -070069 this.spawnStats = new SpawnStats();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010070 eventBus.register(this);
71 }
72
Ulf Adams10efbf22016-04-18 11:35:25 +000073 @Override
74 public void afterCommand() {
75 this.criticalPathComputer = null;
76 this.eventBus = null;
77 this.reporter = null;
Googlera8023b72018-04-03 08:08:18 -070078 this.spawnStats = null;
Ulf Adams10efbf22016-04-18 11:35:25 +000079 }
80
janakr1fb90fd2017-03-30 21:07:04 +000081 @Override
82 public void executorInit(CommandEnvironment env, BuildRequest request, ExecutorBuilder builder) {
83 enabled = env.getOptions().getOptions(ExecutionOptions.class).enableCriticalPathProfiling;
Tobias Werthb3981192020-06-05 06:13:30 -070084 statsSummary = env.getOptions().getOptions(ExecutionOptions.class).statsSummary;
janakr1fb90fd2017-03-30 21:07:04 +000085 }
86
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010087 @Subscribe
88 public void executionPhaseStarting(ExecutionStartingEvent event) {
ulfjack97a3ee42018-11-20 05:40:35 -080089 // TODO(ulfjack): Make sure to use the same clock as for commandStartMillis.
90 executionStartMillis = BlazeClock.instance().currentTimeMillis();
janakr1fb90fd2017-03-30 21:07:04 +000091 if (enabled) {
janakr16274e42018-11-13 13:04:14 -080092 criticalPathComputer = new CriticalPathComputer(actionKeyContext, BlazeClock.instance());
janakr1fb90fd2017-03-30 21:07:04 +000093 eventBus.register(criticalPathComputer);
94 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010095 }
96
97 @Subscribe
Tobias Werth522099d2019-08-13 07:29:04 -070098 public void profileStarting(ProfilerStartedEvent event) {
99 this.profilePath = event.getProfilePath();
100 }
101
102 @Subscribe
ulfjack97a3ee42018-11-20 05:40:35 -0800103 public void executionPhaseFinish(@SuppressWarnings("unused") ExecutionFinishedEvent event) {
104 executionEndMillis = BlazeClock.instance().currentTimeMillis();
105 }
106
107 @Subscribe
ulfjackdf004162018-11-13 07:13:42 -0800108 @AllowConcurrentEvents
Googlera8023b72018-04-03 08:08:18 -0700109 public void actionResultReceived(ActionResultReceivedEvent event) {
110 spawnStats.countActionResult(event.getActionResult());
111 }
112
113 @Subscribe
Tobias Werthc0180512020-09-08 08:03:03 -0700114 @AllowConcurrentEvents
115 public void actionCompletion(ActionCompletionEvent event) {
116 spawnStats.incrementActionCount();
117 }
118
119 @Subscribe
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100120 public void buildComplete(BuildCompleteEvent event) {
121 try {
122 // We might want to make this conditional on a flag; it can sometimes be a bit of a nuisance.
123 List<String> items = new ArrayList<>();
124 items.add(String.format("Elapsed time: %.3fs", event.getResult().getElapsedSeconds()));
ulfjack54c8fcb2018-11-19 05:36:37 -0800125 event.getResult().getBuildToolLogCollection()
126 .addDirectValue(
127 "elapsed time",
128 String.format(
129 "%f", event.getResult().getElapsedSeconds()).getBytes(StandardCharsets.UTF_8));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100130
ulfjack97a3ee42018-11-20 05:40:35 -0800131 AggregatedCriticalPath criticalPath = AggregatedCriticalPath.EMPTY;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100132 if (criticalPathComputer != null) {
ulfjack4cf2ebd2018-06-11 06:00:36 -0700133 try (SilentCloseable c =
134 Profiler.instance().profile(ProfilerTask.CRITICAL_PATH, "Critical path")) {
ulfjack97a3ee42018-11-20 05:40:35 -0800135 criticalPath = criticalPathComputer.aggregate();
Googlerbed7aba2018-10-18 14:19:11 -0700136 items.add(criticalPath.toStringSummaryNoRemote());
ulfjack54c8fcb2018-11-19 05:36:37 -0800137 event.getResult().getBuildToolLogCollection()
138 .addDirectValue(
139 "critical path", criticalPath.toString().getBytes(StandardCharsets.UTF_8));
janakrc3bcb982020-04-14 06:50:08 -0700140 logger.atInfo().log(criticalPath.toString());
141 logger.atInfo().log(
142 "Slowest actions:\n %s",
143 Joiner.on("\n ").join(criticalPathComputer.getSlowestComponents()));
ulfjack4cf2ebd2018-06-11 06:00:36 -0700144 // We reverse the critical path because the profiler expect events ordered by the time
145 // when the actions were executed while critical path computation is stored in the reverse
146 // way.
ulfjack8b7aad42018-09-19 04:05:59 -0700147 for (CriticalPathComponent stat : criticalPath.components().reverse()) {
ulfjack4cf2ebd2018-06-11 06:00:36 -0700148 Profiler.instance()
149 .logSimpleTaskDuration(
ulfjack1af71e52018-07-24 00:31:37 -0700150 stat.getStartTimeNanos(),
151 stat.getElapsedTime(),
ulfjack4cf2ebd2018-06-11 06:00:36 -0700152 ProfilerTask.CRITICAL_PATH_COMPONENT,
153 stat.prettyPrintAction());
154 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100155 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100156 }
Tobias Werth522099d2019-08-13 07:29:04 -0700157 if (profilePath != null) {
158 // This leads to missing the afterCommand profiles of the other modules in the profile.
159 // Since the BEP currently shuts down at the BuildCompleteEvent, we cannot just move posting
160 // the BuildToolLogs to afterCommand of this module.
161 try {
162 Profiler.instance().stop();
163 event
164 .getResult()
165 .getBuildToolLogCollection()
166 .addLocalFile(profilePath.getBaseName(), profilePath);
167 } catch (IOException e) {
168 reporter.handle(Event.error("Error while writing profile file: " + e.getMessage()));
169 }
170 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100171
Googlera8023b72018-04-03 08:08:18 -0700172 String spawnSummary = spawnStats.getSummary();
ulfjack97a3ee42018-11-20 05:40:35 -0800173 if (statsSummary) {
Googler8bbc0582018-11-23 06:37:41 -0800174 reporter.handle(Event.info(spawnSummary));
ulfjack97a3ee42018-11-20 05:40:35 -0800175 reporter.handle(
176 Event.info(
177 String.format(
178 "Total action wall time %.2fs", spawnStats.getTotalWallTimeMillis() / 1000.0)));
179 if (criticalPath != AggregatedCriticalPath.EMPTY) {
180 reporter.handle(Event.info(criticalPath.getNewStringSummary()));
181 }
182 long now = event.getResult().getStopTime();
183 long executionTime = executionEndMillis - executionStartMillis;
184 long overheadTime = now - commandStartMillis - executionTime;
185 reporter.handle(
186 Event.info(
187 String.format(
188 "Elapsed time %.2fs (preparation %.2fs, execution %.2fs)",
189 (now - commandStartMillis) / 1000.0,
190 overheadTime / 1000.0,
191 executionTime / 1000.0)));
192 } else {
193 reporter.handle(Event.info(Joiner.on(", ").join(items)));
194 reporter.handle(Event.info(spawnSummary));
195 }
Googlera8023b72018-04-03 08:08:18 -0700196
ulfjack54c8fcb2018-11-19 05:36:37 -0800197 event.getResult().getBuildToolLogCollection()
198 .addDirectValue("process stats", spawnSummary.getBytes(StandardCharsets.UTF_8));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100199 } finally {
Tobias Werth9db98722019-08-13 08:55:13 -0700200 if (criticalPathComputer != null) {
201 eventBus.unregister(criticalPathComputer);
202 criticalPathComputer = null;
203 }
Tobias Werth522099d2019-08-13 07:29:04 -0700204 profilePath = null;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100205 }
206 }
207}