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.ProfilingSessionStatus;
044: import org.netbeans.lib.profiler.results.ResultsSnapshot;
045: import java.io.DataInputStream;
046: import java.io.DataOutputStream;
047: import java.io.IOException;
048: import java.text.MessageFormat;
049: import java.util.HashMap;
050: import java.util.Map;
051: import java.util.ResourceBundle;
052: import java.util.logging.Level;
053:
054: /**
055: * Management of the CPU profiling results snapshot.
056: * A CPU snapshot is created from the runtime CCTs for all profiled threads, that are provided by CPUCallGraphBuilder.
057: * Initially it contains only these same (logically) CCTs (in instances of CPUCCTContainer), but in compact "flattened"
058: * format that takes less space in memory, and represented in microseconds, cleansed time.
059: * When the user starts to open CCT nodes for the given thread, the relevant objects (PrestimeCPUCCTNodes) are generated lazily
060: * for touched nodes out of the initial compact representation. This is done via a PrestimeCPUCCTNode object keeping a pointer
061: * into the compact tree representation where its data is located, from which the data for its children can be located, etc.
062: * When the user requests data in different "view" (switching say from method-level to class- (or package-) level aggregation),
063: * the relevant compact representation for all threads is generated out of the initial compact representation.
064: * <p/>
065: * Has an API for
066: * - returning the root of the all-threads CCT. The node objects themselves take care of lazy construction of sub-nodes
067: * out of the compact representation, when needed
068: * - generation of the accumulated time per method (flat profile) out of the CCT.
069: *
070: * @author Misha Dmitriev
071: */
072: public class CPUResultsSnapshot extends ResultsSnapshot {
073: //~ Inner Classes ------------------------------------------------------------------------------------------------------------
074:
075: /**
076: * This exception just indicates that snapshot can't be created because no data is available
077: */
078: public static class NoDataAvailableException extends Exception {
079: }
080:
081: //~ Static fields/initializers -----------------------------------------------------------------------------------------------
082:
083: // -----
084: // I18N String constants
085: private static final ResourceBundle messages = ResourceBundle
086: .getBundle("org.netbeans.lib.profiler.results.cpu.Bundle"); // NOI18N
087: private static final String CPU_MSG = messages
088: .getString("CPUResultsSnapshot_CpuMsg"); // NOI18N
089: // -----
090:
091: // Views-related stuff
092: public static final int METHOD_LEVEL_VIEW = 0;
093: public static final int CLASS_LEVEL_VIEW = 1;
094: public static final int PACKAGE_LEVEL_VIEW = 2;
095:
096: //~ Instance fields ----------------------------------------------------------------------------------------------------------
097:
098: private Map threadIdMap;
099: private CPUCCTContainer[] allThreadsMergedCCTContainers; // [method|class|package aggregation level] -> CPUCCTContainer Per-view representation of all threads merged CCT containers
100:
101: // <class name>,<method name>,<method signature> triplets corresponding to methodIds in the profiling results
102: // The 0th entry is reserved for a special "Threads" node
103: // instrMethodClasses consists of 3 arrays, each per different "view"
104: private String[][] instrMethodClassesViews;
105: private String[] instrMethodNames;
106: private String[] instrMethodSignatures;
107: private PrestimeCPUCCTNode[] rootNode; // Per-view root nodes
108: private CPUCCTContainer[][] threadCCTContainers; // [method|class|package aggregation level][0-nThreads] -> CPUCCTContainer
109: private boolean collectingTwoTimeStamps;
110: private boolean sortNodesOrder;
111:
112: // Number of instrumented methods - may be smaller than the size of the above arrays
113: private int nInstrMethods;
114:
115: // Remembered sorting parameters for CCT nodes
116: private int sortNodesBy;
117:
118: //~ Constructors -------------------------------------------------------------------------------------------------------------
119:
120: public CPUResultsSnapshot() { // No-arg constructor needed for above serialization methods to work
121: super ();
122: threadIdMap = new HashMap();
123: }
124:
125: public CPUResultsSnapshot(long beginTime, long timeTaken,
126: CPUCCTProvider cctProvider, ProfilingSessionStatus status)
127: throws NoDataAvailableException {
128: super (beginTime, timeTaken);
129: status.beginTrans(false);
130:
131: try {
132: collectingTwoTimeStamps = status.collectingTwoTimeStamps();
133:
134: instrMethodClassesViews = new String[3][];
135: instrMethodClassesViews[METHOD_LEVEL_VIEW] = status
136: .getInstrMethodClasses();
137: instrMethodNames = status.getInstrMethodNames();
138: instrMethodSignatures = status.getInstrMethodSignatures();
139: nInstrMethods = status.getNInstrMethods();
140: } finally {
141: status.endTrans();
142: }
143:
144: // Generate individual CCT containers for all threads in CPUCallGraphBuilder.
145: CPUCCTContainer[] methodLevelCCTs = cctProvider
146: .createPresentationCCTs(this );
147:
148: if (methodLevelCCTs == null) {
149: throw new NoDataAvailableException();
150: }
151:
152: int len = methodLevelCCTs.length;
153:
154: if (len == 0) {
155: throw new NoDataAvailableException();
156: }
157:
158: threadIdMap = new HashMap(methodLevelCCTs.length);
159:
160: for (int i = 0; i < methodLevelCCTs.length; i++) {
161: threadIdMap.put(Integer
162: .valueOf(methodLevelCCTs[i].threadId), Integer
163: .valueOf(i));
164: }
165:
166: threadCCTContainers = new CPUCCTContainer[3][];
167: threadCCTContainers[METHOD_LEVEL_VIEW] = methodLevelCCTs;
168:
169: allThreadsMergedCCTContainers = new CPUCCTContainer[3];
170: rootNode = new PrestimeCPUCCTNode[3];
171: rootNode[METHOD_LEVEL_VIEW] = createRootNodeForAllThreads(METHOD_LEVEL_VIEW);
172:
173: if (LOGGER.isLoggable(Level.FINEST)) {
174: debugValues();
175: }
176: }
177:
178: //~ Methods ------------------------------------------------------------------------------------------------------------------
179:
180: public boolean isCollectingTwoTimeStamps() {
181: return collectingTwoTimeStamps;
182: }
183:
184: public CPUCCTContainer getContainerForThread(int threadId, int view) {
185: if (threadCCTContainers[view] == null) {
186: generateDataForView(view);
187: }
188:
189: return threadCCTContainers[view][getContainerIdForThreadId(threadId)];
190: }
191:
192: public FlatProfileContainer getFlatProfile(int threadId, int view) {
193: if (threadCCTContainers[view] == null) {
194: generateDataForView(view);
195: }
196:
197: if (threadId != -1) {
198: return threadCCTContainers[view][getContainerIdForThreadId(threadId)]
199: .getFlatProfile();
200: } else {
201: return allThreadsMergedCCTContainers[view].getFlatProfile();
202: }
203: }
204:
205: // -- Views-related code
206: public String[] getInstrMethodClasses(int view) {
207: return instrMethodClassesViews[view];
208: }
209:
210: public String[] getInstrMethodNames() {
211: return instrMethodNames;
212: }
213:
214: public String[] getInstrMethodSignatures() {
215: return instrMethodSignatures;
216: }
217:
218: public String[] getMethodClassNameAndSig(int methodId, int view) {
219: if (view == METHOD_LEVEL_VIEW) {
220: return new String[] {
221: instrMethodClassesViews[METHOD_LEVEL_VIEW][methodId],
222: instrMethodNames[methodId],
223: instrMethodSignatures[methodId] };
224: } else {
225: return new String[] {
226: instrMethodClassesViews[view][methodId], null, null };
227: }
228: }
229:
230: public int getNInstrMethods() {
231: return nInstrMethods;
232: }
233:
234: public int getNThreads() {
235: return threadCCTContainers[METHOD_LEVEL_VIEW].length;
236: }
237:
238: public PrestimeCPUCCTNode getReverseCCT(int threadId, int methodId,
239: int view) {
240: if (threadCCTContainers[view] == null) {
241: generateDataForView(view);
242: }
243:
244: if (threadId >= 0) {
245: return threadCCTContainers[view][getContainerIdForThreadId(threadId)]
246: .getReverseCCT(methodId);
247: } else if (threadId == -1) {
248: return allThreadsMergedCCTContainers[view]
249: .getReverseCCT(methodId);
250: } else {
251: throw new IllegalArgumentException(
252: "!!! Cannot generate reverse CCT for threadId = "
253: + threadId); // NOI18N
254: }
255: }
256:
257: public PrestimeCPUCCTNode getRootNode(int view) {
258: if (threadCCTContainers[view] == null) {
259: generateDataForView(view);
260: }
261:
262: return rootNode[view];
263: }
264:
265: public int getSortBy() {
266: return sortNodesBy;
267: }
268:
269: public boolean getSortOrder() {
270: return sortNodesOrder;
271: }
272:
273: public int[] getThreadIds() {
274: int[] ret = new int[threadCCTContainers[METHOD_LEVEL_VIEW].length];
275:
276: for (int i = 0; i < threadCCTContainers[METHOD_LEVEL_VIEW].length; i++) {
277: ret[i] = threadCCTContainers[METHOD_LEVEL_VIEW][i].threadId;
278: }
279:
280: return ret;
281: }
282:
283: public String getThreadNameForId(int threadId) {
284: return getThreadNames()[getContainerIdForThreadId(threadId)];
285: }
286:
287: public String[] getThreadNames() {
288: String[] ret = new String[threadCCTContainers[METHOD_LEVEL_VIEW].length];
289:
290: for (int i = 0; i < threadCCTContainers[METHOD_LEVEL_VIEW].length; i++) {
291: ret[i] = threadCCTContainers[METHOD_LEVEL_VIEW][i].threadName;
292: }
293:
294: return ret;
295: }
296:
297: public void readFromStream(DataInputStream in) throws IOException {
298: super .readFromStream(in);
299: collectingTwoTimeStamps = in.readBoolean();
300:
301: nInstrMethods = in.readInt();
302: instrMethodClassesViews = new String[3][];
303:
304: String[] classNames = new String[nInstrMethods];
305: instrMethodClassesViews[METHOD_LEVEL_VIEW] = classNames;
306: instrMethodNames = new String[nInstrMethods];
307: instrMethodSignatures = new String[nInstrMethods];
308:
309: for (int i = 0; i < nInstrMethods; i++) {
310: classNames[i] = in.readUTF();
311: instrMethodNames[i] = in.readUTF();
312: instrMethodSignatures[i] = in.readUTF();
313: }
314:
315: int nThreads = in.readInt();
316: threadCCTContainers = new CPUCCTContainer[3][];
317:
318: CPUCCTContainer[] containers = new CPUCCTContainer[nThreads];
319: threadCCTContainers[METHOD_LEVEL_VIEW] = containers;
320:
321: for (int i = 0; i < nThreads; i++) {
322: containers[i] = new CPUCCTContainer(this );
323: containers[i].readFromStream(in);
324: threadIdMap.put(Integer.valueOf(containers[i].threadId),
325: Integer.valueOf(i));
326: }
327:
328: allThreadsMergedCCTContainers = new CPUCCTContainer[3];
329: rootNode = new PrestimeCPUCCTNode[3];
330: rootNode[METHOD_LEVEL_VIEW] = createRootNodeForAllThreads(METHOD_LEVEL_VIEW);
331:
332: if (LOGGER.isLoggable(Level.FINEST)) {
333: debugValues();
334: }
335: }
336:
337: // -- Methods for saving/retrieving sorting parameters
338: public void saveSortParams(int sortBy, boolean sortOrder) {
339: sortNodesBy = sortBy;
340: sortNodesOrder = sortOrder;
341: }
342:
343: public String toString() {
344: return MessageFormat.format(CPU_MSG, new Object[] { super
345: .toString() });
346: }
347:
348: //---- Serialization support
349: public void writeToStream(DataOutputStream out) throws IOException {
350: super .writeToStream(out);
351: out.writeBoolean(collectingTwoTimeStamps);
352:
353: out.writeInt(nInstrMethods);
354:
355: String[] classNames = instrMethodClassesViews[METHOD_LEVEL_VIEW];
356:
357: for (int i = 0; i < nInstrMethods; i++) {
358: out.writeUTF(classNames[i]);
359: out.writeUTF(instrMethodNames[i]);
360: out.writeUTF(instrMethodSignatures[i]);
361: }
362:
363: CPUCCTContainer[] containers = threadCCTContainers[METHOD_LEVEL_VIEW];
364: int nThreads = containers.length;
365: out.writeInt(nThreads);
366:
367: for (int i = 0; i < nThreads; i++) {
368: containers[i].writeToStream(out);
369: }
370: }
371:
372: private int getContainerIdForThreadId(int threadId) {
373: Integer tid = Integer.valueOf(threadId);
374: Integer cId = null;
375:
376: if (threadIdMap.containsKey(tid)) {
377: cId = (Integer) threadIdMap.get(tid);
378: }
379:
380: return (cId != null) ? cId.intValue() : 0;
381: }
382:
383: private PrestimeCPUCCTNode createRootNodeForAllThreads(int view) {
384: CPUCCTContainer[] ccts = threadCCTContainers[view];
385: int len = ccts.length;
386: PrestimeCPUCCTNode[] threadNodes = new PrestimeCPUCCTNode[len];
387:
388: for (int i = 0; i < len; i++) {
389: PrestimeCPUCCTNode tRootNode = ccts[i].getRootNode();
390:
391: if (!tRootNode.isThreadNode()) {
392: threadNodes[i] = new PrestimeCPUCCTNodeBacked(ccts[i],
393: new PrestimeCPUCCTNode[] { tRootNode });
394: } else {
395: threadNodes[i] = tRootNode;
396: }
397: }
398:
399: allThreadsMergedCCTContainers[view] = new AllThreadsMergedCPUCCTContainer(
400: this , threadNodes, view);
401:
402: return allThreadsMergedCCTContainers[view].getRootNode();
403: }
404:
405: private void debugValues() {
406: LOGGER.finest("collectingTwoTimeStamps: "
407: + collectingTwoTimeStamps); // NOI18N
408: LOGGER.finest("threadCCTContainers.length: "
409: + debugLength(threadCCTContainers)); // NOI18N
410: LOGGER.finest("allThreadsMergedCCTContainers.length: "
411: + debugLength(allThreadsMergedCCTContainers) // NOI18N
412: );
413: LOGGER.finest("rootNode.length: " + debugLength(rootNode)); // NOI18N
414: LOGGER.finest("instrMethodClassesViews.length: "
415: + debugLength(instrMethodClassesViews) // NOI18N
416: );
417: LOGGER.finest("instrMethodNames.length: "
418: + debugLength(instrMethodNames)); // NOI18N
419: LOGGER.finest("instrMethodSignatures.length: "
420: + debugLength(instrMethodSignatures) // NOI18N
421: );
422: LOGGER.finest("nInstrMethods: " + nInstrMethods); // NOI18N
423: LOGGER.finest("sortNodesBy: " + sortNodesBy); // NOI18N
424: LOGGER.finest("sortNodesOrder: " + sortNodesOrder); // NOI18N
425: }
426:
427: /**
428: * callChainType code can be:
429: * 1 - call chains with invocations and timings;
430: * 2 - call chains with invocation numbers only;
431: * 3 - call chains with no timings and/or invocation numbers.
432: * /
433: * public StringBuffer getResultsInCSVFormat(int callChainTypeCode, ExportDataDumper dataDumper) {
434: * //!!! Need to copy here a whole lot of stuff from CPUCCTManager
435: *
436: * return null;
437: * }
438: */
439:
440: //------------------------------------ Private implementation ----------------------------------------------------
441: /**
442: * If data for the provided view have not yet been initialized, it creates threadCCTContainers[view],
443: * rootNode[view] and allThreadsMergedCCTContainers[view].
444: *
445: * @param view either of METHOD_LEVEL_VIEW, CLASS_LEVEL_VIEW or PACKAGE_LEVEL_VIEW
446: */
447: private void generateDataForView(int view) {
448: if (threadCCTContainers[view] == null) {
449: MethodIdMap methodIdMap = new MethodIdMap(
450: instrMethodClassesViews[METHOD_LEVEL_VIEW],
451: nInstrMethods, view);
452: int len = threadCCTContainers[METHOD_LEVEL_VIEW].length;
453: threadCCTContainers[view] = new CPUCCTContainer[len];
454:
455: for (int i = 0; i < len; i++) {
456: threadCCTContainers[view][i] = new CPUCCTClassContainer(
457: threadCCTContainers[METHOD_LEVEL_VIEW][i],
458: methodIdMap, view);
459: }
460:
461: rootNode[view] = createRootNodeForAllThreads(view);
462: instrMethodClassesViews[view] = methodIdMap
463: .getInstrClassesOrPackages();
464: }
465: }
466: }
|