blob: 964cb376c2818572e9ea16896e1fde1c8238ae1a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.jmeter.visualizers;
import java.util.Map;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jorphan.math.StatCalculatorLong;
/**
* Aggregate sample data container. Just instantiate a new instance of this
* class, and then call {@link #addSample(SampleResult)} a few times, and pull
* the stats out with whatever methods you prefer.
*
*/
public class SamplingStatCalculator {
private final StatCalculatorLong calculator = new StatCalculatorLong();
private double maxThroughput;
private long firstTime;
private String label;
private volatile Sample currentSample;
public SamplingStatCalculator(){ // Only for use by test code
this("");
}
public SamplingStatCalculator(String label) {
this.label = label;
init();
}
private void init() { // WARNING: called from ctor so must not be overridden (i.e. must be private or final)
firstTime = Long.MAX_VALUE;
calculator.clear();
maxThroughput = Double.MIN_VALUE;
currentSample = new Sample();
}
/**
* Clear the counters (useful for differential stats)
*
*/
public synchronized void clear() {
init();
}
public Sample getCurrentSample() {
return currentSample;
}
/**
* Get the elapsed time for the samples
*
* @return how long the samples took
*/
public long getElapsed() {
if (getCurrentSample().getEndTime() == 0) {
return 0;// No samples collected ...
}
return getCurrentSample().getEndTime() - firstTime;
}
/**
* Returns the throughput associated to this sampler in requests per second.
* May be slightly skewed because it takes the timestamps of the first and
* last samples as the total time passed, and the test may actually have
* started before that start time and ended after that end time.
*
* @return throughput associated with this sampler per second
*/
public double getRate() {
if (calculator.getCount() == 0) {
return 0.0; // Better behaviour when howLong=0 or lastTime=0
}
return getCurrentSample().getThroughput();
}
/**
* Throughput in bytes / second
*
* @return throughput in bytes/second
*/
public double getBytesPerSecond() {
// Code duplicated from getPageSize()
double rate = 0;
if (this.getElapsed() > 0 && calculator.getTotalBytes() > 0) {
rate = calculator.getTotalBytes() / ((double) this.getElapsed() / 1000);
}
if (rate < 0) {
rate = 0;
}
return rate;
}
/**
* Throughput in kilobytes / second
*
* @return Throughput in kilobytes / second
*/
public double getKBPerSecond() {
return getBytesPerSecond() / 1024; // 1024=bytes per kb
}
/**
* calculates the average page size, which means divide the bytes by number
* of samples.
*
* @return average page size in bytes (0 if sample count is zero)
*/
public double getAvgPageBytes() {
long count = calculator.getCount();
if (count == 0) {
return 0;
}
return calculator.getTotalBytes() / (double) count;
}
/**
* @return the label of this component
*/
public String getLabel() {
return label;
}
/**
* Records a sample.
*
* @param res
* the sample to record
* @return newly created sample with current statistics
*
*/
public Sample addSample(SampleResult res) {
long rtime, cmean, cstdv, cmedian, cpercent, eCount, endTime;
double throughput;
boolean rbool;
synchronized (calculator) {
calculator.addValue(res.getTime(), res.getSampleCount());
calculator.addBytes(res.getBytes());
setStartTime(res);
eCount = getCurrentSample().getErrorCount();
eCount += res.getErrorCount();
endTime = getEndTime(res);
long howLongRunning = endTime - firstTime;
throughput = ((double) calculator.getCount() / (double) howLongRunning) * 1000.0;
if (throughput > maxThroughput) {
maxThroughput = throughput;
}
rtime = res.getTime();
cmean = (long)calculator.getMean();
cstdv = (long)calculator.getStandardDeviation();
cmedian = calculator.getMedian().longValue();
cpercent = calculator.getPercentPoint( 0.500 ).longValue();
// TODO cpercent is the same as cmedian here - why? and why pass it to "distributionLine"?
rbool = res.isSuccessful();
}
long count = calculator.getCount();
Sample s =
new Sample( null, rtime, cmean, cstdv, cmedian, cpercent, throughput, eCount, rbool, count, endTime );
currentSample = s;
return s;
}
private long getEndTime(SampleResult res) {
long endTime = res.getEndTime();
long lastTime = getCurrentSample().getEndTime();
if (lastTime < endTime) {
lastTime = endTime;
}
return lastTime;
}
/**
* @param res
*/
private void setStartTime(SampleResult res) {
long startTime = res.getStartTime();
if (firstTime > startTime) {
// this is our first sample, set the start time to current timestamp
firstTime = startTime;
}
}
/**
* Returns the raw double value of the percentage of samples with errors
* that were recorded. (Between 0.0 and 1.0)
*
* @return the raw double value of the percentage of samples with errors
* that were recorded.
*/
public double getErrorPercentage() {
double rval = 0.0;
if (calculator.getCount() == 0) {
return rval;
}
rval = (double) getCurrentSample().getErrorCount() / (double) calculator.getCount();
return rval;
}
/**
* For debugging purposes, only.
*/
@Override
public String toString() {
StringBuilder mySB = new StringBuilder();
mySB.append("Samples: " + this.getCount() + " ");
mySB.append("Avg: " + this.getMean() + " ");
mySB.append("Min: " + this.getMin() + " ");
mySB.append("Max: " + this.getMax() + " ");
mySB.append("Error Rate: " + this.getErrorPercentage() + " ");
mySB.append("Sample Rate: " + this.getRate());
return mySB.toString();
}
/**
* @return errorCount
*/
public long getErrorCount() {
return getCurrentSample().getErrorCount();
}
/**
* @return Returns the maxThroughput.
*/
public double getMaxThroughput() {
return maxThroughput;
}
public Map<Number, Number[]> getDistribution() {
return calculator.getDistribution();
}
public Number getPercentPoint(double percent) {
return calculator.getPercentPoint(percent);
}
public long getCount() {
return calculator.getCount();
}
public Number getMax() {
return calculator.getMax();
}
public double getMean() {
return calculator.getMean();
}
public Number getMeanAsNumber() {
return Long.valueOf((long) calculator.getMean());
}
public Number getMedian() {
return calculator.getMedian();
}
public Number getMin() {
if (calculator.getMin().longValue() < 0) {
return Long.valueOf(0);
}
return calculator.getMin();
}
public Number getPercentPoint(float percent) {
return calculator.getPercentPoint(percent);
}
public double getStandardDeviation() {
return calculator.getStandardDeviation();
}
}