/* * Copyright 2017-present Facebook, Inc. * * 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.facebook.buck.event.listener; import com.facebook.buck.artifact_cache.HttpArtifactCacheEvent; import com.facebook.buck.event.AbstractBuckEvent; import com.facebook.buck.event.ActionGraphEvent; import com.facebook.buck.event.BuckEventBus; import com.facebook.buck.event.BuckEventListener; import com.facebook.buck.event.BuckInitializationDurationEvent; import com.facebook.buck.event.CommandEvent; import com.facebook.buck.event.EventKey; import com.facebook.buck.log.PerfTimesStats; import com.facebook.buck.log.views.JsonViews; import com.facebook.buck.model.BuildId; import com.facebook.buck.parser.ParseEvent; import com.facebook.buck.rules.BuildEvent; import com.facebook.buck.rules.BuildRuleEvent; import com.facebook.buck.util.environment.ExecutionEnvironment; import com.fasterxml.jackson.annotation.JsonView; import com.google.common.eventbus.Subscribe; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; public class PerfTimesEventListener implements BuckEventListener { private final BuckEventBus eventBus; private final AtomicLong buildPhasesLastEvent = new AtomicLong(); private final AtomicBoolean firstCacheFetchEvent = new AtomicBoolean(false); private final AtomicBoolean firstLocalBuildEvent = new AtomicBoolean(false); private PerfTimesStats.Builder perfTimesStatsBuilder = PerfTimesStats.builder(); /** * @param eventBus When we finish gather all data points, we will post the result as event back * into event bus. * @param executionEnvironment We need this in order to get Python_init_time, as it is provided by * our Python wrapper. */ public PerfTimesEventListener(BuckEventBus eventBus, ExecutionEnvironment executionEnvironment) { this.eventBus = eventBus; perfTimesStatsBuilder.setPythonTimeMs( Long.valueOf(executionEnvironment.getenv("BUCK_PYTHON_SPACE_INIT_TIME", "0"))); eventBus.post(PerfTimesEvent.update(perfTimesStatsBuilder.build())); } @Subscribe public synchronized void initializationFinished(BuckInitializationDurationEvent event) { buildPhasesLastEvent.set(event.getTimestamp()); perfTimesStatsBuilder.setInitTimeMs(event.getDuration()); eventBus.post(PerfTimesEvent.update(perfTimesStatsBuilder.build())); } @Subscribe public synchronized void parseStarted(ParseEvent.Started started) { perfTimesStatsBuilder.setProcessingTimeMs(getTimeDifferenceSinceLastEventToEvent(started)); eventBus.post(PerfTimesEvent.update(perfTimesStatsBuilder.build())); } @Subscribe public synchronized void parseFinished(ParseEvent.Finished finished) { perfTimesStatsBuilder.setParseTimeMs(getTimeDifferenceSinceLastEventToEvent(finished)); eventBus.post(PerfTimesEvent.update(perfTimesStatsBuilder.build())); } @Subscribe public void actionGraphFinished(ActionGraphEvent.Finished finished) { perfTimesStatsBuilder.setActionGraphTimeMs(getTimeDifferenceSinceLastEventToEvent(finished)); eventBus.post(PerfTimesEvent.update(perfTimesStatsBuilder.build())); } @Subscribe public void onHttpArtifactCacheStartedEvent(HttpArtifactCacheEvent.Started event) { if (firstCacheFetchEvent.compareAndSet(false, true)) { perfTimesStatsBuilder.setRulekeyTimeMs(getTimeDifferenceSinceLastEventToEvent(event)); eventBus.post(PerfTimesEvent.update(perfTimesStatsBuilder.build())); } } @Subscribe public void buildRuleWillBuildLocally(BuildRuleEvent.WillBuildLocally event) { if (firstLocalBuildEvent.compareAndSet(false, true)) { perfTimesStatsBuilder.setFetchTimeMs(getTimeDifferenceSinceLastEventToEvent(event)); eventBus.post(PerfTimesEvent.update(perfTimesStatsBuilder.build())); } } @Subscribe public synchronized void buildFinished(BuildEvent.Finished finished) { perfTimesStatsBuilder.setBuildTimeMs(getTimeDifferenceSinceLastEventToEvent(finished)); eventBus.post(PerfTimesEvent.update(perfTimesStatsBuilder.build())); } @Subscribe public synchronized void commandFinished(CommandEvent.Finished finished) { perfTimesStatsBuilder.setInstallTimeMs(getTimeDifferenceSinceLastEventToEvent(finished)); eventBus.post(PerfTimesEvent.complete(perfTimesStatsBuilder.build())); } /** Helper method, returns the time difference from last invocation of this method. */ private long getTimeDifferenceSinceLastEventToEvent(AbstractBuckEvent event) { long diff = event.getTimestamp() - buildPhasesLastEvent.get(); buildPhasesLastEvent.set(event.getTimestamp()); return diff; } @Override public void outputTrace(BuildId buildId) throws InterruptedException {} public static class PerfTimesEvent extends AbstractBuckEvent { private String eventName; @JsonView(JsonViews.MachineReadableLog.class) private PerfTimesStats perfTimesStats; public static PerfTimesEvent.Complete complete(PerfTimesStats stats) { return new PerfTimesEvent.Complete(stats); } public static PerfTimesEvent.Update update(PerfTimesStats stats) { return new PerfTimesEvent.Update(stats); } /** This event is to be used when all of the steps of {@link PerfTimesStats} are present. */ static class Complete extends PerfTimesEvent { Complete(PerfTimesStats stats) { super(stats, "PerfTimesStatsEvent.Complete"); } } /** This event is to be used as an update, expect some of the fields to be empty. */ static class Update extends PerfTimesEvent { Update(PerfTimesStats stats) { super(stats, "PerfTimesStatsEvent.Update"); } } PerfTimesEvent(PerfTimesStats perfTimesStats, String eventName) { super(EventKey.unique()); this.perfTimesStats = perfTimesStats; this.eventName = eventName; } PerfTimesStats getPerfTimesStats() { return perfTimesStats; } @Override protected String getValueString() { return perfTimesStats.toString(); } @Override public String getEventName() { return eventName; } } }