/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.btrace.instr;
import com.sun.btrace.util.MethodID;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicLong;
/**
* Provides a centralized place to track the fundamental metrics for
* method execution.
* It is mostly called from the injected code to support sampling and timing.
* @author Jaroslav Bachorik
*/
final public class MethodTracker {
private static final RandomIntProvider rndIntProvider = RandomIntProvider.getInstance();
private static AtomicLong[] counters = new AtomicLong[50];
private static ThreadLocal<Long>[] tsArray = new ThreadLocal[50];
private static Object[] rLocks = new Object[50];
private static int[] means = new int[50];
private static int[] origMeans = new int[50];
private static int[] samplers = new int[50];
/**
* Creates a supporting structures for a new method id
* @param methodId The method id - generated by the {@linkplain MethodID} class
* @param mean The sampler mean or 0 if not applicable
*/
public static synchronized void registerCounter(int methodId, int mean) {
if (counters.length <= methodId) {
int newLen = methodId * 2;
counters = Arrays.copyOf(counters, newLen);
rLocks = Arrays.copyOf(rLocks, newLen);
means = Arrays.copyOf(means, newLen);
origMeans = Arrays.copyOf(means, newLen);
samplers = Arrays.copyOf(samplers, newLen);
tsArray = Arrays.copyOf(tsArray, newLen);
}
if (counters[methodId] == null) {
counters[methodId] = new AtomicLong(0);
rLocks[methodId] = new Object();
means[methodId] = mean * 2;
origMeans[methodId] = mean;
tsArray[methodId] = new ThreadLocal<Long>() {
@Override
protected Long initialValue() {
return 0L;
}
};
samplers[methodId] = 0;
}
}
/**
* Records the invocation of a certain method and indicates whether
* it should be traced or not (sampling).
* This method will be called when using the average sampling mode.
*
* @param methodId The method id - generated by the {@linkplain MethodID} class
* @return {@code true} if the invocation should be traced
*/
public static boolean hit(int methodId) {
int mean = means[methodId];
if (mean == 0) {
return true;
}
AtomicLong l = counters[methodId];
if (l.getAndDecrement() <= 0) {
int inc = rndIntProvider.nextInt(mean) + 1;
l.addAndGet(inc);
return true;
}
return false;
}
/**
* Records the invocation of a certain method alongside the timestamp
* and indicates whether it should be traced or not (sampling).
* This method will be called when using the average sampling mode.
*
* @param methodId The method id - generated by the {@linkplain MethodID} class
* @return a positive number (invocation time stamp) if the invocation should be traced
*/
public static long hitTimed(int methodId) {
int mean = means[methodId];
if (mean == 0) {
long ts = System.nanoTime();
tsArray[methodId].set(ts);
return ts;
}
AtomicLong l = counters[methodId];
if (l.getAndDecrement() <= 0) {
long ts = System.nanoTime();
int inc = rndIntProvider.nextInt(mean) + 1;
l.addAndGet(inc);
tsArray[methodId].set(ts);
return ts;
}
return 0L;
}
/**
* Records the invocation of a certain method and indicates whether
* it should be traced or not (sampling).
* This method will be called when using the adaptive sampling mode.
* @param methodId The method id - generated by the {@linkplain MethodID} class
* @return {@code true} if the invocation should be traced
*/
public static boolean hitAdaptive(int methodId) {
AtomicLong cntr = counters[methodId];
int origMean = origMeans[methodId];
int mean = means[methodId];
if (cntr.getAndDecrement() <= 0) {
long ts = System.nanoTime();
ThreadLocal<Long> tsRef = (ThreadLocal<Long>)tsArray[methodId];
long ts1 = tsRef.get();
if (ts1 != 0) {
long diff = ts - ts1;
if (mean < 1500 && diff < origMean) {
synchronized(rLocks[methodId]) {
means[methodId] = ++mean;
}
} else if (mean > 1 && diff > origMean) {
synchronized(rLocks[methodId]) {
means[methodId] = --mean;
}
}
}
tsRef.set(ts);
int inc = rndIntProvider.nextInt(mean) + 1;
cntr.addAndGet(inc);
return true;
}
return false;
}
/**
* Records the invocation of a certain method alongside the timestamp
* and indicates whether it should be traced or not (sampling).
* This method will be called when using the adaptive sampling mode.
*
* @param methodId The method id - generated by the {@linkplain MethodID} class
* @return a positive number (invocation time stamp) if the invocation should be traced
*/
public static long hitTimedAdaptive(int methodId) {
AtomicLong cntr = counters[methodId];
int mean = means[methodId];
int origMean = origMeans[methodId];
if (cntr.getAndDecrement() <= 0) {
long ts = System.nanoTime();
ThreadLocal<Long> tsRef = (ThreadLocal<Long>)tsArray[methodId];
long ts1 = tsRef.get();
if (ts1 != 0) {
long diff = ts - ts1;
if (mean < 1500 && diff < origMean) {
synchronized(rLocks[methodId]) {
means[methodId] = ++mean;
}
} else if (mean > 1 && diff > origMean) {
synchronized(rLocks[methodId]) {
means[methodId] = --mean;
}
}
}
tsRef.set(ts);
int inc = rndIntProvider.nextInt(mean) + 1;
cntr.addAndGet(inc);
return ts;
}
return 0L;
}
/**
* Used when timing the method execution or in adaptive sampling.
* To be used at the end of the sampled block.
* @param methodId The method id generated by {@linkplain MethodID} class
* @return The time stamp
*/
public static long getEndTs(int methodId) {
long ts = System.nanoTime();
tsArray[methodId].set(ts);
return ts;
}
/**
* Used in adaptive sampling.
* To be used at the end of the sampled block.
* @param methodId The method id generated by {@linkplain MethodID} class
*/
public static void updateEndTs(int methodId) {
tsArray[methodId].set(System.nanoTime());
}
}