| // Copyright 2018 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.query2.aquery; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.devtools.build.lib.actions.ActionAnalysisMetadata; |
| import com.google.devtools.build.lib.actions.ActionOwner; |
| import com.google.devtools.build.lib.analysis.AspectValue; |
| import com.google.devtools.build.lib.analysis.ConfiguredTargetValue; |
| import com.google.devtools.build.lib.buildeventstream.BuildEvent; |
| import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos; |
| import com.google.devtools.build.lib.events.ExtendedEventHandler; |
| import com.google.devtools.build.lib.packages.AspectDescriptor; |
| import com.google.devtools.build.lib.query2.engine.QueryEnvironment.TargetAccessor; |
| import com.google.devtools.build.lib.skyframe.RuleConfiguredTargetValue; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| |
| /** Output callback for aquery, prints a human readable summary. */ |
| class ActionGraphSummaryOutputFormatterCallback extends AqueryThreadsafeCallback { |
| |
| private final AqueryActionFilter actionFilters; |
| private final Map<String, Integer> mnemonicToCount = new HashMap<>(); |
| private final Map<String, Integer> configurationToCount = new HashMap<>(); |
| private final Map<String, Integer> execPlatformToCount = new HashMap<>(); |
| private final Map<String, Integer> aspectToCount = new HashMap<>(); |
| |
| ActionGraphSummaryOutputFormatterCallback( |
| ExtendedEventHandler eventHandler, |
| AqueryOptions options, |
| OutputStream out, |
| TargetAccessor<ConfiguredTargetValue> accessor, |
| AqueryActionFilter actionFilters) { |
| super(eventHandler, options, out, accessor); |
| this.actionFilters = actionFilters; |
| } |
| |
| @Override |
| public String getName() { |
| return "summary"; |
| } |
| |
| @Override |
| public void processOutput(Iterable<ConfiguredTargetValue> partialResult) |
| throws IOException, InterruptedException { |
| // Enabling includeParamFiles should enable includeCommandline by default. |
| options.includeCommandline |= options.includeParamFiles; |
| |
| for (ConfiguredTargetValue configuredTargetValue : partialResult) { |
| if (!(configuredTargetValue instanceof RuleConfiguredTargetValue)) { |
| // We have to include non-rule values in the graph to visit their dependencies, but they |
| // don't have any actions to print out. |
| continue; |
| } |
| for (ActionAnalysisMetadata action : |
| ((RuleConfiguredTargetValue) configuredTargetValue).getActions()) { |
| processAction(action); |
| } |
| if (options.useAspects) { |
| for (AspectValue aspectValue : accessor.getAspectValues(configuredTargetValue)) { |
| for (ActionAnalysisMetadata action : aspectValue.getActions()) { |
| processAction(action); |
| } |
| } |
| } |
| } |
| } |
| |
| private void processAction(ActionAnalysisMetadata action) throws InterruptedException { |
| if (!AqueryUtils.matchesAqueryFilters(action, actionFilters)) { |
| return; |
| } |
| |
| mnemonicToCount.merge(action.getMnemonic(), 1, Integer::sum); |
| ActionOwner actionOwner = action.getOwner(); |
| if (actionOwner != null) { |
| BuildEvent configuration = actionOwner.getBuildConfigurationEvent(); |
| BuildEventStreamProtos.Configuration configProto = |
| configuration.asStreamProto(/*context=*/ null).getConfiguration(); |
| configurationToCount.merge(configProto.getMnemonic(), 1, Integer::sum); |
| |
| if (actionOwner.getExecutionPlatform() != null) { |
| execPlatformToCount.merge( |
| actionOwner.getExecutionPlatform().label().toString(), 1, Integer::sum); |
| } |
| |
| // In the case of aspect-on-aspect, AspectDescriptors are listed in |
| // topological order of the dependency graph. |
| // e.g. [A -> B] would imply that aspect A is applied on top of aspect B. |
| ImmutableList<AspectDescriptor> aspectDescriptors = |
| actionOwner.getAspectDescriptors().reverse(); |
| if (!aspectDescriptors.isEmpty()) { |
| aspectDescriptors.forEach( |
| aspectDescriptor -> |
| aspectToCount.merge(aspectDescriptor.getAspectClass().getName(), 1, Integer::sum)); |
| } |
| } |
| } |
| |
| @Override |
| public void close(boolean failFast) throws InterruptedException, IOException { |
| if (failFast) { |
| return; |
| } |
| |
| int totalActions = mnemonicToCount.values().stream().mapToInt(v -> v).sum(); |
| if (totalActions == 0) { |
| printStream.println("No actions matched."); |
| } else { |
| printStream.println(totalActions + " total action" + (totalActions == 1 ? "" : "s") + "."); |
| } |
| |
| printSummary(mnemonicToCount, "Mnemonics:"); |
| printSummary(configurationToCount, "Configurations:"); |
| printSummary(execPlatformToCount, "Execution Platforms:"); |
| printSummary(aspectToCount, "Aspects:"); |
| } |
| |
| private void printSummary(Map<String, Integer> actionsCount, String s) { |
| if (!actionsCount.isEmpty()) { |
| printStream.println(); |
| printStream.println(s); |
| actionsCount.entrySet().stream() |
| .sorted(Comparator.comparingInt(Entry::getValue)) |
| .forEach(entry -> printStream.println(" " + entry.getKey() + ": " + entry.getValue())); |
| } |
| } |
| } |