blob: 71460b2ca604ed5a03d606a4b0c15c866369eeec [file] [log] [blame]
// Copyright 2020 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.packages.metrics;
import com.google.common.base.Preconditions;
import com.google.devtools.build.lib.cmdline.PackageIdentifier;
import com.google.devtools.build.lib.collect.Extrema;
import com.google.devtools.build.lib.packages.Package;
import com.google.devtools.build.lib.packages.PackageLoadingListener;
import com.google.devtools.build.lib.syntax.StarlarkSemantics;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.annotation.concurrent.GuardedBy;
/** Tracks per-invocation extreme package loading events. */
public class ExtremaPackageLoadingListener implements PackageLoadingListener {
@GuardedBy("this")
private int currentNumPackagesToTrack;
@GuardedBy("this")
private Extrema<PackageIdentifierAndLong> slowestPackagesToLoad;
@GuardedBy("this")
private Extrema<PackageIdentifierAndLong> largestPackages;
@GuardedBy("this")
private Extrema<PackageIdentifierAndLong> packagesWithMostTransitiveLoads;
@GuardedBy("this")
private Extrema<PackageIdentifierAndLong> packagesWithMostComputationSteps;
private static ExtremaPackageLoadingListener instance = null;
public static synchronized ExtremaPackageLoadingListener getInstance() {
if (instance == null) {
instance = new ExtremaPackageLoadingListener();
}
return instance;
}
private ExtremaPackageLoadingListener() {
this.currentNumPackagesToTrack = 0;
this.slowestPackagesToLoad = Extrema.max(0);
this.largestPackages = Extrema.max(0);
this.packagesWithMostTransitiveLoads = Extrema.max(0);
this.packagesWithMostComputationSteps = Extrema.max(0);
}
synchronized void setNumPackagesToTrack(int numPackagesToTrack) {
Preconditions.checkArgument(numPackagesToTrack >= 0, "num packages must be >= 0");
if (numPackagesToTrack == currentNumPackagesToTrack) {
// Micro-optimization to avoid turning over collections.
clear();
return;
}
currentNumPackagesToTrack = numPackagesToTrack;
this.slowestPackagesToLoad = Extrema.max(currentNumPackagesToTrack);
this.largestPackages = Extrema.max(currentNumPackagesToTrack);
this.packagesWithMostTransitiveLoads = Extrema.max(currentNumPackagesToTrack);
this.packagesWithMostComputationSteps = Extrema.max(currentNumPackagesToTrack);
}
@Override
public synchronized void onLoadingCompleteAndSuccessful(
Package pkg, StarlarkSemantics starlarkSemantics, long loadTimeNanos) {
if (currentNumPackagesToTrack == 0) {
// Micro-optimization - no need to track.
return;
}
long loadTimeMillis = TimeUnit.MILLISECONDS.convert(loadTimeNanos, TimeUnit.NANOSECONDS);
slowestPackagesToLoad.aggregate(
new PackageIdentifierAndLong(pkg.getPackageIdentifier(), loadTimeMillis));
packagesWithMostComputationSteps.aggregate(
new PackageIdentifierAndLong(pkg.getPackageIdentifier(), pkg.getComputationSteps()));
largestPackages.aggregate(
new PackageIdentifierAndLong(pkg.getPackageIdentifier(), pkg.getTargets().size()));
packagesWithMostTransitiveLoads.aggregate(
new PackageIdentifierAndLong(
pkg.getPackageIdentifier(), pkg.getStarlarkFileDependencies().size()));
}
public synchronized TopPackages getTopPackages() {
TopPackages result =
new TopPackages(
slowestPackagesToLoad.getExtremeElements(),
packagesWithMostComputationSteps.getExtremeElements(),
largestPackages.getExtremeElements(),
packagesWithMostTransitiveLoads.getExtremeElements());
return result;
}
synchronized TopPackages getAndResetTopPackages() {
TopPackages result = getTopPackages();
clear();
return result;
}
private synchronized void clear() {
slowestPackagesToLoad.clear();
packagesWithMostComputationSteps.clear();
largestPackages.clear();
packagesWithMostTransitiveLoads.clear();
}
/** Container around lists of packages which are slow or large in some form. */
public static class TopPackages {
private final List<PackageIdentifierAndLong> slowestPackages;
private final List<PackageIdentifierAndLong> packagesWithMostComputationSteps;
private final List<PackageIdentifierAndLong> largestPackages;
private final List<PackageIdentifierAndLong> packagesWithMostTransitiveLoads;
private TopPackages(
List<PackageIdentifierAndLong> slowestPackages,
List<PackageIdentifierAndLong> packagesWithMostComputationSteps,
List<PackageIdentifierAndLong> largestPackages,
List<PackageIdentifierAndLong> packagesWithMostTransitiveLoads) {
this.slowestPackages = slowestPackages;
this.packagesWithMostComputationSteps = packagesWithMostComputationSteps;
this.largestPackages = largestPackages;
this.packagesWithMostTransitiveLoads = packagesWithMostTransitiveLoads;
}
public List<PackageIdentifierAndLong> getSlowestPackages() {
return slowestPackages;
}
public List<PackageIdentifierAndLong> getPackagesWithMostComputationSteps() {
return packagesWithMostComputationSteps;
}
public List<PackageIdentifierAndLong> getLargestPackages() {
return largestPackages;
}
public List<PackageIdentifierAndLong> getPackagesWithMostTransitiveLoads() {
return packagesWithMostTransitiveLoads;
}
}
/** A pair of PackageIdentifier and a corresponding value. */
public static class PackageIdentifierAndLong implements Comparable<PackageIdentifierAndLong> {
private final PackageIdentifier pkgId;
private final long val;
private PackageIdentifierAndLong(PackageIdentifier pkgId, long val) {
this.pkgId = pkgId;
this.val = val;
}
public long getVal() {
return val;
}
public PackageIdentifier getPkgId() {
return pkgId;
}
@Override
public int compareTo(PackageIdentifierAndLong other) {
return Long.compare(val, other.val);
}
@Override
public String toString() {
return String.format("%s (%d)", pkgId, val);
}
}
}