package rocks.inspectit.shared.all.communication.data;
import java.sql.Timestamp;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.Index;
import javax.persistence.Table;
import org.codehaus.jackson.annotate.JsonAutoDetect;
import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility;
import org.codehaus.jackson.annotate.JsonIgnore;
import org.codehaus.jackson.annotate.JsonTypeInfo;
import rocks.inspectit.shared.all.cmr.cache.IObjectSizes;
import rocks.inspectit.shared.all.communication.DefaultData;
/**
* The timer data class stores information about the execution time of a java method.
*
* Notes
* <ul>
* <li>This class is used for multiple purposes over time. Be aware that although this class
* provides exclusive timings these are only available if the timer is included in an invocation
* sequence and is calculated on the CMR NOT the agent.</li>
* <li>In order to check if certain information is available use the checking methods
* <code>isCpuMetricAvailable</code> and <code>isExclusiveMetricAvailable</code></li>
* <li>To change a minimum of maximum (normal time, exclusive, cpu) use the calculate methods as
* these deal with the internals in a correct way (we must initialize some fields seemingly in a
* strange way but we want to improve performance and size)</li>
* </ul>
*
* @author Patrice Bouillet
* @author Stefan Siegl
*
*/
@Entity
@Table(indexes = { @Index(name = "time_stamp_idx", columnList = "timeStamp") })
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
@JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE, isGetterVisibility = Visibility.NONE)
public class TimerData extends InvocationAwareData {
/**
* Generated serial UID.
*/
private static final long serialVersionUID = 8992128958802371539L;
/**
* The minimum value.
*/
private double min = -1;
/**
* The maximum value.
*/
private double max = -1;
/**
* The count.
*/
private long count = 0;
/**
* The complete duration.
*/
private double duration = 0;
/**
* The variance (optional parameter).
*/
@JsonIgnore
private double variance;
/**
* The cpu minimum value.
*/
private double cpuMin = -1;
/**
* The cpu maximum value.
*/
private double cpuMax = -1;
/**
* The cpu complete duration.
*/
private double cpuDuration = 0;
/**
* Exclusive count. Needed because this count can be less than the total count.
*/
private long exclusiveCount = 0;
/**
* Exclusive duration.
*/
private double exclusiveDuration;
/**
* Exclusive max duration.
*/
private double exclusiveMax = -1;
/**
* Exclusive min duration.
*/
private double exclusiveMin = -1;
/**
* Defines if the data should be saved to database and available for charting.
*/
@JsonIgnore
private boolean charting;
/**
* Default no-args constructor.
*/
public TimerData() {
}
/**
* Creates a new instance of the <code>Timerdata</code>.
*
* @param timeStamp
* the timestamp.
* @param platformIdent
* the platform identifier.
* @param sensorTypeIdent
* the sensor type identifier.
* @param methodIdent
* the method identifier.
*/
public TimerData(Timestamp timeStamp, long platformIdent, long sensorTypeIdent, long methodIdent) {
super(timeStamp, platformIdent, sensorTypeIdent, methodIdent);
}
/**
* Creates a new instance of the <code>Timerdata</code>.
*
* @param timeStamp
* the timestamp.
* @param platformIdent
* the platform identifier.
* @param sensorTypeIdent
* the sensor type identifier.
* @param methodIdent
* the method identifier.
* @param parameterContentData
* The information of captured parameters.
*/
public TimerData(Timestamp timeStamp, long platformIdent, long sensorTypeIdent, long methodIdent, List<ParameterContentData> parameterContentData) {
super(timeStamp, platformIdent, sensorTypeIdent, methodIdent, parameterContentData);
}
/**
* <b> CAREFUL! min is initialized to -1 due to data transfer sizes! </b>
*
* @return the min time.
*/
public double getMin() {
return min;
}
/**
* Sets the minimum and deals with the -1 initialization!.
*
* @param min
* the minimum value to be set to if it is smaller than the minimum.
*/
public void calculateMin(double min) {
if (this.min == -1) {
this.min = min;
} else {
this.min = Math.min(this.min, min);
}
}
/**
* Gets {@link #max}.
*
* @return {@link #max}
*/
public double getMax() {
return max;
}
/**
* Sets the maximum if the given value is bigger than the current value.
*
* @param max
* the maximum to be set
*/
public void calculateMax(double max) {
this.max = Math.max(this.max, max);
}
/**
* Gets {@link #count}.
*
* @return {@link #count}
*/
public long getCount() {
return count;
}
/**
* Sets {@link #count}.
*
* @param count
* New value for {@link #count}
*/
public void setCount(long count) {
this.count = count;
}
/**
* increases the count by 1.
*/
public void increaseCount() {
this.count++;
}
/**
* Gets {@link #duration}.
*
* @return {@link #duration}
*/
public double getDuration() {
return duration;
}
/**
* Sets {@link #duration}.
*
* @param duration
* New value for {@link #duration}
*/
public void setDuration(double duration) {
this.duration = duration;
}
/**
* adds the given time to the duration.
*
* @param duration
* the duration to add.
*/
public void addDuration(double duration) {
this.duration += duration;
}
/**
* Returns average time.
*
* @return Returns average time.
*/
public double getAverage() {
return duration / count;
}
/**
* Gets {@link #variance}.
*
* @return {@link #variance}
*/
public double getVariance() {
return variance;
}
/**
* Sets {@link #variance}.
*
* @param variance
* New value for {@link #variance}
*/
public void setVariance(double variance) {
this.variance = variance;
}
/**
* Checks if this data object contains captured parameters.
*
* @return if this data object contains captured parameters.
*/
public boolean providesCapturedParameters() {
return (null != getParameterContentData()) && !getParameterContentData().isEmpty();
}
/**
* Sets the minimum and deals with the -1 initialization!.
*
* @param min
* the minimum value to be set to if it is smaller than the minimum.
*/
public void calculateCpuMin(double min) {
if (cpuMin == -1) {
cpuMin = min;
} else {
cpuMin = Math.min(cpuMin, min);
}
}
/**
* Sets the maximum if the given value is bigger than the current value.
*
* @param time
* the maximum to be set
*/
public void calculateCpuMax(double time) {
cpuMax = Math.max(cpuMax, time);
}
/**
* <b> Notice: ensure to check using the <code> isCpuMetricDataAvailable() </code> if cpu metric
* data is in fact available, otherwise you might get strange results. </b>
*
* @return the cpuMin
*/
public double getCpuMin() {
return cpuMin;
}
/**
* <b> Notice: ensure to check using the <code> isCpuMetricDataAvailable() </code> if cpu metric
* data is in fact available, otherwise you might get strange results. </b>
*
* @return the cpuMax
*/
public double getCpuMax() {
return cpuMax;
}
/**
* <b> Notice: ensure to check using the <code> isCpuMetricDataAvailable() </code> if cpu metric
* data is in fact available, otherwise you might get strange results. </b>
*
* @return the cpuDuration
*/
public double getCpuDuration() {
return cpuDuration;
}
/**
* @param cpuDuration
* the cpuDuration to set
*/
public void setCpuDuration(double cpuDuration) {
this.cpuDuration = cpuDuration;
}
/**
* @param cpuDuration
* the cpuDuration to add
*/
public void addCpuDuration(double cpuDuration) {
this.cpuDuration += cpuDuration;
}
/**
* <b> Notice: ensure to check using the <code> isCpuMetricDataAvailable() </code> if cpu metric
* data is in fact available, otherwise you might get strange results. </b>
*
* @return the cpuAverage
*/
public double getCpuAverage() {
return cpuDuration / count;
}
/**
* <b> Notice: ensure to check using the <code> isExclusiveMetricDataAvailable() </code> if cpu
* metric data is in fact available, otherwise you might get strange results. </b>
*
* @return exclusive count
*/
public long getExclusiveCount() {
return exclusiveCount;
}
/**
* Sets {@link #exclusiveCount}.
*
* @param exclusiveCount
* New value for {@link #exclusiveCount}
*/
public void setExclusiveCount(long exclusiveCount) {
this.exclusiveCount = exclusiveCount;
}
/**
* increases the exclusive count by 1.
*/
public void increaseExclusiveCount() {
this.exclusiveCount++;
}
/**
* <b> Notice: ensure to check using the <code> isExclusiveMetricDataAvailable() </code> if cpu
* metric data is in fact available, otherwise you might get strange results. </b>
*
* @return duration
*/
public double getExclusiveDuration() {
return exclusiveDuration;
}
/**
* Sets {@link #exclusiveDuration}.
*
* @param exclusiveDuration
* New value for {@link #exclusiveDuration}
*/
public void setExclusiveDuration(double exclusiveDuration) {
this.exclusiveDuration = exclusiveDuration;
}
/**
* adds the given time to the exclusive duration.
*
* @param exclusiveDuration
* the duration to add.
*/
public void addExclusiveDuration(double exclusiveDuration) {
this.exclusiveDuration += exclusiveDuration;
}
/**
* <b> Notice: ensure to check using the <code> isExclusiveMetricDataAvailable() </code> if cpu
* metric data is in fact available, otherwise you might get strange results. </b>
*
* @return exlusive max
*/
public double getExclusiveMax() {
return exclusiveMax;
}
/**
* Sets the maximum if the given value is bigger than the current value.
*
* @param max
* the maximum to be set
*/
public void calculateExclusiveMax(double max) {
exclusiveMax = Math.max(exclusiveMax, max);
}
/**
* <b> Notice: ensure to check using the <code> isExclusiveMetricDataAvailable() </code> if cpu
* metric data is in fact available, otherwise you might get strange results. </b>
*
* @return the exclusive minimum time.
*/
public double getExclusiveMin() {
return exclusiveMin;
}
/**
* Sets the minimum and deals with the -1 initialization!.
*
* @param min
* the minimum value to be set to if it is smaller than the minimum.
*/
public void calculateExclusiveMin(double min) {
if (exclusiveMin == -1) {
exclusiveMin = min;
} else {
exclusiveMin = Math.min(exclusiveMin, min);
}
}
/**
* Returns the average exclusive time calculated as exclusive duration % count.
*
* @return Average exclusive time.
*/
public double getExclusiveAverage() {
return exclusiveDuration / exclusiveCount;
}
// Private setters for hibernate. Users should use the calculate methods.
@SuppressWarnings("unused")
private void setMin(double min) {
this.min = min;
}
@SuppressWarnings("unused")
private void setMax(double max) {
this.max = max;
}
@SuppressWarnings("unused")
private void setCpuMin(double cpuMin) {
this.cpuMin = cpuMin;
}
@SuppressWarnings("unused")
private void setCpuMax(double cpuMax) {
this.cpuMax = cpuMax;
}
@SuppressWarnings("unused")
private void setExclusiveMax(double exclusiveMax) {
this.exclusiveMax = exclusiveMax;
}
@SuppressWarnings("unused")
private void setExclusiveMin(double exclusiveMin) {
this.exclusiveMin = exclusiveMin;
}
/**
* Gets {@link #charting}.
*
* @return {@link #charting}
*/
public boolean isCharting() {
return charting;
}
/**
* Sets {@link #charting}.
*
* @param charting
* New value for {@link #charting}
*/
public void setCharting(boolean charting) {
this.charting = charting;
}
/**
* {@inheritDoc}
*/
@Override
public DefaultData finalizeData() {
// no need
return this;
}
/**
* {@inheritDoc}
*/
@Override
public long getObjectSize(IObjectSizes objectSizes, boolean doAlign) {
long size = super.getObjectSize(objectSizes, doAlign);
size += objectSizes.getPrimitiveTypesSize(0, 1, 0, 0, 2, 10);
if (doAlign) {
return objectSizes.alignTo8Bytes(size);
} else {
return size;
}
}
/**
* {@inheritDoc}
*/
@Override
public double getInvocationAffiliationPercentage() {
return (double) getObjectsInInvocationsCount() / count;
}
/**
* Whether or not this timer data contains cpu related metrics.
*
* @return Whether or not this timer data contains cpu related metrics.
*/
public boolean isCpuMetricDataAvailable() {
// cpu duration cannot be used as comparison, because:
// in the timer hook cpu time is calculated using an JVM JMX bean that only has a resolution
// of 10ms (or more depending on the system). Thus even though CPU times could be
// calculated, the time might be 0. So in this case 0 will be added to the duration. But
// still, yes, we do have cpu metric data available, but it is 0.
return cpuMin != -1;
}
/**
* Whether or not this timer data contains exclusive time metrics.
*
* @return Whether or not this timer data contains exclusive time metrics.
*/
public boolean isExclusiveTimeDataAvailable() {
return exclusiveMin != -1;
}
/**
* Whether or not this timer data contains time metrics.
*
* @return Whether or not this timer data contains time metrics.
*/
public boolean isTimeDataAvailable() {
return min != -1;
}
/**
* Aggregates the values given in the supplied timer data parameter to the objects data.
*
* @param timerData
* Data to be aggregated into current object.
*/
public void aggregateTimerData(TimerData timerData) {
super.aggregateInvocationAwareData(timerData);
this.setCount(this.getCount() + timerData.getCount());
this.setDuration(this.getDuration() + timerData.getDuration());
this.calculateMax(timerData.getMax());
this.calculateMin(timerData.getMin());
if (timerData.isCpuMetricDataAvailable()) {
this.setCpuDuration(this.getCpuDuration() + timerData.getCpuDuration());
this.calculateCpuMax(timerData.getCpuMax());
this.calculateCpuMin(timerData.getCpuMin());
}
if (timerData.isExclusiveTimeDataAvailable()) {
this.addExclusiveDuration(timerData.getExclusiveDuration());
this.setExclusiveCount(this.getExclusiveCount() + timerData.getExclusiveCount());
this.calculateExclusiveMax(timerData.getExclusiveMax());
this.calculateExclusiveMin(timerData.getExclusiveMin());
}
this.charting = this.charting | timerData.isCharting();
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = (prime * result) + (int) (count ^ (count >>> 32));
long temp;
temp = Double.doubleToLongBits(cpuDuration);
result = (prime * result) + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(cpuMax);
result = (prime * result) + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(cpuMin);
result = (prime * result) + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(duration);
result = (prime * result) + (int) (temp ^ (temp >>> 32));
result = (prime * result) + (int) (exclusiveCount ^ (exclusiveCount >>> 32));
temp = Double.doubleToLongBits(exclusiveDuration);
result = (prime * result) + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(exclusiveMax);
result = (prime * result) + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(exclusiveMin);
result = (prime * result) + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(max);
result = (prime * result) + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(min);
result = (prime * result) + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(variance);
result = (prime * result) + (int) (temp ^ (temp >>> 32));
result = (prime * result) + (charting ? 1231 : 1237);
return result;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj)) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
TimerData other = (TimerData) obj;
if (count != other.count) {
return false;
}
if (Double.doubleToLongBits(cpuDuration) != Double.doubleToLongBits(other.cpuDuration)) {
return false;
}
if (Double.doubleToLongBits(cpuMax) != Double.doubleToLongBits(other.cpuMax)) {
return false;
}
if (Double.doubleToLongBits(cpuMin) != Double.doubleToLongBits(other.cpuMin)) {
return false;
}
if (Double.doubleToLongBits(duration) != Double.doubleToLongBits(other.duration)) {
return false;
}
if (exclusiveCount != other.exclusiveCount) {
return false;
}
if (Double.doubleToLongBits(exclusiveDuration) != Double.doubleToLongBits(other.exclusiveDuration)) {
return false;
}
if (Double.doubleToLongBits(exclusiveMax) != Double.doubleToLongBits(other.exclusiveMax)) {
return false;
}
if (Double.doubleToLongBits(exclusiveMin) != Double.doubleToLongBits(other.exclusiveMin)) {
return false;
}
if (Double.doubleToLongBits(max) != Double.doubleToLongBits(other.max)) {
return false;
}
if (Double.doubleToLongBits(min) != Double.doubleToLongBits(other.min)) {
return false;
}
if (Double.doubleToLongBits(variance) != Double.doubleToLongBits(other.variance)) {
return false;
}
if (charting != other.charting) {
return false;
}
return true;
}
}