blob: ae9baeff2f45a6cf913d67a10d6abc4fe1e7551c [file] [log] [blame]
/*
* Copyright (C) 2013 Google 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.google.caliper.worker;
import static com.google.common.base.Preconditions.checkState;
import static java.util.Arrays.asList;
import com.google.caliper.runner.Running;
import com.google.common.collect.ConcurrentHashMultiset;
import com.google.monitoring.runtime.instrumentation.Sampler;
import javax.inject.Inject;
/**
* An {@link AllocationRecorder} that records every allocation and its location.
*
* <p>This recorder is enabled via the {@code trackAllocations} worker option.
*/
final class AllAllocationsRecorder extends AllocationRecorder {
private final Class<?> benchmarkClass;
private final String benchmarkMethodName;
private volatile boolean recording = false;
private final ConcurrentHashMultiset<Allocation> allocations = ConcurrentHashMultiset.create();
private final Sampler sampler = new Sampler() {
@Override public void sampleAllocation(int arrayCount, String desc, Object newObj,
long size) {
if (recording) {
if (arrayCount != -1) {
desc = desc + "[" + arrayCount + "]";
}
// The first item is this line, the second is in AllocationRecorder and the
// one before that is the allocating line, so we start at index 2.
// We want to grab all lines until we get into the benchmark method.
StackTraceElement[] stackTrace = new Exception().getStackTrace();
int startIndex = 2;
int endIndex = 2;
for (int i = startIndex; i < stackTrace.length; i++) {
StackTraceElement element = stackTrace[i];
if (element.getClassName().startsWith(
AllAllocationsRecorder.class.getPackage().getName())) {
// Don't track locations up into the worker code, or originating within the worker
// code.
break;
}
endIndex = i;
if (element.getClassName().equals(benchmarkClass.getName())
&& element.getMethodName().equals(benchmarkMethodName)) {
// stop logging at the method under test
break;
}
}
allocations.add(
new Allocation(desc, size, asList(stackTrace).subList(startIndex, endIndex + 1)));
}
}
};
@Inject AllAllocationsRecorder(@Running.BenchmarkClass Class<?> benchmarkClass,
@Running.BenchmarkMethod String benchmarkMethodName) {
this.benchmarkClass = benchmarkClass;
this.benchmarkMethodName = benchmarkMethodName;
com.google.monitoring.runtime.instrumentation.AllocationRecorder.addSampler(sampler);
}
@Override protected void doStartRecording() {
checkState(!recording, "startRecording called, but we were already recording.");
allocations.clear();
recording = true;
}
@Override public AllocationStats stopRecording(int reps) {
checkState(recording, "stopRecording called, but we were not recording.");
recording = false;
return new AllocationStats(allocations, reps);
}
}