001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.jmeter.visualizers;
020:
021: import java.io.Serializable;
022: import java.util.Collections;
023: import java.util.HashMap;
024: import java.util.List;
025: import java.util.Vector;
026:
027: import org.apache.jmeter.samplers.SampleResult;
028: import org.apache.jorphan.logging.LoggingManager;
029: import org.apache.jorphan.math.StatCalculator;
030: import org.apache.log.Logger;
031:
032: /**
033: * Aggegate sample data container. Just instantiate a new instance of this
034: * class, and then call {@link #addSample(SampleResult)} a few times, and pull
035: * the stats out with whatever methods you prefer.
036: *
037: */
038: public class SamplingStatCalculator implements Serializable {
039: private static final Logger log = LoggingManager
040: .getLoggerForClass();
041:
042: private final StatCalculator calculator = new StatCalculator();
043:
044: private final List storedValues = new Vector();
045:
046: private double maxThroughput;
047:
048: private long firstTime;
049:
050: private String label;
051:
052: public SamplingStatCalculator() {// Don't (can't) use this...
053: log.warn("Constructor only intended for use in testing"); // $NON-NLS-1$
054: }
055:
056: /**
057: * Use this constructor.
058: */
059: public SamplingStatCalculator(String label) {
060: this .label = label;
061: init();
062: }
063:
064: /**
065: * Essentially a copy function
066: *
067: * @param stat
068: */
069: public SamplingStatCalculator(SamplingStatCalculator stat) {
070: this (stat.label);
071: addSamples(stat);
072: }
073:
074: private void init() {
075: firstTime = Long.MAX_VALUE;
076: calculator.clear();
077: storedValues.clear();
078: maxThroughput = Double.MIN_VALUE;
079: }
080:
081: public void addSamples(SamplingStatCalculator ssc) {
082: calculator.addAll(ssc.calculator);
083: synchronized (storedValues) {
084: storedValues.addAll(ssc.storedValues);
085: Collections.sort(storedValues);
086: }
087: if (firstTime > ssc.firstTime) {
088: firstTime = ssc.firstTime;
089: }
090: }
091:
092: /**
093: * Clear the counters (useful for differential stats)
094: *
095: */
096: public synchronized void clear() {
097: init();
098: }
099:
100: public Sample getCurrentSample() {
101: synchronized (storedValues) {
102: if (storedValues.size() == 0) {
103: return new Sample();
104: }
105: return (Sample) storedValues.get(storedValues.size() - 1);
106: }
107: }
108:
109: /**
110: * Get the elapsed time for the samples
111: *
112: * @return how long the samples took
113: */
114: public long getElapsed() {
115: if (getCurrentSample().getEndTime() == 0)
116: return 0;// No samples collected ...
117: return getCurrentSample().getEndTime() - firstTime;
118: }
119:
120: /**
121: * Returns the throughput associated to this sampler in requests per second.
122: * May be slightly skewed because it takes the timestamps of the first and
123: * last samples as the total time passed, and the test may actually have
124: * started before that start time and ended after that end time.
125: */
126: public double getRate() {
127: if (calculator.getCount() == 0)
128: return 0.0; // Better behaviour when howLong=0 or lastTime=0
129:
130: return getCurrentSample().getThroughput();
131: }
132:
133: /**
134: * Should calculate the average page size, which means divide the bytes by number
135: * of samples - actually calculates the throughput in bytes / second
136: *
137: * @deprecated use getBytesPerSecond() instead
138: * @see #getAvgPageBytes() for the average page size
139: */
140: public double getPageSize() {
141: double rate = 0;
142: if (this .getElapsed() > 0 && calculator.getTotalBytes() > 0) {
143: rate = calculator.getTotalBytes()
144: / ((double) this .getElapsed() / 1000);
145: }
146: if (rate < 0) {
147: rate = 0;
148: }
149: return rate;
150: }
151:
152: /**
153: * Throughput in bytes / second
154: *
155: * @return throughput in bytes/second
156: */
157: public double getBytesPerSecond() {
158: // Code duplicated from getPageSize()
159: double rate = 0;
160: if (this .getElapsed() > 0 && calculator.getTotalBytes() > 0) {
161: rate = calculator.getTotalBytes()
162: / ((double) this .getElapsed() / 1000);
163: }
164: if (rate < 0) {
165: rate = 0;
166: }
167: return rate;
168: }
169:
170: /**
171: * Throughput in kilobytes / second
172: *
173: * @return Throughput in kilobytes / second
174: */
175: public double getKBPerSecond() {
176: return getBytesPerSecond() / 1024; // 1024=bytes per kb
177: }
178:
179: /**
180: * calculates the average page size, which means divide the bytes by number
181: * of samples.
182: *
183: * @return average page size in bytes
184: */
185: public double getAvgPageBytes() {
186: double rate = 0;
187: if (this .getElapsed() > 0 && calculator.getTotalBytes() > 0) {
188: rate = calculator.getTotalBytes()
189: / ((double) this .getElapsed() / 1000);
190: }
191: if (rate < 0) {
192: rate = 0;
193: }
194: return rate;
195: }
196:
197: public String getLabel() {
198: return label;
199: }
200:
201: /**
202: * Records a sample.
203: *
204: */
205: public Sample addSample(SampleResult res) {
206: long rtime, cmean, cstdv, cmedian, cpercent, eCount, endTime;
207: double throughput;
208: boolean rbool;
209: synchronized (calculator) {
210: long byteslength = res.getBytes();
211: // if there was more than 1 loop in the sample, we
212: // handle it appropriately
213: if (res.getSampleCount() > 1) {
214: long time = res.getTime() / res.getSampleCount();
215: long resbytes = byteslength / res.getSampleCount();
216: for (int idx = 0; idx < res.getSampleCount(); idx++) {
217: calculator.addValue(time);
218: calculator.addBytes(resbytes);
219: }
220: } else {
221: calculator.addValue(res.getTime());
222: calculator.addBytes(byteslength);
223: }
224: setStartTime(res);
225: eCount = getCurrentSample().getErrorCount();
226: if (!res.isSuccessful()) {
227: eCount++;
228: }
229: endTime = getEndTime(res);
230: long howLongRunning = endTime - firstTime;
231: throughput = ((double) calculator.getCount() / (double) howLongRunning) * 1000.0;
232: if (throughput > maxThroughput) {
233: maxThroughput = throughput;
234: }
235:
236: rtime = res.getTime();
237: cmean = (long) calculator.getMean();
238: cstdv = (long) calculator.getStandardDeviation();
239: cmedian = calculator.getMedian().longValue();
240: cpercent = calculator.getPercentPoint(0.500).longValue();
241: // TODO cpercent is the same as cmedian here - why? and why pass it to "distributionLine"?
242: rbool = res.isSuccessful();
243: }
244:
245: synchronized (storedValues) {
246: int count = storedValues.size() + 1;
247: Sample s = new Sample(null, rtime, cmean, cstdv, cmedian,
248: cpercent, throughput, eCount, rbool, count, endTime);
249: storedValues.add(s);
250: return s;
251: }
252: }
253:
254: public List getSamples() {
255: return storedValues;
256: }
257:
258: public Sample getSample(int index) {
259: synchronized (storedValues) {
260: if (index < storedValues.size()) {
261: return (Sample) storedValues.get(index);
262: }
263: return null;
264: }
265: }
266:
267: private long getEndTime(SampleResult res) {
268: long endTime = res.getEndTime();
269: long lastTime = getCurrentSample().getEndTime();
270: if (lastTime < endTime) {
271: lastTime = endTime;
272: }
273: return lastTime;
274: }
275:
276: /**
277: * @param res
278: */
279: private void setStartTime(SampleResult res) {
280: long startTime = res.getStartTime();
281: if (firstTime > startTime) {
282: // this is our first sample, set the start time to current timestamp
283: firstTime = startTime;
284: }
285: }
286:
287: /**
288: * Returns the raw double value of the percentage of samples with errors
289: * that were recorded. (Between 0.0 and 1.0)
290: *
291: * @return the raw double value of the percentage of samples with errors
292: * that were recorded.
293: */
294: public double getErrorPercentage() {
295: double rval = 0.0;
296:
297: if (calculator.getCount() == 0) {
298: return (rval);
299: }
300: rval = (double) getCurrentSample().getErrorCount()
301: / (double) calculator.getCount();
302: return (rval);
303: }
304:
305: /**
306: * For debugging purposes, only.
307: */
308: public String toString() {
309: StringBuffer mySB = new StringBuffer();
310:
311: mySB.append("Samples: " + this .getCount() + " ");
312: mySB.append("Avg: " + this .getMean() + " ");
313: mySB.append("Min: " + this .getMin() + " ");
314: mySB.append("Max: " + this .getMax() + " ");
315: mySB.append("Error Rate: " + this .getErrorPercentage() + " ");
316: mySB.append("Sample Rate: " + this .getRate());
317: return (mySB.toString());
318: }
319:
320: /**
321: * @return errorCount
322: */
323: public long getErrorCount() {
324: return getCurrentSample().getErrorCount();
325: }
326:
327: /**
328: * @return Returns the maxThroughput.
329: */
330: public double getMaxThroughput() {
331: return maxThroughput;
332: }
333:
334: public HashMap getDistribution() {
335: return calculator.getDistribution();
336: }
337:
338: public Number getPercentPoint(double percent) {
339: return calculator.getPercentPoint(percent);
340: }
341:
342: public int getCount() {
343: return calculator.getCount();
344: }
345:
346: public Number getMax() {
347: return calculator.getMax();
348: }
349:
350: public double getMean() {
351: return calculator.getMean();
352: }
353:
354: public Number getMeanAsNumber() {
355: return new Long((long) calculator.getMean());
356: }
357:
358: public Number getMedian() {
359: return calculator.getMedian();
360: }
361:
362: public Number getMin() {
363: if (calculator.getMin().longValue() < 0) {
364: return new Long(0);
365: } else {
366: return calculator.getMin();
367: }
368: }
369:
370: public Number getPercentPoint(float percent) {
371: return calculator.getPercentPoint(percent);
372: }
373:
374: public double getStandardDeviation() {
375: return calculator.getStandardDeviation();
376: }
377: }
|