0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: * The Original Software is NetBeans. The Initial Developer of the Original
0026: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0027: * Microsystems, Inc. All Rights Reserved.
0028: *
0029: * If you wish your version of this file to be governed by only the CDDL
0030: * or only the GPL Version 2, indicate your decision by adding
0031: * "[Contributor] elects to include this software in this distribution
0032: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0033: * single choice of license, a recipient has the option to distribute
0034: * your version of this file under either the CDDL, the GPL Version 2 or
0035: * to extend the choice of license to its licensees as provided above.
0036: * However, if you add GPL Version 2 code and therefore, elected the GPL
0037: * Version 2 license, then the option applies only if the new code is
0038: * made subject to such option by the copyright holder.
0039: */
0040:
0041: package org.netbeans.lib.profiler.results.cpu;
0042:
0043: import org.netbeans.lib.profiler.ProfilerLogger;
0044: import org.netbeans.lib.profiler.global.InstrumentationFilter;
0045: import org.netbeans.lib.profiler.global.ProfilingSessionStatus;
0046: import org.netbeans.lib.profiler.results.cpu.cct.nodes.MethodCPUCCTNode;
0047: import org.netbeans.lib.profiler.results.cpu.cct.nodes.RuntimeCPUCCTNode;
0048: import org.netbeans.lib.profiler.results.cpu.cct.nodes.TimedCPUCCTNode;
0049: import java.io.DataInputStream;
0050: import java.io.DataOutputStream;
0051: import java.io.IOException;
0052: import java.util.logging.Level;
0053: import java.util.logging.Logger;
0054:
0055: /**
0056: * An instance of this class contains a presentation-time CCT for the given thread in the compact, flattened form, that is also fast
0057: * to generate and save/retrieve.
0058: * Can represent data only on the method level "view" AKA "aggregation level". The CPUCCTClassContainer subclass provides functionality
0059: * to create and represent data at class and package aggregation level. The AllThreadsMergedCPUCCTContainer also supports views. A single
0060: * instance of CPUCCTContainer or its subclass can represent data only on a single aggregation level.
0061: *
0062: * @author Tomas Hurka
0063: * @author Misha Dmitriev
0064: */
0065: public class CPUCCTContainer {
0066: //~ Static fields/initializers -----------------------------------------------------------------------------------------------
0067:
0068: private static final Logger LOGGER = Logger
0069: .getLogger(CPUCCTContainer.class.getName());
0070:
0071: // -- Data used for the compact representation of the CCT
0072: /* In the compactData array, data is packed in the following way:
0073: * |---------------------------------------------------------------------------------------------------------
0074: * | methodID | nCalls |time0 | self |time1 | self |nbr. of subnodes | subnode0 | | subnodeN |
0075: * | | | | time0 |(if 2 timers | time1 | | offset | ... | offset |
0076: * | | | | | used) | | | | | |
0077: * |---------------------------------------------------------------------------------------------------------
0078: * 2 bytes 4 bytes 5 bytes 5 bytes 5 bytes 5 bytes 2 bytes 3 or 4 bytes depending on the size of compactData array
0079: */
0080: protected static final int OFS_METHODID = 0;
0081: protected static final int OFS_NCALLS = OFS_METHODID + 2;
0082: protected static final int OFS_TIME0 = OFS_NCALLS + 4;
0083: protected static final int OFS_SELFTIME0 = OFS_TIME0 + 5;
0084: protected static final int OFS_TIME1 = OFS_SELFTIME0 + 5;
0085: protected static final int OFS_SELFTIME1 = OFS_TIME1 + 5;
0086: protected static final int OFS_NSUBNODES1 = OFS_SELFTIME0 + 5;
0087: protected static final int OFS_NSUBNODES2 = OFS_SELFTIME1 + 5;
0088: protected static final int OFS_SUBNODE01 = OFS_NSUBNODES1 + 2;
0089: protected static final int OFS_SUBNODE02 = OFS_NSUBNODES2 + 2;
0090: protected static final int CHILD_OFS_SIZE_3 = 3;
0091: protected static final int CHILD_OFS_SIZE_4 = 4;
0092:
0093: // These are just the same-named xxxAbsCounts values converted into microseconds. So far used ONLY for informational purposes
0094: // (in "get internal statistics"), thus static is more or less tolerable (so far...)
0095: private static double timeInInjectedCodeInMS;
0096: private static double wholeGraphGrossTimeAbsInMS;
0097:
0098: //~ Instance fields ----------------------------------------------------------------------------------------------------------
0099:
0100: protected CPUResultsSnapshot cpuResSnapshot;
0101: protected FlatProfileContainer cachedFlatProfile;
0102: protected PrestimeCPUCCTNode rootNode;
0103: protected String threadName;
0104: protected byte[] compactData;
0105: protected int[] invPerMethodId;
0106:
0107: // -- Temporary data used during flat profile generation
0108: protected long[] timePerMethodId0;
0109: protected long[] timePerMethodId1;
0110: protected boolean collectingTwoTimeStamps; // True if we collect two timestamps, absolute and thread CPU, for each method invocation
0111: protected boolean displayWholeThreadCPUTime; // True if we can calculate, and thus display, valid whole thread CPU time
0112:
0113: // Time spent in instrumentation, measured in counts
0114: protected double timeInInjectedCodeInAbsCounts;
0115: protected double timeInInjectedCodeInThreadCPUCounts;
0116: protected int childOfsSize = -1;
0117: protected int nodeSize; // Size of a single node above, not taking possible subnodeOffset fields into account
0118: protected int threadId;
0119:
0120: // -- Data that is supposed to be used for user information in various parts of the CPU results display
0121: // Gross time spent in the whole graph, measured in counts. It's measured by "starting the clock" when the root
0122: // method is entered, and "stopping the clock" when it exits. Time for hotswapping and on-line data processing
0123: // is factored out, so what is actually contained in wholeGraphGrossTime is (pure time + instrumentation time).
0124: // Note that independent of method timestamps collected, root method entry and exit events always have both
0125: // absolute and thread CPU time stamps.
0126: protected long wholeGraphGrossTimeAbs;
0127: protected long wholeGraphGrossTimeThreadCPU;
0128:
0129: // This is calculated as a sum of net times spent in all methods
0130: protected long wholeGraphNetTime0;
0131: protected long wholeGraphNetTime1;
0132:
0133: // This is calculated as the above gross time minus total time spent in instrumentation
0134: protected long wholeGraphPureTimeAbs;
0135: protected long wholeGraphPureTimeThreadCPU;
0136: private InstrumentationFilter filter;
0137: private PrestimeCPUCCTNodeFree reverseCCTRootNode;
0138: private ProfilingSessionStatus status;
0139: private int[] nodeStack;
0140: private int childTotalNCalls;
0141: private int currentNodeStackSize;
0142: private int nodeStackPtr;
0143:
0144: // -- Temporary data used during reverse CCT generation
0145: private int selectedMethodId;
0146:
0147: // -- Temporary data used during above array generation
0148: private long childTotalTime0InTimerUnits;
0149: private long childTotalTime1InTimerUnits;
0150: private long totalInvNo;
0151:
0152: //~ Constructors -------------------------------------------------------------------------------------------------------------
0153:
0154: public CPUCCTContainer(TimedCPUCCTNode rtRootNode,
0155: CPUResultsSnapshot cpuResSnapshot,
0156: ProfilingSessionStatus status,
0157: InstrumentationFilter usedFilter, int nNodes,
0158: double[] threadActiveTimesInCounts, int threadId,
0159: String threadName) {
0160: this (cpuResSnapshot);
0161:
0162: this .threadId = threadId;
0163: this .threadName = threadName;
0164:
0165: this .status = status;
0166: this .filter = usedFilter;
0167:
0168: collectingTwoTimeStamps = cpuResSnapshot
0169: .isCollectingTwoTimeStamps();
0170:
0171: generateCompactData(rtRootNode, nNodes);
0172:
0173: calculateThreadActiveTimesInMS(threadActiveTimesInCounts,
0174: status);
0175:
0176: rootNode = new PrestimeCPUCCTNodeBacked(this , null, 0);
0177:
0178: if (rtRootNode.isRoot()) {
0179: rootNode.setThreadNode();
0180: }
0181: }
0182:
0183: protected CPUCCTContainer(CPUResultsSnapshot cpuResSnapshot) {
0184: this .cpuResSnapshot = cpuResSnapshot;
0185: }
0186:
0187: //~ Methods ------------------------------------------------------------------------------------------------------------------
0188:
0189: public CPUResultsSnapshot getCPUResSnapshot() {
0190: return cpuResSnapshot;
0191: }
0192:
0193: public int getChildOfsForNodeOfs(int nodeOfs, int childIdx) {
0194: if (childOfsSize == CHILD_OFS_SIZE_4) {
0195: return get4Bytes(nodeOfs
0196: + (collectingTwoTimeStamps ? OFS_SUBNODE02
0197: : OFS_SUBNODE01)
0198: + (childOfsSize * childIdx));
0199: } else {
0200: return get3Bytes(nodeOfs
0201: + (collectingTwoTimeStamps ? OFS_SUBNODE02
0202: : OFS_SUBNODE01)
0203: + (childOfsSize * childIdx));
0204: }
0205: }
0206:
0207: public boolean isCollectingTwoTimeStamps() {
0208: return collectingTwoTimeStamps;
0209: }
0210:
0211: public FlatProfileContainer getFlatProfile() {
0212: // if (cachedFlatProfile == null) {
0213: // generateFlatProfile();
0214: // }
0215: // return cachedFlatProfile;
0216: return generateFlatProfile();
0217: }
0218:
0219: public String[] getMethodClassNameAndSig(int methodId) {
0220: return cpuResSnapshot.getMethodClassNameAndSig(methodId,
0221: CPUResultsSnapshot.METHOD_LEVEL_VIEW);
0222: }
0223:
0224: // -- Methods for retrieving data for individual nodes
0225: public int getMethodIdForNodeOfs(int nodeOfs) {
0226: return get2Bytes(nodeOfs + OFS_METHODID);
0227: }
0228:
0229: public int getNCallsForNodeOfs(int nodeOfs) {
0230: return get4Bytes(nodeOfs + OFS_NCALLS);
0231: }
0232:
0233: public int getNChildrenForNodeOfs(int nodeOfs) {
0234: return get2Bytes(nodeOfs
0235: + (collectingTwoTimeStamps ? OFS_NSUBNODES2
0236: : OFS_NSUBNODES1));
0237: }
0238:
0239: public PrestimeCPUCCTNode getReverseCCT(int methodId) {
0240: return generateReverseCCT(methodId);
0241: }
0242:
0243: public PrestimeCPUCCTNode getRootNode() {
0244: return rootNode;
0245: }
0246:
0247: public long getSelfTime0ForNodeOfs(int nodeOfs) {
0248: return get5Bytes(nodeOfs + OFS_SELFTIME0);
0249: }
0250:
0251: public long getSelfTime1ForNodeOfs(int nodeOfs) {
0252: return get5Bytes(nodeOfs + OFS_SELFTIME1);
0253: }
0254:
0255: public long getSleepTime0ForNodeOfs(int nodeOfs) {
0256: return 0;
0257: } // TODO [wait]
0258:
0259: public int getThreadId() {
0260: return threadId;
0261: }
0262:
0263: public String getThreadName() {
0264: return threadName;
0265: }
0266:
0267: // Provided for information purposes (that is, the "get internal statistics" action) only. Since this stuff
0268: // is not used in any real calculations, it's more or less tolerable so far to have it static.
0269: public static double getTimeInInjectedCodeForDisplayedThread() {
0270: return timeInInjectedCodeInMS;
0271: }
0272:
0273: public long getTotalTime0ForNodeOfs(int nodeOfs) {
0274: return get5Bytes(nodeOfs + OFS_TIME0);
0275: }
0276:
0277: public long getTotalTime1ForNodeOfs(int nodeOfs) {
0278: return get5Bytes(nodeOfs + OFS_TIME1);
0279: }
0280:
0281: public long getWaitTime0ForNodeOfs(int nodeOfs) {
0282: return 0;
0283: } // TODO [wait]
0284:
0285: public static double getWholeGraphGrossTimeAbsForDisplayedThread() {
0286: return wholeGraphGrossTimeAbsInMS;
0287: }
0288:
0289: public long getWholeGraphNetTime0() {
0290: return wholeGraphNetTime0;
0291: }
0292:
0293: public long getWholeGraphNetTime1() {
0294: return wholeGraphNetTime1;
0295: }
0296:
0297: public long getWholeGraphPureTimeAbs() {
0298: return wholeGraphPureTimeAbs;
0299: }
0300:
0301: public long getWholeGraphPureTimeThreadCPU() {
0302: return wholeGraphPureTimeThreadCPU;
0303: }
0304:
0305: public boolean canDisplayWholeGraphCPUTime() {
0306: return displayWholeThreadCPUTime;
0307: }
0308:
0309: public void readFromStream(DataInputStream in) throws IOException {
0310: threadId = in.readInt();
0311: threadName = in.readUTF();
0312:
0313: collectingTwoTimeStamps = in.readBoolean();
0314:
0315: int len = in.readInt();
0316: compactData = new byte[len];
0317:
0318: if (compactData.length > 0xFFFFFF) {
0319: childOfsSize = CHILD_OFS_SIZE_4;
0320: } else {
0321: childOfsSize = CHILD_OFS_SIZE_3;
0322: }
0323:
0324: in.readFully(compactData);
0325:
0326: nodeSize = in.readInt();
0327:
0328: wholeGraphGrossTimeAbs = in.readLong();
0329: wholeGraphGrossTimeThreadCPU = in.readLong();
0330: timeInInjectedCodeInAbsCounts = in.readDouble();
0331: timeInInjectedCodeInThreadCPUCounts = in.readDouble();
0332: wholeGraphPureTimeAbs = in.readLong();
0333: wholeGraphPureTimeThreadCPU = in.readLong();
0334: wholeGraphNetTime0 = in.readLong();
0335: wholeGraphNetTime1 = in.readLong();
0336: totalInvNo = in.readLong();
0337: displayWholeThreadCPUTime = in.readBoolean();
0338:
0339: rootNode = new PrestimeCPUCCTNodeBacked(this , null, 0);
0340:
0341: if (this .getMethodIdForNodeOfs(0) == 0) {
0342: rootNode.setThreadNode();
0343: }
0344: }
0345:
0346: // -- Serialization support
0347: public void writeToStream(DataOutputStream out) throws IOException {
0348: out.writeInt(threadId);
0349: out.writeUTF(threadName);
0350:
0351: out.writeBoolean(collectingTwoTimeStamps);
0352:
0353: out.writeInt(compactData.length);
0354: out.write(compactData);
0355: out.writeInt(nodeSize);
0356:
0357: out.writeLong(wholeGraphGrossTimeAbs);
0358: out.writeLong(wholeGraphGrossTimeThreadCPU);
0359: out.writeDouble(timeInInjectedCodeInAbsCounts);
0360: out.writeDouble(timeInInjectedCodeInThreadCPUCounts);
0361: out.writeLong(wholeGraphPureTimeAbs);
0362: out.writeLong(wholeGraphPureTimeThreadCPU);
0363: out.writeLong(wholeGraphNetTime0);
0364: out.writeLong(wholeGraphNetTime1);
0365: out.writeLong(totalInvNo);
0366: out.writeBoolean(displayWholeThreadCPUTime);
0367: }
0368:
0369: protected void setChildOfsForNodeOfs(int nodeOfs, int childIdx,
0370: int val) {
0371: if (childOfsSize == CHILD_OFS_SIZE_4) {
0372: store4Bytes(nodeOfs
0373: + (collectingTwoTimeStamps ? OFS_SUBNODE02
0374: : OFS_SUBNODE01)
0375: + (childOfsSize * childIdx), val);
0376: } else {
0377: store3Bytes(nodeOfs
0378: + (collectingTwoTimeStamps ? OFS_SUBNODE02
0379: : OFS_SUBNODE01)
0380: + (childOfsSize * childIdx), val);
0381: }
0382: }
0383:
0384: // -- Methods for setting data for individual nodes
0385: protected void setMethodIdForNodeOfs(int nodeOfs, int val) {
0386: store2Bytes(nodeOfs + OFS_METHODID, val);
0387: }
0388:
0389: protected void setNCallsForNodeOfs(int nodeOfs, int val) {
0390: store4Bytes(nodeOfs + OFS_NCALLS, val);
0391: }
0392:
0393: protected void setNChildrenForNodeOfs(int nodeOfs, int val) {
0394: store2Bytes(nodeOfs
0395: + (collectingTwoTimeStamps ? OFS_NSUBNODES2
0396: : OFS_NSUBNODES1), val);
0397: }
0398:
0399: protected void setSelfTime0ForNodeOfs(int nodeOfs, long val) {
0400: store5Bytes(nodeOfs + OFS_SELFTIME0, val);
0401: }
0402:
0403: protected void setSelfTime1ForNodeOfs(int nodeOfs, long val) {
0404: store5Bytes(nodeOfs + OFS_SELFTIME1, val);
0405: }
0406:
0407: protected void setSleepTime0ForNodeOfs(int dataOfs, long waitTime0) {
0408: } // TODO [sleep should be stored separately in future versions]
0409:
0410: protected void setTotalTime0ForNodeOfs(int nodeOfs, long val) {
0411: store5Bytes(nodeOfs + OFS_TIME0, val);
0412: }
0413:
0414: protected void setTotalTime1ForNodeOfs(int nodeOfs, long val) {
0415: store5Bytes(nodeOfs + OFS_TIME1, val);
0416: }
0417:
0418: protected void setWaitTime0ForNodeOfs(int dataOfs, long waitTime0) {
0419: } // TODO [wait should be stored separately in future versions]
0420:
0421: protected void addFlatProfTimeForNode(int dataOfs) {
0422: int nChildren = getNChildrenForNodeOfs(dataOfs);
0423:
0424: if (nChildren > 0) {
0425: for (int i = 0; i < nChildren; i++) {
0426: int childOfs = getChildOfsForNodeOfs(dataOfs, i);
0427: addFlatProfTimeForNode(childOfs);
0428: }
0429: }
0430:
0431: int methodId = getMethodIdForNodeOfs(dataOfs);
0432: timePerMethodId0[methodId] += getSelfTime0ForNodeOfs(dataOfs);
0433:
0434: if (collectingTwoTimeStamps) {
0435: timePerMethodId1[methodId] += getSelfTime1ForNodeOfs(dataOfs);
0436: }
0437:
0438: invPerMethodId[methodId] += getNCallsForNodeOfs(dataOfs);
0439: }
0440:
0441: protected void addToReverseCCT(PrestimeCPUCCTNodeFree reverseNode,
0442: int methodId) {
0443: selectedMethodId = methodId;
0444: reverseCCTRootNode = reverseNode;
0445:
0446: currentNodeStackSize = 320;
0447: nodeStack = new int[currentNodeStackSize];
0448: nodeStackPtr = 0;
0449: checkStraightGraphNode(0);
0450:
0451: nodeStack = null; // Free memory
0452: reverseCCTRootNode = null; // Ditto
0453: }
0454:
0455: /**
0456: * Walk all the elements of the main graph, looking for nodes with selectedMethodId signature.
0457: * Whenever one is found, add its path, in reversed form, to the rootNode.
0458: * When path is added, same-named nodes are merged until the first pair of different nodes is found.
0459: */
0460: protected void checkStraightGraphNode(int dataOfs) {
0461: if (nodeStackPtr >= currentNodeStackSize) {
0462: int[] newNodeStack = new int[currentNodeStackSize * 2];
0463: System.arraycopy(nodeStack, 0, newNodeStack, 0,
0464: currentNodeStackSize);
0465: nodeStack = newNodeStack;
0466: currentNodeStackSize = currentNodeStackSize * 2;
0467: }
0468:
0469: nodeStack[nodeStackPtr++] = dataOfs;
0470:
0471: if (getMethodIdForNodeOfs(dataOfs) == selectedMethodId) {
0472: addReversePath();
0473: }
0474:
0475: int nChildren = getNChildrenForNodeOfs(dataOfs);
0476:
0477: for (int i = 0; i < nChildren; i++) {
0478: checkStraightGraphNode(getChildOfsForNodeOfs(dataOfs, i));
0479: }
0480:
0481: nodeStackPtr--;
0482: }
0483:
0484: protected FlatProfileContainer generateFlatProfile() {
0485: preGenerateFlatProfile();
0486:
0487: addFlatProfTimeForNode(0);
0488:
0489: return postGenerateFlatProfile();
0490: }
0491:
0492: protected PrestimeCPUCCTNodeFree generateReverseCCT(int methodId) {
0493: selectedMethodId = methodId;
0494:
0495: currentNodeStackSize = 320;
0496: nodeStack = new int[currentNodeStackSize];
0497: nodeStackPtr = 0;
0498: checkStraightGraphNode(0);
0499:
0500: PrestimeCPUCCTNodeFree ret = reverseCCTRootNode;
0501:
0502: nodeStack = null; // Free memory
0503: reverseCCTRootNode = null; // Ditto
0504:
0505: return ret;
0506: }
0507:
0508: protected int get2Bytes(int ofs) {
0509: return (((int) compactData[ofs] & 0xFF) << 8)
0510: | ((int) compactData[ofs + 1] & 0xFF);
0511: }
0512:
0513: protected int get3Bytes(int ofs) {
0514: return (((int) compactData[ofs++] & 0xFF) << 16)
0515: | (((int) compactData[ofs++] & 0xFF) << 8)
0516: | ((int) compactData[ofs++] & 0xFF);
0517: }
0518:
0519: protected int get4Bytes(int ofs) {
0520: return (((int) compactData[ofs++] & 0xFF) << 24)
0521: | (((int) compactData[ofs++] & 0xFF) << 16)
0522: | (((int) compactData[ofs++] & 0xFF) << 8)
0523: | ((int) compactData[ofs++] & 0xFF);
0524: }
0525:
0526: protected long get5Bytes(int ofs) {
0527: return (((long) compactData[ofs++] & 0xFF) << 32)
0528: | (((long) compactData[ofs++] & 0xFF) << 24)
0529: | (((long) compactData[ofs++] & 0xFF) << 16)
0530: | (((long) compactData[ofs++] & 0xFF) << 8)
0531: | ((long) compactData[ofs++] & 0xFF);
0532: }
0533:
0534: protected FlatProfileContainer postGenerateFlatProfile() {
0535: FlatProfileContainer fpc = new FlatProfileContainerBacked(this ,
0536: timePerMethodId0, timePerMethodId1, invPerMethodId,
0537: timePerMethodId0.length);
0538:
0539: timePerMethodId0 = timePerMethodId1 = null;
0540: invPerMethodId = null;
0541:
0542: return fpc;
0543: }
0544:
0545: protected void preGenerateFlatProfile() {
0546: int totalMethods = cpuResSnapshot.getNInstrMethods();
0547: timePerMethodId0 = new long[totalMethods];
0548:
0549: if (collectingTwoTimeStamps) {
0550: timePerMethodId1 = new long[totalMethods];
0551: }
0552:
0553: invPerMethodId = new int[totalMethods];
0554: timePerMethodId0[0] = -1; // 0th element is a hidden "Thread" quazi-method. This prevents exposing it in a pathological case when all times are zero.
0555: }
0556:
0557: // -- Utility methods, not interesting enough to place earlier in the code
0558: protected void store2Bytes(int ofs, int data) {
0559: compactData[ofs] = (byte) ((data >> 8) & 0xFF);
0560: compactData[ofs + 1] = (byte) ((data) & 0xFF);
0561: }
0562:
0563: protected void store3Bytes(int ofs, int data) {
0564: int curPos = ofs;
0565: compactData[curPos++] = (byte) ((data >> 16) & 0xFF);
0566: compactData[curPos++] = (byte) ((data >> 8) & 0xFF);
0567: compactData[curPos++] = (byte) ((data) & 0xFF);
0568: }
0569:
0570: protected void store4Bytes(int ofs, int data) {
0571: int curPos = ofs;
0572: compactData[curPos++] = (byte) ((data >> 24) & 0xFF);
0573: compactData[curPos++] = (byte) ((data >> 16) & 0xFF);
0574: compactData[curPos++] = (byte) ((data >> 8) & 0xFF);
0575: compactData[curPos++] = (byte) ((data) & 0xFF);
0576: }
0577:
0578: protected void store5Bytes(int ofs, long data) {
0579: int curPos = ofs;
0580: compactData[curPos++] = (byte) ((data >> 32) & 0xFF);
0581: compactData[curPos++] = (byte) ((data >> 24) & 0xFF);
0582: compactData[curPos++] = (byte) ((data >> 16) & 0xFF);
0583: compactData[curPos++] = (byte) ((data >> 8) & 0xFF);
0584: compactData[curPos++] = (byte) ((data) & 0xFF);
0585: }
0586:
0587: private void addChild(TimedCPUCCTNode node, TimedCPUCCTNode parent) {
0588: if ((node == null) || (parent == null)) {
0589: return;
0590: }
0591:
0592: TimedCPUCCTNode compParent = null;
0593: TimedCPUCCTNode newChild = null;
0594:
0595: int filterStatus = node.getFilteredStatus();
0596:
0597: if (!(node instanceof MethodCPUCCTNode)) {
0598: filterStatus = TimedCPUCCTNode.FILTERED_YES;
0599: }
0600:
0601: switch (filterStatus) {
0602: case TimedCPUCCTNode.FILTERED_YES: {
0603: compParent = parent;
0604:
0605: break;
0606: }
0607: case TimedCPUCCTNode.FILTERED_MAYBE: {
0608: if (node instanceof MethodCPUCCTNode) {
0609: status.beginTrans(false);
0610:
0611: try {
0612: String className = status.getInstrMethodClasses()[((MethodCPUCCTNode) node)
0613: .getMethodId()].replace('.', '/'); // NOI18N
0614:
0615: if (!filter.passesFilter(className)) {
0616: compParent = parent;
0617: } else {
0618: newChild = (TimedCPUCCTNode) node.clone();
0619: compParent = newChild;
0620: }
0621: } finally {
0622: status.endTrans();
0623: }
0624: } else {
0625: compParent = parent;
0626: }
0627:
0628: break;
0629: }
0630: case TimedCPUCCTNode.FILTERED_NO: {
0631: MethodCPUCCTNode existingChild = MethodCPUCCTNode.Locator
0632: .locate(((MethodCPUCCTNode) node).getMethodId(),
0633: parent.getChildren());
0634:
0635: if (existingChild == null) {
0636: newChild = (TimedCPUCCTNode) node.clone();
0637: } else {
0638: newChild = null;
0639: existingChild.addNCalls(node.getNCalls());
0640: existingChild.addNCallsDiff(node.getNCallsDiff());
0641: existingChild.addNetTime0(node.getNetTime0());
0642: existingChild.addNetTime1(node.getNetTime1());
0643: existingChild.addSleepTime0(node.getSleepTime0());
0644: existingChild.addWaitTime0(node.getWaitTime0());
0645: }
0646:
0647: compParent = newChild;
0648:
0649: break;
0650: }
0651: default:
0652: ProfilerLogger.warning("Unknown filtered status ("
0653: + filterStatus + ") for " + node); // NOI18N
0654: }
0655:
0656: int nChildren = (node.getChildren() != null) ? node
0657: .getChildren().size() : 0;
0658:
0659: for (int i = 0; i < nChildren; i++) {
0660: addChild(
0661: (TimedCPUCCTNode) node.getChildren().getChildAt(i),
0662: compParent);
0663: }
0664:
0665: if (newChild != null) {
0666: parent.attachNodeAsChild(newChild);
0667: } else {
0668: if (!parent.isRoot()) { // no propagation of filtered-out data to the Thread level node
0669: parent.addNetTime0(node.getNetTime0());
0670: parent.addNCallsDiff(node.getNCalls());
0671:
0672: if (collectingTwoTimeStamps) {
0673: parent.addNetTime1(node.getNetTime1());
0674: }
0675: } else {
0676: // threadTimeCompensation0 += node.getNetTime0();
0677: // if (collectingTwoTimeStamps) {
0678: // threadTimeCompensation1 += node.getNetTime1();
0679: // }
0680: }
0681: }
0682: }
0683:
0684: /**
0685: * Add the whole reverse path contained in the nodeStack to the reverse call tree, merging nodes where appropriate.
0686: * Most of the complexity of the code is due to handling of the intermediate "from" nodes.
0687: */
0688: private void addReversePath() {
0689: PrestimeCPUCCTNodeFree curNode = null; // This is effectively a node above the root node - which is non-existent
0690: int stackTopIdx = nodeStackPtr - 1;
0691:
0692: for (int i = stackTopIdx; i >= 0; i--) {
0693: int sourceNodeOfs = nodeStack[i];
0694: int sourceNodeId = getMethodIdForNodeOfs(sourceNodeOfs);
0695:
0696: if (sourceNodeId == 0) {
0697: return; // It doesn't make sense to add "Thread" nodes to the reverse tree
0698: }
0699:
0700: boolean matchingChildFound = false;
0701:
0702: if (i < stackTopIdx) { // sourceNodeOfs is some intermediate node
0703:
0704: PrestimeCPUCCTNodeFree[] curNodeChildren = (PrestimeCPUCCTNodeFree[]) curNode
0705: .getChildren();
0706:
0707: if (curNodeChildren != null) {
0708: for (int j = 0; j < curNodeChildren.length; j++) {
0709: if (curNodeChildren[j].getMethodId() == sourceNodeId) {
0710: curNode = curNodeChildren[j];
0711:
0712: if (curNode.isContextCallsNode()) { // Skip the "context calls" node if it exists
0713:
0714: int prevSourceNodeOfs = nodeStack[i + 1];
0715: mergeBySelfTime(curNode,
0716: prevSourceNodeOfs);
0717: curNode = (PrestimeCPUCCTNodeFree) curNode
0718: .getChildren()[0];
0719: }
0720:
0721: mergeBySelfTime(curNode, sourceNodeOfs);
0722: matchingChildFound = true;
0723:
0724: break;
0725: }
0726: }
0727: }
0728: } else { // sourceNode is the topmost stack node
0729: curNode = reverseCCTRootNode;
0730:
0731: if (curNode == null) {
0732: curNode = createChildlessCopyBySelfTime(sourceNodeOfs);
0733: reverseCCTRootNode = curNode;
0734: } else {
0735: mergeBySelfTime(curNode, sourceNodeOfs);
0736: }
0737:
0738: matchingChildFound = true;
0739: }
0740:
0741: if (!matchingChildFound) { // sourceNode may only be an intermediate node
0742:
0743: PrestimeCPUCCTNodeFree newNode = createChildlessCopyBySelfTime(sourceNodeOfs);
0744: PrestimeCPUCCTNodeFree[] curNodeChildren = (PrestimeCPUCCTNodeFree[]) curNode
0745: .getChildren();
0746:
0747: if (curNodeChildren != null) {
0748: // For the given node, add an intermediate "context calls" node. If previously there was just one child,
0749: // insert another "context calls" node for it.
0750: int prevSourceNodeOfs = nodeStack[i + 1];
0751:
0752: if (curNodeChildren.length == 1) { // Insert a context node for the already existing single child
0753:
0754: PrestimeCPUCCTNodeFree origFirstChild = curNodeChildren[0];
0755: PrestimeCPUCCTNodeFree ccNode = curNode
0756: .createChildlessCopy();
0757: subtractNodeDataBySelfTime(ccNode,
0758: prevSourceNodeOfs); // Undo the results of merging with the parent of sourceNode
0759: ccNode
0760: .setMethodId(origFirstChild
0761: .getMethodId());
0762: ccNode.setContextCallsNode();
0763: curNodeChildren[0] = ccNode;
0764: ccNode.parent = curNode;
0765: ccNode.addChild(origFirstChild);
0766: origFirstChild.parent = ccNode;
0767: }
0768:
0769: PrestimeCPUCCTNodeFree ccNode = createChildlessCopyBySelfTime(prevSourceNodeOfs);
0770: ccNode
0771: .setMethodId(getMethodIdForNodeOfs(sourceNodeOfs));
0772: ccNode.setContextCallsNode();
0773: curNode.addChild(ccNode);
0774: ccNode.parent = curNode;
0775: curNode = ccNode;
0776: }
0777:
0778: curNode.addChild(newNode);
0779: newNode.parent = curNode;
0780: curNode = newNode;
0781: }
0782: }
0783: }
0784:
0785: /**
0786: * After presentation-time CCT is generated, calculate various special time values stored in this instance
0787: */
0788: private void calculateThreadActiveTimesInMS(
0789: double[] threadActiveTimesInCounts,
0790: ProfilingSessionStatus status) {
0791: //!!! Delete this comment after deciding what to do with the whole issue.
0792: // In the code below, '+=' is caused by the fact that this method may be called multiple times when in getRootNode() we
0793: // generate CCTs for all threads. In the default single-thread case the real value is just added to the initial zero value.
0794: wholeGraphGrossTimeAbs = (long) threadActiveTimesInCounts[0];
0795: wholeGraphGrossTimeThreadCPU = (long) threadActiveTimesInCounts[1];
0796: timeInInjectedCodeInAbsCounts = threadActiveTimesInCounts[2];
0797: timeInInjectedCodeInThreadCPUCounts = threadActiveTimesInCounts[3];
0798:
0799: wholeGraphGrossTimeAbsInMS += ((wholeGraphGrossTimeAbs * 1000.0) / status.timerCountsInSecond[0]);
0800: timeInInjectedCodeInMS += ((timeInInjectedCodeInAbsCounts * 1000.0) / status.timerCountsInSecond[0]);
0801:
0802: // Note that here we have to use status.timerCountsInSecond[x] explicitly instead of timerCountsInSecond0/1 (which may correspond to wrong time type)
0803: wholeGraphPureTimeAbs += (int) ((((double) wholeGraphGrossTimeAbs - timeInInjectedCodeInAbsCounts) * 1000000) / status.timerCountsInSecond[0]);
0804:
0805: //System.err.println("*** wholeGraphTimeAbs gross (cnts) = " + wholeGraphGrossTimeAbs + ", pure (mcs) = " + wholeGraphPureTimeAbs);
0806: if (wholeGraphGrossTimeThreadCPU > 0) { // Otherwise it means we couldn't calculate it and it shouldn't be displayed
0807: displayWholeThreadCPUTime = true;
0808: wholeGraphPureTimeThreadCPU += (int) ((((double) wholeGraphGrossTimeThreadCPU - timeInInjectedCodeInThreadCPUCounts) * 1000000) / status.timerCountsInSecond[1]);
0809:
0810: //System.err.println("*** wholeGraphTimeThreadCPU gross (cnts) = " + wholeGraphGrossTimeThreadCPU + ", pure (mcs) = " + wholeGraphPureTimeThreadCPU);
0811: //System.err.println("*** timeInInjectedCode (mcs) = " + (timeInInjectedCodeInAbsCounts * 1000000 / status.timerCountsInSecond[0]));
0812: } else {
0813: displayWholeThreadCPUTime = false;
0814: }
0815:
0816: // Take measures in case timer's low resolution has caused funny results
0817: if (wholeGraphPureTimeAbs < 0) {
0818: wholeGraphPureTimeAbs = 0;
0819: }
0820:
0821: if (wholeGraphPureTimeThreadCPU < 0) {
0822: wholeGraphPureTimeThreadCPU = 0;
0823: }
0824:
0825: wholeGraphNetTime0 += get5Bytes(0 + OFS_TIME0);
0826:
0827: if (collectingTwoTimeStamps) {
0828: wholeGraphNetTime1 += get5Bytes(0 + OFS_TIME1);
0829: }
0830: }
0831:
0832: private PrestimeCPUCCTNodeFree createChildlessCopyBySelfTime(
0833: int sourceNodeDataOfs) {
0834: PrestimeCPUCCTNodeFree node = new PrestimeCPUCCTNodeFree(this ,
0835: null, getMethodIdForNodeOfs(sourceNodeDataOfs));
0836: mergeBySelfTime(node, sourceNodeDataOfs);
0837:
0838: return node;
0839: }
0840:
0841: // private long threadTimeCompensation0, threadTimeCompensation1;
0842: private TimedCPUCCTNode filterCCT(final TimedCPUCCTNode rootNode) {
0843: TimedCPUCCTNode newRoot = (TimedCPUCCTNode) rootNode.clone();
0844:
0845: // threadTimeCompensation0 = threadTimeCompensation1 = 0;
0846: int nChildren = (rootNode.getChildren() != null) ? rootNode
0847: .getChildren().size() : 0;
0848:
0849: for (int i = 0; i < nChildren; i++) {
0850: addChild((TimedCPUCCTNode) rootNode.getChildren()
0851: .getChildAt(i), newRoot);
0852: }
0853:
0854: // long time0, time1;
0855: // time0 = newRoot.getNetTime0() - threadTimeCompensation0;
0856: newRoot.setNetTime0(0);
0857:
0858: if (collectingTwoTimeStamps) {
0859: // time1 = newRoot.getNetTime1() - threadTimeCompensation1;
0860: newRoot.setNetTime1(0);
0861: }
0862:
0863: return newRoot;
0864: }
0865:
0866: private void generateCompactData(TimedCPUCCTNode rootNode,
0867: int nNodes) {
0868: nodeSize = collectingTwoTimeStamps ? OFS_SUBNODE02
0869: : OFS_SUBNODE01;
0870: childOfsSize = CHILD_OFS_SIZE_3;
0871:
0872: int arraySize = (nodeSize * nNodes)
0873: + (childOfsSize * (nNodes - 1)); // For each node, except the root one, there is a parent node that references it with childOfsSize bytes long offset
0874:
0875: if (arraySize > 0xFFFFFF) { // compactData is to big to use 3 bytes subnode offsets
0876: childOfsSize = CHILD_OFS_SIZE_4;
0877: arraySize = (nodeSize * nNodes)
0878: + (childOfsSize * (nNodes - 1));
0879: }
0880:
0881: if (LOGGER.isLoggable(Level.FINEST)) {
0882: LOGGER.finest("generateCompact data: nNodes " + nNodes); // NOI18N
0883: LOGGER
0884: .finest("generateCompact data: node size "
0885: + nodeSize); // NOI18N
0886: LOGGER.finest("generateCompact data: array size "
0887: + arraySize); // NOI18N
0888: LOGGER.finest("generateCompact data: child offset "
0889: + childOfsSize); // NOI18N
0890: }
0891:
0892: compactData = new byte[arraySize];
0893:
0894: rootNode = filterCCT(rootNode);
0895:
0896: generateMirrorNode(rootNode, 0);
0897: }
0898:
0899: /**
0900: * Generates an equivalent of rtNode in the compact data. Returns the offset right after the last generated node, which
0901: * is this node if it has no children, or the last recursive child of this node.
0902: */
0903: private int generateMirrorNode(final TimedCPUCCTNode rtNode,
0904: final int dataOfs) {
0905: if (LOGGER.isLoggable(Level.FINEST)) {
0906: LOGGER.finest("Generate mirror node for ofs: " + dataOfs
0907: + ", node: " + rtNode); // NOI18N
0908: }
0909:
0910: long this NodeTotalTime0InTimerUnits = 0;
0911: long this NodeTotalTime1InTimerUnits = 0;
0912: int nCallsFromThisNode = 0;
0913: int totalNCallsFromThisNode = 0;
0914:
0915: generateNodeBase(rtNode, dataOfs);
0916:
0917: totalInvNo += rtNode.getNCalls();
0918:
0919: RuntimeCPUCCTNode.Children nodeChildren = rtNode.getChildren();
0920:
0921: int nChildren = (nodeChildren != null) ? nodeChildren.size()
0922: : 0;
0923: int nextNodeOfs = dataOfs + nodeSize
0924: + (nChildren * childOfsSize);
0925:
0926: nCallsFromThisNode += rtNode.getNCallsDiff();
0927:
0928: if (nChildren > 0) {
0929: int childCounter = 0;
0930:
0931: for (int i = 0; i < nChildren; i++) {
0932: RuntimeCPUCCTNode aNode = nodeChildren.getChildAt(i);
0933:
0934: if (aNode instanceof MethodCPUCCTNode) { // TODO replace "instanceof" by a visitor implementation
0935: setChildOfsForNodeOfs(dataOfs, childCounter,
0936: nextNodeOfs);
0937: nextNodeOfs = generateMirrorNode(
0938: (MethodCPUCCTNode) aNode, nextNodeOfs);
0939:
0940: this NodeTotalTime0InTimerUnits += childTotalTime0InTimerUnits; // Completely uncleansed time
0941:
0942: if (collectingTwoTimeStamps) {
0943: this NodeTotalTime1InTimerUnits += childTotalTime1InTimerUnits; // Ditto
0944: }
0945:
0946: nCallsFromThisNode += ((MethodCPUCCTNode) aNode)
0947: .getNCalls();
0948: totalNCallsFromThisNode += childTotalNCalls;
0949: childCounter++;
0950: }
0951: }
0952: }
0953:
0954: // Calculate cleansed self time
0955: /* PROTOTYPE [wait]
0956: long time = (long) (((double) rtNode.netTime0 - rtNode.waitTime0 - rtNode.nCalls * timingData.methodEntryExitInnerTime0 - nCallsFromThisNode * timingData.methodEntryExitOuterTime0) * 1000000 / timingData.timerCountsInSecond0);
0957: */
0958: long time = (long) TimingAdjusterOld.getDefault(status)
0959: .adjustTime(rtNode.getNetTime0(), rtNode.getNCalls(),
0960: nCallsFromThisNode, false);
0961:
0962: // (long) (((double) rtNode.getNetTime0() - rtNode.getNCalls() * timingData.methodEntryExitInnerTime0
0963: // - nCallsFromThisNode * timingData.methodEntryExitOuterTime0) * 1000000 / timingData
0964: // .timerCountsInSecond0);
0965: if (time < 0) {
0966: // It may happen that for some very short methods the resulting time, after subtracting the instrumentation time, gets negative.
0967: // When I calculated some concrete results using the (now-commented) code below, it appeared that for such methods the net
0968: // time per call, in timer counts, is in the order of -0.1.. -0.2. In other words, it's a very small error caused by the
0969: // hi-res timer (on Windows at least) being still too coarse-grain if just a few machine instructions are to be measured; and
0970: // possibly insufficient precision of our advanced determination of instrumentation time.
0971: // Setting the result to zero seems reasonable in this situation.
0972: //if (nCallsFromThisNode == 0) {
0973: // Net time per call, in counts
0974: //double ntpc = ((double)cgNode.netTime - thisNode.nCalls * methodEntryExitInnerTime - nCallsFromThisNode * methodEntryExitOuterTime) / thisNode.nCalls;
0975: //System.out.println("*** N: id= " + thisNode.methodId + ", cls= " + thisNode.nCalls + ", netTime= " + cgNode.netTime + ", nCFrom= " + nCallsFromThisNode + ", res = " + thisNode.netTime + ", ntpc = " + ntpc);
0976: //}
0977: time = 0;
0978: }
0979:
0980: setSelfTime0ForNodeOfs(dataOfs, time);
0981: setWaitTime0ForNodeOfs(dataOfs, rtNode.getWaitTime0());
0982: setSleepTime0ForNodeOfs(dataOfs, rtNode.getSleepTime0());
0983:
0984: this NodeTotalTime0InTimerUnits += rtNode.getNetTime0(); // Uncleansed time for this node and all its children
0985: childTotalTime0InTimerUnits = this NodeTotalTime0InTimerUnits; // It will be effectively returned by this method
0986: // Calculate cleansed total time
0987:
0988: time = (long) TimingAdjusterOld.getDefault(status).adjustTime(
0989: this NodeTotalTime0InTimerUnits, rtNode.getNCalls(),
0990: totalNCallsFromThisNode, false);
0991:
0992: // time = (long) (((double) thisNodeTotalTime0InTimerUnits - rtNode.getNCalls()* timingData.methodEntryExitInnerTime0
0993: // - totalNCallsFromThisNode * timingData.methodEntryExitCallTime0) * 1000000 / timingData
0994: // .timerCountsInSecond0);
0995: if (time < 0) {
0996: //System.out.println("*** Negative: " + thisNode.totalTime0 + ", thisNCalls = " + thisNode.nCalls + ", fromNCalls = " + totalNCallsFromThisNode);
0997: time = 0;
0998: }
0999:
1000: setTotalTime0ForNodeOfs(dataOfs, time);
1001:
1002: if (collectingTwoTimeStamps) {
1003: // Calculate cleansed self time
1004: time = (long) TimingAdjusterOld.getDefault(status)
1005: .adjustTime(rtNode.getNetTime1(),
1006: rtNode.getNCalls(), nCallsFromThisNode,
1007: true);
1008:
1009: // time = (long) (((double) rtNode.getNetTime1()
1010: // - rtNode.getNCalls() * timingData.methodEntryExitInnerTime1
1011: // - nCallsFromThisNode * timingData.methodEntryExitOuterTime1) * 1000000 / timingData
1012: // .timerCountsInSecond1);
1013: if (time < 0) {
1014: time = 0;
1015: }
1016:
1017: setSelfTime1ForNodeOfs(dataOfs, time);
1018: this NodeTotalTime1InTimerUnits += rtNode.getNetTime1();
1019: childTotalTime1InTimerUnits = this NodeTotalTime1InTimerUnits; // It will be effectively returned by this method
1020: // Calculate cleansed total time
1021:
1022: time = (long) TimingAdjusterOld.getDefault(status)
1023: .adjustTime(this NodeTotalTime1InTimerUnits,
1024: rtNode.getNCalls(),
1025: totalNCallsFromThisNode, true);
1026:
1027: // time = (long) (((double) thisNodeTotalTime1InTimerUnits - rtNode.getNCalls() * timingData.methodEntryExitInnerTime0
1028: // - totalNCallsFromThisNode * timingData.methodEntryExitCallTime1) * 1000000 / timingData
1029: // .timerCountsInSecond1);
1030: if (time < 0) {
1031: time = 0;
1032: }
1033:
1034: setTotalTime1ForNodeOfs(dataOfs, time);
1035: }
1036:
1037: childTotalNCalls = totalNCallsFromThisNode + rtNode.getNCalls(); // It will be effectively returned by this method
1038:
1039: return nextNodeOfs;
1040: }
1041:
1042: private void generateNodeBase(TimedCPUCCTNode rtNode,
1043: int nodeDataOfs) {
1044: int methodId = (rtNode instanceof MethodCPUCCTNode) ? ((MethodCPUCCTNode) rtNode)
1045: .getMethodId()
1046: : 0;
1047: int nCalls = rtNode.getNCalls();
1048: int nChildren = (rtNode.getChildren() != null) ? rtNode
1049: .getChildren().size() : 0;
1050:
1051: setMethodIdForNodeOfs(nodeDataOfs, methodId);
1052: setNCallsForNodeOfs(nodeDataOfs, nCalls);
1053: setNChildrenForNodeOfs(nodeDataOfs, nChildren);
1054: }
1055:
1056: private void mergeBySelfTime(PrestimeCPUCCTNodeFree curNode,
1057: int sourceNodeDataOfs) {
1058: curNode.addNCalls(getNCallsForNodeOfs(sourceNodeDataOfs));
1059: curNode
1060: .addTotalTime0(getSelfTime0ForNodeOfs(sourceNodeDataOfs));
1061:
1062: if (collectingTwoTimeStamps) {
1063: curNode
1064: .addTotalTime1(getSelfTime1ForNodeOfs(sourceNodeDataOfs));
1065: }
1066:
1067: curNode.addWaitTime0(getWaitTime0ForNodeOfs(sourceNodeDataOfs));
1068: curNode
1069: .addSleepTime0(getSleepTime0ForNodeOfs(sourceNodeDataOfs));
1070: }
1071:
1072: private void subtractNodeDataBySelfTime(
1073: PrestimeCPUCCTNodeFree curNode, int sourceNodeDataOfs) {
1074: curNode.addNCalls(-getNCallsForNodeOfs(sourceNodeDataOfs));
1075: curNode
1076: .addTotalTime0(-getSelfTime0ForNodeOfs(sourceNodeDataOfs));
1077:
1078: if (collectingTwoTimeStamps) {
1079: curNode
1080: .addTotalTime1(-getSelfTime1ForNodeOfs(sourceNodeDataOfs));
1081: }
1082:
1083: curNode
1084: .addWaitTime0(-getWaitTime0ForNodeOfs(sourceNodeDataOfs)); // TODO: [wait] what is this?
1085: curNode
1086: .addSleepTime0(-getSleepTime0ForNodeOfs(sourceNodeDataOfs)); // TODO: [wait] what is this?
1087: }
1088: }
|