001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: * The Original Software is NetBeans. The Initial Developer of the Original
026: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
027: * Microsystems, Inc. All Rights Reserved.
028: *
029: * If you wish your version of this file to be governed by only the CDDL
030: * or only the GPL Version 2, indicate your decision by adding
031: * "[Contributor] elects to include this software in this distribution
032: * under the [CDDL or GPL Version 2] license." If you do not indicate a
033: * single choice of license, a recipient has the option to distribute
034: * your version of this file under either the CDDL, the GPL Version 2 or
035: * to extend the choice of license to its licensees as provided above.
036: * However, if you add GPL Version 2 code and therefore, elected the GPL
037: * Version 2 license, then the option applies only if the new code is
038: * made subject to such option by the copyright holder.
039: */
040:
041: package org.netbeans.lib.profiler.results.cpu;
042:
043: import org.netbeans.lib.profiler.global.CommonConstants;
044: import org.netbeans.lib.profiler.global.Platform;
045: import org.netbeans.lib.profiler.global.ProfilingSessionStatus;
046: import java.lang.ref.WeakReference;
047:
048: /**
049: * A utility class to perform time measurements adjustments
050: * @author Jaroslav Bachorik
051: */
052: public class TimingAdjusterOld {
053: //~ Static fields/initializers -----------------------------------------------------------------------------------------------
054:
055: private static TimingAdjusterOld instance;
056: private static WeakReference lastStatusRef;
057:
058: //~ Instance fields ----------------------------------------------------------------------------------------------------------
059:
060: private InstrTimingData timingData;
061:
062: //~ Constructors -------------------------------------------------------------------------------------------------------------
063:
064: private TimingAdjusterOld(InstrTimingData timing) {
065: timingData = timing;
066: }
067:
068: //~ Methods ------------------------------------------------------------------------------------------------------------------
069:
070: public static synchronized TimingAdjusterOld getDefault() {
071: if (instance == null) {
072: instance = new TimingAdjusterOld(null);
073: }
074:
075: return instance;
076: }
077:
078: public static synchronized TimingAdjusterOld getDefault(
079: final ProfilingSessionStatus status) {
080: if (instance != null) {
081: ProfilingSessionStatus lastStatus = (lastStatusRef != null) ? (ProfilingSessionStatus) lastStatusRef
082: .get()
083: : null;
084:
085: if (((lastStatus != null) && !lastStatus.equals(status))
086: || (lastStatus == null)) {
087: lastStatusRef = new WeakReference(status);
088:
089: switch (status.currentInstrType) {
090: case CommonConstants.INSTR_RECURSIVE_FULL: {
091: instance = new TimingAdjusterOld(
092: getFullInstrTimingData(status));
093:
094: break;
095: }
096: case CommonConstants.INSTR_RECURSIVE_SAMPLED: {
097: instance = new TimingAdjusterOld(
098: getSampledInstrTimingData(status));
099:
100: break;
101: }
102: }
103: }
104: } else {
105: lastStatusRef = new WeakReference(status);
106:
107: switch (status.currentInstrType) {
108: case CommonConstants.INSTR_RECURSIVE_FULL: {
109: instance = new TimingAdjusterOld(
110: getFullInstrTimingData(status));
111:
112: break;
113: }
114: case CommonConstants.INSTR_RECURSIVE_SAMPLED: {
115: instance = new TimingAdjusterOld(
116: getSampledInstrTimingData(status));
117:
118: break;
119: }
120: }
121: }
122:
123: return instance;
124: }
125:
126: public final double adjustTime(long time, int incommingInv,
127: int outgoingInv, boolean secondTimestamp) {
128: if (timingData == null) {
129: return (double) time;
130: }
131:
132: if (secondTimestamp) {
133: return (((double) time
134: - (incommingInv * timingData.methodEntryExitInnerTime1) - (outgoingInv * timingData.methodEntryExitOuterTime1)) * 1000000)
135: / timingData.timerCountsInSecond1;
136: } else {
137: return (((double) time
138: - (incommingInv * timingData.methodEntryExitInnerTime0) - (outgoingInv * timingData.methodEntryExitOuterTime0)) * 1000000)
139: / timingData.timerCountsInSecond0;
140: }
141: }
142:
143: public final double delta(int incommingInv, int outgoingInv,
144: boolean secondTimestamp) {
145: if (timingData == null) {
146: return 0d;
147: }
148:
149: double adjusted = 0;
150:
151: if (secondTimestamp) {
152: adjusted = ((((double) incommingInv * timingData.methodEntryExitInnerTime1) + (outgoingInv * timingData.methodEntryExitOuterTime1)) * 1000000)
153: / timingData.timerCountsInSecond1;
154: } else {
155: adjusted = ((((double) incommingInv * timingData.methodEntryExitInnerTime0) + (outgoingInv * timingData.methodEntryExitOuterTime0)) * 1000000)
156: / timingData.timerCountsInSecond0;
157: }
158:
159: if (adjusted < 0) {
160: adjusted = 0;
161: }
162:
163: return adjusted;
164: }
165:
166: private static InstrTimingData getFullInstrTimingData(
167: ProfilingSessionStatus status) {
168: InstrTimingData timingData = new InstrTimingData();
169:
170: // We use the following "safety margins" to artificially decrease the time spent in instrumentation.
171: // That's because it looks like due to various CPU level speed optimizations the execution time for some code with
172: // added instrumentation may be pretty much equal to the execution time of the instrumentation itself. The profiled
173: // code, esp. if it's just one or two lines, is effectively executed in parallel with instrumentation.
174: // Furthermore, highly optimizing CPU plus possibly smth. like rounding errors may occasionally result in the
175: // measured time that is even *smaller* than the execution time of the instrumentation. This will result in
176: // *negative* cleansed times presented to the user.
177: // This safety margin is rough, but probably most reliable way to deal with these issues. The value has been
178: // chosen experimentally, and seems to result in reasonable compromise between data accuracy and no occurrences
179: // of zero time results.
180: double safetyMargin0 = 0.88;
181: double safetyMargin1 = 0.88;
182: double safetyMargin2 = 0.88;
183:
184: if (status.collectingTwoTimeStamps()) {
185: // Note that all methodEntryExitXXX times are measured in status in absolute timer counts.
186: // For usage in factor-out operations, we need to convert those that are used with thread CPU time values
187: // into thread CPU time units.
188: timingData.methodEntryExitCallTime0 = status.methodEntryExitCallTime[2]
189: * safetyMargin2;
190: timingData.methodEntryExitCallTime1 = (status.methodEntryExitCallTime[3] * status.timerCountsInSecond[1])
191: / status.timerCountsInSecond[0] * safetyMargin2;
192: timingData.methodEntryExitInnerTime0 = status.methodEntryExitInnerTime[2]
193: * safetyMargin2;
194: timingData.methodEntryExitOuterTime0 = status.methodEntryExitOuterTime[2]
195: * safetyMargin2;
196: timingData.methodEntryExitInnerTime1 = (status.methodEntryExitInnerTime[3] * status.timerCountsInSecond[1])
197: / status.timerCountsInSecond[0] * safetyMargin2;
198: timingData.methodEntryExitOuterTime1 = (status.methodEntryExitOuterTime[3] * status.timerCountsInSecond[1])
199: / status.timerCountsInSecond[0] * safetyMargin2;
200: timingData.timerCountsInSecond0 = status.timerCountsInSecond[0];
201: timingData.timerCountsInSecond1 = status.timerCountsInSecond[1];
202: } else {
203: if (status.absoluteTimerOn) {
204: timingData.methodEntryExitCallTime0 = status.methodEntryExitCallTime[0]
205: * safetyMargin0;
206: timingData.methodEntryExitInnerTime0 = status.methodEntryExitInnerTime[0]
207: * safetyMargin0;
208: timingData.methodEntryExitOuterTime0 = status.methodEntryExitOuterTime[0]
209: * safetyMargin0;
210: timingData.timerCountsInSecond0 = status.timerCountsInSecond[0];
211: } else {
212: // Thread CPU only timer used. On Windows and Linux, with extremely low (10 ms or even worse) resolution of
213: // this timer, it doesn't make sense to use cleansing - it introduces negative figures for short-running
214: // methods, for which I didn't find any graceful way to deal with.
215: // Also, cleansing is needed to make results sensible for short-running methods - but here for such methods
216: // they are already non-sensible, with 0 as a most typical result.
217: String targetMachineOSName = status.targetMachineOSName;
218:
219: if (Platform.isWindows(targetMachineOSName)
220: || Platform.isLinux(targetMachineOSName)) {
221: safetyMargin1 = 0;
222: }
223:
224: timingData.methodEntryExitCallTime0 = (status.methodEntryExitCallTime[1] * status.timerCountsInSecond[1])
225: / status.timerCountsInSecond[0] * safetyMargin1;
226: timingData.methodEntryExitInnerTime0 = (status.methodEntryExitInnerTime[1] * status.timerCountsInSecond[1])
227: / status.timerCountsInSecond[0] * safetyMargin1;
228: timingData.methodEntryExitOuterTime0 = (status.methodEntryExitOuterTime[1] * status.timerCountsInSecond[1])
229: / status.timerCountsInSecond[0] * safetyMargin1;
230: timingData.timerCountsInSecond0 = status.timerCountsInSecond[1];
231: }
232: }
233:
234: return timingData;
235: }
236:
237: private static InstrTimingData getSampledInstrTimingData(
238: ProfilingSessionStatus status) {
239: InstrTimingData timingData = new InstrTimingData();
240:
241: double entryExitTimeInAbsCounts = status.methodEntryExitCallTime[4];
242: double entryExitTimeInThreadCPUCounts = entryExitTimeInAbsCounts
243: / status.timerCountsInSecond[0]
244: * status.timerCountsInSecond[1];
245:
246: if (status.collectingTwoTimeStamps()) {
247: timingData.methodEntryExitCallTime0 = entryExitTimeInAbsCounts;
248: timingData.methodEntryExitCallTime1 = entryExitTimeInThreadCPUCounts;
249: timingData.methodEntryExitInnerTime0 = entryExitTimeInAbsCounts;
250: timingData.methodEntryExitOuterTime0 = 0;
251: timingData.methodEntryExitInnerTime1 = entryExitTimeInThreadCPUCounts;
252: timingData.methodEntryExitOuterTime1 = 0;
253: timingData.timerCountsInSecond0 = status.timerCountsInSecond[0];
254: timingData.timerCountsInSecond1 = status.timerCountsInSecond[1];
255: } else {
256: if (status.absoluteTimerOn) {
257: timingData.methodEntryExitCallTime0 = entryExitTimeInAbsCounts;
258: timingData.methodEntryExitInnerTime0 = entryExitTimeInAbsCounts;
259: timingData.methodEntryExitOuterTime0 = 0;
260: timingData.timerCountsInSecond0 = status.timerCountsInSecond[0];
261: } else {
262: timingData.methodEntryExitCallTime0 = entryExitTimeInThreadCPUCounts;
263: timingData.methodEntryExitInnerTime0 = entryExitTimeInThreadCPUCounts;
264: timingData.methodEntryExitOuterTime0 = 0;
265: timingData.timerCountsInSecond0 = status.timerCountsInSecond[1];
266: }
267: }
268:
269: return timingData;
270: }
271: }
|