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.memory;
042:
043: import java.io.DataInputStream;
044: import java.io.DataOutputStream;
045: import java.io.IOException;
046:
047: /**
048: * A terminal node used in Object Liveness Profiling Calling Context Tree (CCT).
049: * Contains the information gathered during object liveness profiling, which can be calculated for intermediate nodes
050: * if known for terminal nodes.
051: * <p/>
052: * Normally used as a leaf, except in case there are multiple same paths in the tree with differfent length.
053: * <p/>
054: * The information in TermCCTNode represents all objects of the same type allocated using same call path.
055: *
056: * @author Misha Dmitriev
057: * @author Ian Formanek
058: */
059: public class RuntimeObjLivenessTermCCTNode extends
060: RuntimeObjAllocTermCCTNode {
061: //~ Instance fields ----------------------------------------------------------------------------------------------------------
062:
063: // info on surviving generation
064: // [0] - epoch number
065: // [1] - # allocated in given epoch
066: // e.g.
067: // +---------+
068: // | 1 | 4 |
069: // | 5 | 3 |
070: // | 6 | 34 |
071: // | 14 | 56 |
072: // +---------+
073: private int[][] epochAndNLiveObjects; // null in static snapshots
074:
075: //~ Constructors -------------------------------------------------------------------------------------------------------------
076:
077: public RuntimeObjLivenessTermCCTNode(int methodId) {
078: super (methodId);
079: }
080:
081: protected RuntimeObjLivenessTermCCTNode() {
082: } // only for I/O
083:
084: //~ Methods ------------------------------------------------------------------------------------------------------------------
085:
086: public int getType() {
087: return TYPE_RuntimeObjLivenessTermCCTNode;
088: }
089:
090: /**
091: * Only called (from ObjLivenessCallGraphBuilder) when processing results during profiled app runtime
092: */
093: public void addLiveObjectForEpoch(int epoch) {
094: if (epochAndNLiveObjects == null) {
095: epochAndNLiveObjects = new int[2][2];
096: epochAndNLiveObjects[0][0] = epochAndNLiveObjects[0][1] = -1;
097: }
098:
099: int len = epochAndNLiveObjects[0].length;
100: int lenMinusOne = len - 1;
101: int row = lenMinusOne;
102:
103: while ((epochAndNLiveObjects[0][row] == -1) && (row > 0)) {
104: row--;
105: }
106:
107: if (epochAndNLiveObjects[0][row] > -1) { // Some epochs already exist
108:
109: if (epochAndNLiveObjects[0][row] < epoch) { // This epoch is newer than the newest recorded one; need to open a new epoch
110:
111: if (row == lenMinusOne) {
112: int[][] newEpochAndNLiveObjects = new int[2][len + 2];
113: System.arraycopy(epochAndNLiveObjects[0], 0,
114: newEpochAndNLiveObjects[0], 0, len);
115: System.arraycopy(epochAndNLiveObjects[1], 0,
116: newEpochAndNLiveObjects[1], 0, len);
117: epochAndNLiveObjects = newEpochAndNLiveObjects;
118: epochAndNLiveObjects[0][len] = epochAndNLiveObjects[0][len + 1] = -1;
119: }
120:
121: row++;
122: } else {
123: // Search if this epoch exists at all. Such a thing (an object allocation record for an old epoch arriving when
124: // a newer epoch(s) have already been opened) may happen in heavily multithreaded programs, where rescheduling
125: // happens when a thread is inside our ProfilerRuntimeObjLiveness.traceObjAlloc() method.
126: while ((row > 0)
127: && (epochAndNLiveObjects[0][row] != epoch)) {
128: row--;
129: }
130:
131: if (epochAndNLiveObjects[0][row] != epoch) { // Pathological case. We don't track them - they shouldn't be frequent anyway.
132:
133: return;
134: }
135: }
136: }
137:
138: epochAndNLiveObjects[0][row] = epoch;
139: epochAndNLiveObjects[1][row]++;
140: }
141:
142: public static float calculateAvgObjectAgeForAllPaths(
143: RuntimeMemoryCCTNode rootNode, int currentEpoch) {
144: int[] nObjAndAge = new int[2];
145: calculateNObjAndAge(rootNode, currentEpoch, nObjAndAge);
146:
147: if (nObjAndAge[0] == 0) {
148: return 0.0f; // Zero live objects - zero age
149: }
150:
151: return (float) ((double) nObjAndAge[1] / (double) nObjAndAge[0]);
152: }
153:
154: /**
155: * Works with epoch
156: */
157: public int calculateTotalNLiveObjects() {
158: if (epochAndNLiveObjects == null) {
159: return 0;
160: }
161:
162: int res = 0;
163: int row = 0;
164: int len = epochAndNLiveObjects[0].length;
165:
166: while ((row < len) && (epochAndNLiveObjects[0][row] != -1)) {
167: res += epochAndNLiveObjects[1][row];
168: row++;
169: }
170:
171: return res;
172: }
173:
174: public static int calculateTotalNumberOfSurvGensForAllPaths(
175: RuntimeMemoryCCTNode rootNode) {
176: SurvGenSet sgSet = new SurvGenSet();
177: calculateTotalNumberOfSurvGens(rootNode, sgSet);
178:
179: return sgSet.getTotalNoOfAges();
180: }
181:
182: public Object clone() {
183: RuntimeObjLivenessTermCCTNode ret = (RuntimeObjLivenessTermCCTNode) super
184: .clone();
185:
186: if (epochAndNLiveObjects == null) {
187: ret.epochAndNLiveObjects = null;
188: } else {
189: int len = epochAndNLiveObjects[0].length;
190: ret.epochAndNLiveObjects = new int[2][len];
191: System.arraycopy(epochAndNLiveObjects[0], 0,
192: ret.epochAndNLiveObjects[0], 0, len);
193: System.arraycopy(epochAndNLiveObjects[1], 0,
194: ret.epochAndNLiveObjects[1], 0, len);
195: }
196:
197: return ret;
198: }
199:
200: public void readFromStream(DataInputStream in) throws IOException {
201: super .readFromStream(in);
202:
203: int len = in.readInt();
204:
205: epochAndNLiveObjects = new int[2][len];
206:
207: for (int i = 0; i < len; i++) {
208: epochAndNLiveObjects[0][i] = in.readInt();
209: epochAndNLiveObjects[1][i] = in.readInt();
210: }
211: }
212:
213: /**
214: * Only called (from ObjLivenessCallGraphBuilder) when processing results during profiled app runtime
215: */
216: public void removeLiveObjectForEpoch(int epoch) {
217: assert (epochAndNLiveObjects != null);
218:
219: int len = epochAndNLiveObjects[0].length;
220: int lenMinusOne = len - 1;
221: int row = lenMinusOne;
222:
223: while ((epochAndNLiveObjects[0][row] != epoch) && (row > 0)) {
224: row--;
225: }
226:
227: if (epochAndNLiveObjects[0][row] != epoch) { // Pathological case. We don't track them - they shouldn't be frequent anyway.
228:
229: return;
230: }
231:
232: epochAndNLiveObjects[1][row]--;
233:
234: if (epochAndNLiveObjects[1][row] == 0) { // Perform array compaction
235:
236: if (row < lenMinusOne) {
237: if (!((row < lenMinusOne) && (epochAndNLiveObjects[0][row + 1] == -1))) { // There are non-empty rows behind this one
238: System.arraycopy(epochAndNLiveObjects[0], row + 1,
239: epochAndNLiveObjects[0], row, lenMinusOne
240: - row);
241: System.arraycopy(epochAndNLiveObjects[1], row + 1,
242: epochAndNLiveObjects[1], row, lenMinusOne
243: - row);
244: } else {
245: epochAndNLiveObjects[0][row] = -1;
246: }
247: }
248:
249: epochAndNLiveObjects[0][lenMinusOne] = -1;
250: epochAndNLiveObjects[1][lenMinusOne] = 0;
251: }
252: }
253:
254: public void writeToStream(DataOutputStream out) throws IOException {
255: super .writeToStream(out);
256:
257: int len = (epochAndNLiveObjects == null) ? 0
258: : epochAndNLiveObjects[0].length;
259: out.writeInt(len);
260:
261: for (int i = 0; i < len; i++) {
262: out.writeInt(epochAndNLiveObjects[0][i]);
263: out.writeInt(epochAndNLiveObjects[1][i]);
264: }
265: }
266:
267: /**
268: * Works with epoch
269: */
270: protected static void calculateNObjAndAge(
271: RuntimeMemoryCCTNode node, int currentEpoch,
272: int[] nObjAndAge) {
273: if (node instanceof RuntimeObjLivenessTermCCTNode) {
274: RuntimeObjLivenessTermCCTNode this Node = (RuntimeObjLivenessTermCCTNode) node;
275: int[][] epochAndNLiveObjects = this Node.epochAndNLiveObjects;
276:
277: if (epochAndNLiveObjects != null) {
278: int row = 0;
279: int len = epochAndNLiveObjects[0].length;
280:
281: while ((row < len)
282: && (epochAndNLiveObjects[0][row] != -1)) {
283: nObjAndAge[0] += epochAndNLiveObjects[1][row]; // Add the number of objects for this epoch
284: nObjAndAge[1] += (epochAndNLiveObjects[1][row] * (currentEpoch - epochAndNLiveObjects[0][row])); // Add their total age
285: row++;
286: }
287: }
288: }
289:
290: if (node.children != null) {
291: if (node.children instanceof RuntimeMemoryCCTNode) {
292: calculateNObjAndAge(
293: (RuntimeMemoryCCTNode) node.children,
294: currentEpoch, nObjAndAge);
295: } else {
296: RuntimeMemoryCCTNode[] ar = (RuntimeMemoryCCTNode[]) node.children;
297:
298: for (int i = 0; i < ar.length; i++) {
299: calculateNObjAndAge(ar[i], currentEpoch, nObjAndAge);
300: }
301: }
302: }
303: }
304:
305: /**
306: * Works with epoch
307: */
308: protected static void calculateTotalNumberOfSurvGens(
309: RuntimeMemoryCCTNode node, SurvGenSet sgSet) {
310: if (node instanceof RuntimeObjLivenessTermCCTNode) {
311: RuntimeObjLivenessTermCCTNode this Node = (RuntimeObjLivenessTermCCTNode) node;
312: int[][] epochAndNLiveObjects = this Node.epochAndNLiveObjects;
313:
314: if (epochAndNLiveObjects != null) {
315: int[] epochs = epochAndNLiveObjects[0];
316: int len = epochs.length;
317:
318: for (int i = 0; i < len; i++) {
319: if (epochs[i] != -1) {
320: sgSet.addAge(epochs[i]);
321: } else {
322: break;
323: }
324: }
325: }
326: }
327:
328: if (node.children != null) {
329: if (node.children instanceof RuntimeMemoryCCTNode) {
330: calculateTotalNumberOfSurvGens(
331: (RuntimeMemoryCCTNode) node.children, sgSet);
332: } else {
333: RuntimeMemoryCCTNode[] ar = (RuntimeMemoryCCTNode[]) node.children;
334:
335: for (int i = 0; i < ar.length; i++) {
336: calculateTotalNumberOfSurvGens(ar[i], sgSet);
337: }
338: }
339: }
340: }
341:
342: /**
343: * Works with epoch
344: */
345: protected void dumpEpochs() {
346: if (epochAndNLiveObjects != null) {
347: int len = epochAndNLiveObjects[0].length;
348:
349: for (int i = 0; i < len; i++) {
350: System.err.println("epoch = "
351: + epochAndNLiveObjects[0][i] + ", objno = "
352: + epochAndNLiveObjects[1][i]); // NOI18N
353: }
354: } else {
355: System.err.println("epoch = null"); // NOI18N
356: }
357: }
358: }
|