blob: 7c1cdb51c5eee5137202b195e361a99039a8e6b4 [file] [log] [blame]
// Copyright 2015 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.profiler.statistics;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Table;
import com.google.common.collect.Table.Cell;
import com.google.common.collect.Tables;
import com.google.devtools.build.lib.profiler.ProfilePhase;
import com.google.devtools.build.lib.profiler.ProfilerTask;
import com.google.devtools.build.lib.profiler.analysis.ProfileInfo;
import com.google.devtools.build.lib.profiler.analysis.ProfileInfo.Task;
import java.util.Arrays;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.SortedSet;
/**
* Compute and store statistics of all {@link ProfilerTask}s that begin with VFS_ in sorted order.
*/
public final class PhaseVfsStatistics implements Iterable<ProfilerTask> {
/**
* Duration, count and path for sorting by duration first and count in case of tie. Path for
* easy returning of a {@link SortedSet}.
*/
public static final class Stat implements Comparable<Stat> {
private long duration;
private long count;
public final String path;
public Stat(String path) {
this.path = path;
}
public Stat(Stat other) {
this.duration = other.duration;
this.count = other.count;
this.path = other.path;
}
public long getDuration() {
return duration;
}
public long getCount() {
return count;
}
private void add(Stat other) {
this.duration += other.duration;
this.count += other.count;
}
private void add(long duration) {
this.duration += duration;
this.count++;
}
/**
* Order first by duration, then count, then path
*/
@Override
public int compareTo(Stat o) {
return ComparisonChain.start()
.compare(duration, o.duration)
.compare(count, o.count)
.compare(path, o.path)
.result();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Stat) {
return compareTo((Stat) obj) == 0;
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(duration, count, path);
}
}
private final ProfilePhase phase;
private final Table<ProfilerTask, String, Stat> statistics;
public PhaseVfsStatistics(ProfilePhase phase) {
this.phase = phase;
this.statistics =
Tables.newCustomTable(
new EnumMap<ProfilerTask, Map<String, Stat>>(ProfilerTask.class), HashMap::new);
}
public PhaseVfsStatistics(final String workSpaceName, ProfilePhase phase, ProfileInfo info) {
this(phase);
addProfileInfo(workSpaceName, info);
}
/**
* Accumulate statistics from another {@link ProfileInfo} in this object.
*/
public void addProfileInfo(final String workSpaceName, ProfileInfo info) {
Task phaseTask = info.getPhaseTask(phase);
if (phaseTask == null) {
return;
}
collectVfsEntries(workSpaceName, info.getTasksForPhase(phaseTask));
}
public ProfilePhase getProfilePhase() {
return phase;
}
public boolean isEmpty() {
return statistics.isEmpty();
}
/**
* Builds a new {@link ImmutableSortedSet} of the path statistics for the given
* {@link ProfilerTask}.
*
* <p>{@link Stat}s are sorted by their natural order.
*/
public ImmutableSortedSet<Stat> getSortedStatistics(ProfilerTask taskType) {
return ImmutableSortedSet.copyOf(statistics.row(taskType).values());
}
public int getStatisticsCount(ProfilerTask taskType) {
return statistics.row(taskType).size();
}
@Override
public Iterator<ProfilerTask> iterator() {
return statistics.rowKeySet().iterator();
}
/**
* Add statistics from another PhaseVfsStatistics aggregation to this one.
*/
public void add(PhaseVfsStatistics other) {
for (Cell<ProfilerTask, String, Stat> cell : other.statistics.cellSet()) {
Stat stat = statistics.get(cell.getRowKey(), cell.getColumnKey());
if (stat == null) {
stat = new Stat(cell.getValue());
statistics.put(cell.getRowKey(), stat.path, stat);
} else {
stat.add(cell.getValue());
}
}
}
/**
* Add the VFS operations from the list of tasks to the {@link #statistics} table
*/
private void collectVfsEntries(String workSpaceName, List<Task> taskList) {
for (Task task : taskList) {
collectVfsEntries(workSpaceName, Arrays.asList(task.subtasks));
if (!task.type.name().startsWith("VFS_")) {
continue;
}
String path = pathMapping(workSpaceName, task.getDescription());
Stat stat = statistics.get(task.type, path);
if (stat == null) {
stat = new Stat(path);
statistics.put(task.type, path, stat);
}
stat.add(task.durationNanos);
}
}
private String pathMapping(String workSpaceName, String input) {
if (workSpaceName.isEmpty()) {
return input;
} else {
return input.substring(input.lastIndexOf("/" + workSpaceName) + 1);
}
}
}