Damien Martin-Guillerez | f88f4d8 | 2015-09-25 13:56:55 +0000 | [diff] [blame] | 1 | // Copyright 2014 The Bazel Authors. All rights reserved. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 2 | // |
| 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. |
| 14 | package com.google.devtools.build.lib.runtime; |
| 15 | |
| 16 | import com.google.common.base.Joiner; |
twerth | 178615d | 2020-11-30 07:29:38 -0800 | [diff] [blame] | 17 | import com.google.common.collect.ImmutableMap; |
ulfjack | df00416 | 2018-11-13 07:13:42 -0800 | [diff] [blame] | 18 | import com.google.common.eventbus.AllowConcurrentEvents; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 19 | import com.google.common.eventbus.EventBus; |
| 20 | import com.google.common.eventbus.Subscribe; |
janakr | c3bcb98 | 2020-04-14 06:50:08 -0700 | [diff] [blame] | 21 | import com.google.common.flogger.GoogleLogger; |
Tobias Werth | c018051 | 2020-09-08 08:03:03 -0700 | [diff] [blame] | 22 | import com.google.devtools.build.lib.actions.ActionCompletionEvent; |
tomlu | 3d1a194 | 2017-11-29 14:01:21 -0800 | [diff] [blame] | 23 | import com.google.devtools.build.lib.actions.ActionKeyContext; |
Googler | a8023b7 | 2018-04-03 08:08:18 -0700 | [diff] [blame] | 24 | import com.google.devtools.build.lib.actions.ActionResultReceivedEvent; |
Googler | 84228ea | 2024-09-09 06:39:28 -0700 | [diff] [blame] | 25 | import com.google.devtools.build.lib.actions.cache.PostableActionCacheStats; |
janakr | 1fb90fd | 2017-03-30 21:07:04 +0000 | [diff] [blame] | 26 | import com.google.devtools.build.lib.buildtool.BuildRequest; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 27 | import com.google.devtools.build.lib.buildtool.buildevent.BuildCompleteEvent; |
| 28 | import com.google.devtools.build.lib.buildtool.buildevent.ExecutionStartingEvent; |
Tobias Werth | 522099d | 2019-08-13 07:29:04 -0700 | [diff] [blame] | 29 | import com.google.devtools.build.lib.buildtool.buildevent.ProfilerStartedEvent; |
philwo | 3bcb9f6 | 2017-09-06 12:52:21 +0200 | [diff] [blame] | 30 | import com.google.devtools.build.lib.clock.BlazeClock; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 31 | import com.google.devtools.build.lib.events.Event; |
| 32 | import com.google.devtools.build.lib.events.Reporter; |
janakr | 1fb90fd | 2017-03-30 21:07:04 +0000 | [diff] [blame] | 33 | import com.google.devtools.build.lib.exec.ExecutionOptions; |
| 34 | import com.google.devtools.build.lib.exec.ExecutorBuilder; |
Yannic Bonenberger | 986863a | 2023-04-06 04:37:45 -0700 | [diff] [blame] | 35 | import com.google.devtools.build.lib.metrics.criticalpath.AggregatedCriticalPath; |
| 36 | import com.google.devtools.build.lib.metrics.criticalpath.CriticalPathComponent; |
| 37 | import com.google.devtools.build.lib.metrics.criticalpath.CriticalPathComputer; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 38 | import com.google.devtools.build.lib.profiler.Profiler; |
| 39 | import com.google.devtools.build.lib.profiler.ProfilerTask; |
ulfjack | 4cf2ebd | 2018-06-11 06:00:36 -0700 | [diff] [blame] | 40 | import com.google.devtools.build.lib.profiler.SilentCloseable; |
janakr | ac58ca8 | 2019-02-14 09:34:20 -0800 | [diff] [blame] | 41 | import com.google.devtools.build.lib.skyframe.ExecutionFinishedEvent; |
Googler | aeb5372 | 2022-08-26 07:01:58 -0700 | [diff] [blame] | 42 | import com.google.devtools.build.lib.skyframe.TopLevelStatusEvents.TopLevelTargetPendingExecutionEvent; |
Tobias Werth | 522099d | 2019-08-13 07:29:04 -0700 | [diff] [blame] | 43 | import java.io.IOException; |
ulfjack | 54c8fcb | 2018-11-19 05:36:37 -0800 | [diff] [blame] | 44 | import java.nio.charset.StandardCharsets; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 45 | import java.util.ArrayList; |
| 46 | import java.util.List; |
Googler | aeb5372 | 2022-08-26 07:01:58 -0700 | [diff] [blame] | 47 | import java.util.concurrent.atomic.AtomicBoolean; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 48 | |
| 49 | /** |
| 50 | * Blaze module for the build summary message that reports various stats to the user. |
| 51 | */ |
| 52 | public class BuildSummaryStatsModule extends BlazeModule { |
| 53 | |
janakr | c3bcb98 | 2020-04-14 06:50:08 -0700 | [diff] [blame] | 54 | private static final GoogleLogger logger = GoogleLogger.forEnclosingClass(); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 55 | |
tomlu | 3d1a194 | 2017-11-29 14:01:21 -0800 | [diff] [blame] | 56 | private ActionKeyContext actionKeyContext; |
ulfjack | 3b0d72a | 2018-09-20 06:17:16 -0700 | [diff] [blame] | 57 | private CriticalPathComputer criticalPathComputer; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 58 | private EventBus eventBus; |
| 59 | private Reporter reporter; |
janakr | 1fb90fd | 2017-03-30 21:07:04 +0000 | [diff] [blame] | 60 | private boolean enabled; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 61 | |
ulfjack | 97a3ee4 | 2018-11-20 05:40:35 -0800 | [diff] [blame] | 62 | private boolean statsSummary; |
| 63 | private long commandStartMillis; |
| 64 | private long executionStartMillis; |
| 65 | private long executionEndMillis; |
Googler | a8023b7 | 2018-04-03 08:08:18 -0700 | [diff] [blame] | 66 | private SpawnStats spawnStats; |
Googler | 09dafdd | 2024-08-02 05:14:57 -0700 | [diff] [blame] | 67 | private ProfilerStartedEvent profileEvent; |
Googler | aeb5372 | 2022-08-26 07:01:58 -0700 | [diff] [blame] | 68 | private AtomicBoolean executionStarted; |
Googler | a8023b7 | 2018-04-03 08:08:18 -0700 | [diff] [blame] | 69 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 70 | @Override |
ulfjack | a6a9910 | 2017-06-13 17:15:45 +0200 | [diff] [blame] | 71 | public void beforeCommand(CommandEnvironment env) { |
Ulf Adams | 633f539 | 2015-09-15 11:13:08 +0000 | [diff] [blame] | 72 | this.reporter = env.getReporter(); |
| 73 | this.eventBus = env.getEventBus(); |
tomlu | 3d1a194 | 2017-11-29 14:01:21 -0800 | [diff] [blame] | 74 | this.actionKeyContext = env.getSkyframeExecutor().getActionKeyContext(); |
ulfjack | 97a3ee4 | 2018-11-20 05:40:35 -0800 | [diff] [blame] | 75 | commandStartMillis = env.getCommandStartTime(); |
Googler | a8023b7 | 2018-04-03 08:08:18 -0700 | [diff] [blame] | 76 | this.spawnStats = new SpawnStats(); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 77 | eventBus.register(this); |
Googler | aeb5372 | 2022-08-26 07:01:58 -0700 | [diff] [blame] | 78 | executionStarted = new AtomicBoolean(false); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 79 | } |
| 80 | |
Ulf Adams | 10efbf2 | 2016-04-18 11:35:25 +0000 | [diff] [blame] | 81 | @Override |
| 82 | public void afterCommand() { |
| 83 | this.criticalPathComputer = null; |
| 84 | this.eventBus = null; |
| 85 | this.reporter = null; |
Googler | a8023b7 | 2018-04-03 08:08:18 -0700 | [diff] [blame] | 86 | this.spawnStats = null; |
Googler | aeb5372 | 2022-08-26 07:01:58 -0700 | [diff] [blame] | 87 | executionStarted.set(false); |
Ulf Adams | 10efbf2 | 2016-04-18 11:35:25 +0000 | [diff] [blame] | 88 | } |
| 89 | |
janakr | 1fb90fd | 2017-03-30 21:07:04 +0000 | [diff] [blame] | 90 | @Override |
| 91 | public void executorInit(CommandEnvironment env, BuildRequest request, ExecutorBuilder builder) { |
| 92 | enabled = env.getOptions().getOptions(ExecutionOptions.class).enableCriticalPathProfiling; |
Tobias Werth | b398119 | 2020-06-05 06:13:30 -0700 | [diff] [blame] | 93 | statsSummary = env.getOptions().getOptions(ExecutionOptions.class).statsSummary; |
Googler | 7e9e503 | 2022-08-26 08:53:41 -0700 | [diff] [blame] | 94 | if (enabled) { |
Googler | e92249e | 2023-06-14 06:11:44 -0700 | [diff] [blame] | 95 | criticalPathComputer = new CriticalPathComputer(actionKeyContext); |
Googler | 7e9e503 | 2022-08-26 08:53:41 -0700 | [diff] [blame] | 96 | eventBus.register(criticalPathComputer); |
| 97 | } |
janakr | 1fb90fd | 2017-03-30 21:07:04 +0000 | [diff] [blame] | 98 | } |
| 99 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 100 | @Subscribe |
| 101 | public void executionPhaseStarting(ExecutionStartingEvent event) { |
Googler | aeb5372 | 2022-08-26 07:01:58 -0700 | [diff] [blame] | 102 | 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() { |
ulfjack | 97a3ee4 | 2018-11-20 05:40:35 -0800 | [diff] [blame] | 119 | // TODO(ulfjack): Make sure to use the same clock as for commandStartMillis. |
| 120 | executionStartMillis = BlazeClock.instance().currentTimeMillis(); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 121 | } |
| 122 | |
| 123 | @Subscribe |
Tobias Werth | 522099d | 2019-08-13 07:29:04 -0700 | [diff] [blame] | 124 | public void profileStarting(ProfilerStartedEvent event) { |
Googler | 09dafdd | 2024-08-02 05:14:57 -0700 | [diff] [blame] | 125 | this.profileEvent = event; |
Tobias Werth | 522099d | 2019-08-13 07:29:04 -0700 | [diff] [blame] | 126 | } |
| 127 | |
| 128 | @Subscribe |
ulfjack | 97a3ee4 | 2018-11-20 05:40:35 -0800 | [diff] [blame] | 129 | public void executionPhaseFinish(@SuppressWarnings("unused") ExecutionFinishedEvent event) { |
| 130 | executionEndMillis = BlazeClock.instance().currentTimeMillis(); |
| 131 | } |
| 132 | |
| 133 | @Subscribe |
ulfjack | df00416 | 2018-11-13 07:13:42 -0800 | [diff] [blame] | 134 | @AllowConcurrentEvents |
Googler | a8023b7 | 2018-04-03 08:08:18 -0700 | [diff] [blame] | 135 | public void actionResultReceived(ActionResultReceivedEvent event) { |
| 136 | spawnStats.countActionResult(event.getActionResult()); |
| 137 | } |
| 138 | |
| 139 | @Subscribe |
Tobias Werth | c018051 | 2020-09-08 08:03:03 -0700 | [diff] [blame] | 140 | @AllowConcurrentEvents |
| 141 | public void actionCompletion(ActionCompletionEvent event) { |
| 142 | spawnStats.incrementActionCount(); |
| 143 | } |
| 144 | |
| 145 | @Subscribe |
Googler | 84228ea | 2024-09-09 06:39:28 -0700 | [diff] [blame] | 146 | public void actionCacheStats(PostableActionCacheStats event) { |
| 147 | spawnStats.recordActionCacheStats(event.asProto()); |
| 148 | } |
| 149 | |
| 150 | @Subscribe |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 151 | 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())); |
ulfjack | 54c8fcb | 2018-11-19 05:36:37 -0800 | [diff] [blame] | 156 | event.getResult().getBuildToolLogCollection() |
| 157 | .addDirectValue( |
| 158 | "elapsed time", |
| 159 | String.format( |
| 160 | "%f", event.getResult().getElapsedSeconds()).getBytes(StandardCharsets.UTF_8)); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 161 | |
ulfjack | 97a3ee4 | 2018-11-20 05:40:35 -0800 | [diff] [blame] | 162 | AggregatedCriticalPath criticalPath = AggregatedCriticalPath.EMPTY; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 163 | if (criticalPathComputer != null) { |
ulfjack | 4cf2ebd | 2018-06-11 06:00:36 -0700 | [diff] [blame] | 164 | try (SilentCloseable c = |
| 165 | Profiler.instance().profile(ProfilerTask.CRITICAL_PATH, "Critical path")) { |
ulfjack | 97a3ee4 | 2018-11-20 05:40:35 -0800 | [diff] [blame] | 166 | criticalPath = criticalPathComputer.aggregate(); |
Googler | bed7aba | 2018-10-18 14:19:11 -0700 | [diff] [blame] | 167 | items.add(criticalPath.toStringSummaryNoRemote()); |
ulfjack | 54c8fcb | 2018-11-19 05:36:37 -0800 | [diff] [blame] | 168 | event.getResult().getBuildToolLogCollection() |
| 169 | .addDirectValue( |
| 170 | "critical path", criticalPath.toString().getBytes(StandardCharsets.UTF_8)); |
Googler | 1987af2 | 2021-12-06 10:27:42 -0800 | [diff] [blame] | 171 | logger.atInfo().log("%s", criticalPath); |
janakr | c3bcb98 | 2020-04-14 06:50:08 -0700 | [diff] [blame] | 172 | logger.atInfo().log( |
| 173 | "Slowest actions:\n %s", |
| 174 | Joiner.on("\n ").join(criticalPathComputer.getSlowestComponents())); |
ulfjack | 4cf2ebd | 2018-06-11 06:00:36 -0700 | [diff] [blame] | 175 | // 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. |
ulfjack | 8b7aad4 | 2018-09-19 04:05:59 -0700 | [diff] [blame] | 178 | for (CriticalPathComponent stat : criticalPath.components().reverse()) { |
ulfjack | 4cf2ebd | 2018-06-11 06:00:36 -0700 | [diff] [blame] | 179 | Profiler.instance() |
| 180 | .logSimpleTaskDuration( |
ulfjack | 1af71e5 | 2018-07-24 00:31:37 -0700 | [diff] [blame] | 181 | stat.getStartTimeNanos(), |
| 182 | stat.getElapsedTime(), |
ulfjack | 4cf2ebd | 2018-06-11 06:00:36 -0700 | [diff] [blame] | 183 | ProfilerTask.CRITICAL_PATH_COMPONENT, |
| 184 | stat.prettyPrintAction()); |
| 185 | } |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 186 | } |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 187 | } |
Googler | cda1a28 | 2024-08-15 08:30:32 -0700 | [diff] [blame] | 188 | if (profileEvent != null && profileEvent.getProfile() != null) { |
Tobias Werth | 522099d | 2019-08-13 07:29:04 -0700 | [diff] [blame] | 189 | // 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(); |
Googler | cda1a28 | 2024-08-15 08:30:32 -0700 | [diff] [blame] | 194 | profileEvent.getProfile().publish(event.getResult().getBuildToolLogCollection()); |
Tobias Werth | 522099d | 2019-08-13 07:29:04 -0700 | [diff] [blame] | 195 | } catch (IOException e) { |
| 196 | reporter.handle(Event.error("Error while writing profile file: " + e.getMessage())); |
| 197 | } |
| 198 | } |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 199 | |
twerth | 178615d | 2020-11-30 07:29:38 -0800 | [diff] [blame] | 200 | ImmutableMap<String, Integer> spawnSummary = spawnStats.getSummary(); |
| 201 | String spawnSummaryString = SpawnStats.convertSummaryToString(spawnSummary); |
ulfjack | 97a3ee4 | 2018-11-20 05:40:35 -0800 | [diff] [blame] | 202 | if (statsSummary) { |
twerth | 178615d | 2020-11-30 07:29:38 -0800 | [diff] [blame] | 203 | reporter.handle(Event.info(spawnSummaryString)); |
ulfjack | 97a3ee4 | 2018-11-20 05:40:35 -0800 | [diff] [blame] | 204 | 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))); |
Googler | 2d6807c | 2024-09-04 01:23:31 -0700 | [diff] [blame] | 221 | logger.atInfo().log("Stats summary: %s", Joiner.on(", ").join(items)); |
ulfjack | 97a3ee4 | 2018-11-20 05:40:35 -0800 | [diff] [blame] | 222 | } else { |
| 223 | reporter.handle(Event.info(Joiner.on(", ").join(items))); |
twerth | 178615d | 2020-11-30 07:29:38 -0800 | [diff] [blame] | 224 | reporter.handle(Event.info(spawnSummaryString)); |
ulfjack | 97a3ee4 | 2018-11-20 05:40:35 -0800 | [diff] [blame] | 225 | } |
Googler | a8023b7 | 2018-04-03 08:08:18 -0700 | [diff] [blame] | 226 | |
twerth | 178615d | 2020-11-30 07:29:38 -0800 | [diff] [blame] | 227 | event |
| 228 | .getResult() |
| 229 | .getBuildToolLogCollection() |
| 230 | .addDirectValue("process stats", spawnSummaryString.getBytes(StandardCharsets.UTF_8)); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 231 | } finally { |
Tobias Werth | 9db9872 | 2019-08-13 08:55:13 -0700 | [diff] [blame] | 232 | if (criticalPathComputer != null) { |
| 233 | eventBus.unregister(criticalPathComputer); |
| 234 | criticalPathComputer = null; |
| 235 | } |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 236 | } |
| 237 | } |
| 238 | } |