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.server;
042:
043: import org.netbeans.lib.profiler.server.system.Classes;
044: import org.netbeans.lib.profiler.server.system.Stacks;
045: import java.util.HashMap;
046: import java.util.Map;
047:
048: /**
049: * This class contains methods and data structures that are common for both modes of memory profiling - object
050: * allocation and object liveness.
051: *
052: * @author Tomas Hurka
053: * @author Misha Dmitriev
054: */
055: public class ProfilerRuntimeMemory extends ProfilerRuntime {
056: //~ Static fields/initializers -----------------------------------------------------------------------------------------------
057:
058: protected static final int MAX_STACK_FRAMES = 100; // Maximum number of stack frames that we can sample
059: protected static final int NO_OF_PROFILER_FRAMES = 3; // Number of our own frames on stack when we take a stack sample
060: protected static int[] allocatedInstancesCount;
061: protected static int allocatedInstArrayLength;
062: protected static short[] allocatedInstThreshold;
063: protected static char[] objectSize;
064: protected static short samplingInterval;
065: protected static int samplingDepth;
066: private static int stackDepth;
067: private static int[] stackFrameIds;
068: private static Map classIdMap;
069:
070: // -------------------------------------- Miscellaneous support routines ------------------------------------------
071: private static long randSeed;
072: private static int bits;
073: private static short samplingIntervalBase;
074:
075: //~ Methods ------------------------------------------------------------------------------------------------------------------
076:
077: public static void setAllocatedInstancesCountArray(int[] aic) {
078: allocatedInstancesCount = aic;
079:
080: if (aic == null) {
081: allocatedInstThreshold = null;
082: objectSize = null;
083: stackFrameIds = null;
084: Stacks.clearNativeStackFrameBuffer();
085:
086: return;
087: } else if (allocatedInstArrayLength < aic.length) {
088: short[] oldThresh = (allocatedInstThreshold != null) ? allocatedInstThreshold
089: : null;
090: allocatedInstThreshold = new short[aic.length];
091:
092: if (oldThresh != null) {
093: System.arraycopy(oldThresh, 0, allocatedInstThreshold,
094: 0, allocatedInstArrayLength);
095: }
096:
097: char[] oldObjectSize = (objectSize != null) ? objectSize
098: : null;
099: objectSize = new char[aic.length];
100:
101: if (oldObjectSize != null) {
102: System.arraycopy(oldObjectSize, 0, objectSize, 0,
103: allocatedInstArrayLength);
104: }
105:
106: allocatedInstArrayLength = aic.length;
107: }
108: }
109:
110: /** Negative parameter means the depth is not limited. val == 0 means we should not take stack samples */
111: public static void setSamplingDepth(int val) {
112: if (val < 0) {
113: val = MAX_STACK_FRAMES;
114: } else if (val > 0) {
115: val += NO_OF_PROFILER_FRAMES; // Top frames are always our own methods
116:
117: if (val > MAX_STACK_FRAMES) {
118: val = MAX_STACK_FRAMES;
119: }
120: } else {
121: stackDepth = 0;
122: }
123:
124: samplingDepth = val;
125: }
126:
127: public static void setSamplingInterval(short val) {
128: samplingInterval = val;
129: initRandomGenerator();
130: }
131:
132: /** Memory profiling-specific reset collectors functionality. */
133: public static void resetProfilerCollectors(int instrType) {
134: if (allocatedInstancesCount != null) {
135: for (int i = 0; i < allocatedInstancesCount.length; i++) {
136: allocatedInstancesCount[i] = 0;
137: allocatedInstThreshold[i] = 0;
138: }
139: }
140:
141: if (instrType == INSTR_OBJECT_LIVENESS) {
142: ProfilerRuntimeObjLiveness.resetProfilerCollectors();
143: }
144:
145: classIdMap = new HashMap();
146: }
147:
148: public static void traceVMObjectAlloc(Object instance, Class clazz) {
149: if ((classIdMap == null)
150: || ThreadInfo.profilingSuspended()
151: || ThreadInfo.isProfilerServerThread(Thread
152: .currentThread())) {
153: // Avoid counting objects allocated by our own agent threads, or by this method's callees
154: return;
155: }
156:
157: ThreadInfo ti = ThreadInfo.getThreadInfo();
158: String className;
159: String classNameId;
160: int classLoaderId;
161: Integer classIdInt;
162: char classId;
163: boolean isObjectLiveness;
164:
165: if (ti.inProfilingRuntimeMethod > 0) {
166: return;
167: }
168:
169: if (!ti.isInitialized()) {
170: ti.initialize(true);
171: }
172:
173: ti.inProfilingRuntimeMethod++;
174: className = clazz.getName();
175: classLoaderId = ClassLoaderManager.registerLoader(clazz);
176: classNameId = new StringBuffer(className).append('#').append(
177: classLoaderId).toString();
178: classIdInt = (Integer) classIdMap.get(classNameId);
179:
180: if (classIdInt == null) {
181: int newClassId = externalActionsHandler
182: .handleFirstTimeVMObjectAlloc(className,
183: classLoaderId);
184:
185: if (newClassId != -1) {
186: classIdInt = new Integer(newClassId);
187: classIdMap.put(classNameId, classIdInt);
188: } else {
189: // System.err.println("*** JFluid warning: Invalid classId for class:"+classNameId);
190: ti.inProfilingRuntimeMethod--;
191:
192: return;
193: }
194: }
195:
196: isObjectLiveness = ProfilerInterface.getCurrentInstrType() == INSTR_OBJECT_LIVENESS;
197: classId = (char) classIdInt.intValue();
198: ti.inProfilingRuntimeMethod--;
199:
200: if (isObjectLiveness) {
201: ProfilerRuntimeObjLiveness.traceObjAlloc(instance, classId);
202: } else {
203: ProfilerRuntimeObjAlloc.traceObjAlloc(instance, classId);
204: }
205: }
206:
207: // ------------------------------------------ Stack trace obtaining -----------------------------------------------
208:
209: /** This is used in Object Allocation profiling mode */
210: protected static synchronized void getAndSendCurrentStackTrace(
211: char classId, long objSize) {
212: if (eventBuffer == null) {
213: return; // Chances are that instrumentation has been removed while we were executing instrumentation code
214: }
215:
216: synchronized (eventBuffer) { // Note that we have to use synchronization here due to the static stackFrameIds[] array
217:
218: if (samplingDepth != 0) {
219: stackDepth = Stacks.getCurrentStackFrameIds(Thread
220: .currentThread(), samplingDepth, stackFrameIds);
221: }
222:
223: writeObjAllocStackTraceEvent(classId, objSize);
224: }
225: }
226:
227: /** This is used in Object Liveness profiling mode */
228: protected static synchronized void getAndSendCurrentStackTrace(
229: char classId, char epoch, int objCount, long objSize) {
230: if (eventBuffer == null) {
231: return; // Chances are that instrumentation has been removed while we were executing instrumentation code
232: }
233:
234: synchronized (eventBuffer) { // Note that we have to use synchronization here due to the static stackFrameIds[] array
235:
236: if (samplingDepth != 0) {
237: stackDepth = Stacks.getCurrentStackFrameIds(Thread
238: .currentThread(), samplingDepth, stackFrameIds);
239: }
240:
241: writeObjLivenessStackTraceEvent(classId, epoch, objCount,
242: objSize);
243: }
244: }
245:
246: protected static long getCachedObjectSize(char classId,
247: Object object) {
248: long objSize = objectSize[classId];
249:
250: if (objSize <= 1) { // An array (variable size, value 1) or cached size unset (value 0)
251:
252: if (object == null) { // Should not happen, this is a debugging/critical error statement
253: System.err
254: .println("*** JFluid critical error: received null object for classId = "
255: + (int) classId
256: + " in getCachedObjectSize"); // NOI18N
257: Thread.dumpStack();
258: System.err
259: .println("*** End JFluid critical error message ---------------------------"); // NOI18N
260: }
261:
262: if (objSize == 0) { // Size not determined yet
263: objSize = Classes.getObjectSize(object);
264:
265: if (object.getClass().isArray() || (objSize > 0xFFFF)) {
266: objectSize[classId] = 1; // Size will be determined separately every time
267: } else {
268: objectSize[classId] = (char) objSize; // Size will be used for all objects of this class
269: }
270: } else {
271: objSize = Classes.getObjectSize(object);
272: }
273: }
274:
275: return objSize;
276: }
277:
278: protected static void clearDataStructures() {
279: ProfilerRuntime.clearDataStructures();
280: allocatedInstancesCount = null;
281: stackFrameIds = null;
282: Stacks.clearNativeStackFrameBuffer();
283: }
284:
285: protected static void createNewDataStructures() {
286: ProfilerRuntime.createNewDataStructures();
287: stackFrameIds = new int[MAX_STACK_FRAMES];
288: Stacks.createNativeStackFrameBuffer(MAX_STACK_FRAMES);
289: classIdMap = new HashMap();
290: }
291:
292: protected static void enableProfiling(boolean v) {
293: // Doesn't call createNewDataStructures() or clearDataStructures() since this is an "abstract" class
294: }
295:
296: protected static void initRandomGenerator() {
297: randSeed = System.currentTimeMillis();
298:
299: if (samplingInterval == 1) {
300: return;
301: } else if ((samplingInterval == 2) || (samplingInterval == 3)) {
302: bits = 1;
303: samplingIntervalBase = (short) (samplingInterval - 1);
304:
305: return;
306: } else {
307: bits = 1;
308:
309: int val = 1; // That should be the nearest power of two smaller than samplingInterval
310:
311: while (true) {
312: int newVal = val << 1;
313:
314: if (newVal < samplingInterval) {
315: bits++;
316: val = newVal;
317: } else {
318: break;
319: }
320: }
321:
322: // Set samplingIntervalBase to (samplingInterval - val/2), and make bits correspond to val/2
323: samplingIntervalBase = (short) (samplingInterval - (val >> 1));
324: bits -= 1;
325: }
326: }
327:
328: protected static short nextRandomizedInterval() {
329: if (samplingInterval == 1) {
330: return 1;
331: }
332:
333: // This is copied from 'int java.util.Random.next(int bits)'
334: randSeed = ((randSeed * 0x5DEECE66DL) + 0xBL)
335: & ((1L << 48) - 1);
336:
337: return (short) (samplingIntervalBase + ((int) (randSeed >>> (48 - bits))));
338: }
339:
340: // ---------------------------------------- Writing profiler events -----------------------------------------
341:
342: /** Note that there is no synchronized(eventBuffer) in this method, since synchronization is already required by its callers */
343: protected static void writeObjAllocStackTraceEvent(char classId,
344: long objSize) {
345: if (eventBuffer == null) {
346: return; // Instrumentation removal happened when we were in instrumentation
347: }
348:
349: if (stackDepth != 0) {
350: stackDepth -= NO_OF_PROFILER_FRAMES; // Top frames are our own methods
351: }
352:
353: if (globalEvBufPos == 0) {
354: ProfilerServer.notifyClientOnResultsAvailability();
355: }
356:
357: int curPos = globalEvBufPos;
358:
359: if ((curPos + 16 + (stackDepth * 4)) > globalEvBufPosThreshold) { // Dump the buffer
360: externalActionsHandler.handleEventBufferDump(eventBuffer,
361: 0, curPos);
362: curPos = 0;
363: }
364:
365: eventBuffer[curPos++] = OBJ_ALLOC_STACK_TRACE;
366: eventBuffer[curPos++] = (byte) ((classId >> 8) & 0xFF);
367: eventBuffer[curPos++] = (byte) ((classId) & 0xFF);
368:
369: eventBuffer[curPos++] = (byte) ((objSize >> 32) & 0xFF);
370: eventBuffer[curPos++] = (byte) ((objSize >> 24) & 0xFF);
371: eventBuffer[curPos++] = (byte) ((objSize >> 16) & 0xFF);
372: eventBuffer[curPos++] = (byte) ((objSize >> 8) & 0xFF);
373: eventBuffer[curPos++] = (byte) (objSize & 0xFF);
374:
375: curPos = writeStack(curPos);
376: globalEvBufPos = curPos;
377: }
378:
379: protected static void writeObjGCEvent(long objectId) {
380: if (eventBuffer == null) {
381: return; // Instrumentation removal happened when we were in instrumentation
382: }
383:
384: synchronized (eventBuffer) {
385: int curPos = globalEvBufPos;
386:
387: if (curPos > globalEvBufPosThreshold) { // Dump the buffer
388: externalActionsHandler.handleEventBufferDump(
389: eventBuffer, 0, curPos);
390: curPos = 0;
391: }
392:
393: eventBuffer[curPos++] = OBJ_GC_HAPPENED;
394: eventBuffer[curPos++] = (byte) ((objectId >> 56) & 0xFF);
395: eventBuffer[curPos++] = (byte) ((objectId >> 48) & 0xFF);
396: eventBuffer[curPos++] = (byte) ((objectId >> 40) & 0xFF);
397: eventBuffer[curPos++] = (byte) ((objectId >> 32) & 0xFF);
398: eventBuffer[curPos++] = (byte) ((objectId >> 24) & 0xFF);
399: eventBuffer[curPos++] = (byte) ((objectId >> 16) & 0xFF);
400: eventBuffer[curPos++] = (byte) ((objectId >> 8) & 0xFF);
401: eventBuffer[curPos++] = (byte) (objectId & 0xFF);
402: globalEvBufPos = curPos;
403: }
404: }
405:
406: /** Note that there is no synchronized(eventBuffer) in this method, since synchronization is already required by its callers */
407: protected static void writeObjLivenessStackTraceEvent(char classId,
408: char epoch, int objCount, long objSize) {
409: if (eventBuffer == null) {
410: return; // Instrumentation removal happened when we were in instrumentation
411: }
412:
413: if (stackDepth != 0) {
414: stackDepth -= NO_OF_PROFILER_FRAMES; // Top 4 frames are our own methods
415: }
416:
417: if (globalEvBufPos == 0) {
418: ProfilerServer.notifyClientOnResultsAvailability();
419: }
420:
421: int curPos = globalEvBufPos;
422:
423: if ((curPos + 24 + (stackDepth * 4)) > globalEvBufPosThreshold) { // Dump the buffer
424: externalActionsHandler.handleEventBufferDump(eventBuffer,
425: 0, curPos);
426: curPos = 0;
427: }
428:
429: eventBuffer[curPos++] = OBJ_LIVENESS_STACK_TRACE;
430: eventBuffer[curPos++] = (byte) ((classId >> 8) & 0xFF);
431: eventBuffer[curPos++] = (byte) ((classId) & 0xFF);
432: eventBuffer[curPos++] = (byte) ((epoch >> 8) & 0xFF);
433: eventBuffer[curPos++] = (byte) ((epoch) & 0xFF);
434: eventBuffer[curPos++] = (byte) ((objCount >> 24) & 0xFF);
435: eventBuffer[curPos++] = (byte) ((objCount >> 16) & 0xFF);
436: eventBuffer[curPos++] = (byte) ((objCount >> 8) & 0xFF);
437: eventBuffer[curPos++] = (byte) ((objCount) & 0xFF);
438:
439: eventBuffer[curPos++] = (byte) ((objSize >> 32) & 0xFF);
440: eventBuffer[curPos++] = (byte) ((objSize >> 24) & 0xFF);
441: eventBuffer[curPos++] = (byte) ((objSize >> 16) & 0xFF);
442: eventBuffer[curPos++] = (byte) ((objSize >> 8) & 0xFF);
443: eventBuffer[curPos++] = (byte) (objSize & 0xFF);
444:
445: curPos = writeStack(curPos);
446: globalEvBufPos = curPos;
447: }
448:
449: private static int writeStack(int curPos) {
450: eventBuffer[curPos++] = (byte) ((stackDepth >> 16) & 0xFF);
451: eventBuffer[curPos++] = (byte) ((stackDepth >> 8) & 0xFF);
452: eventBuffer[curPos++] = (byte) ((stackDepth) & 0xFF);
453:
454: /// A variant when we send non-reversed call graph
455: //int base = depth + NO_OF_PROFILER_FRAMES - 1;
456: //for (int i = 0; i < depth; i++) {
457: // eventBuffer[curPos++] = (char) ((stackFrameIds[base-i] >> 16) & 0xFFFF);
458: // eventBuffer[curPos++] = (char) ((stackFrameIds[base-i]) & 0xFFFF);
459: //}
460: int frameIdx = NO_OF_PROFILER_FRAMES;
461:
462: for (int i = 0; i < stackDepth; i++) {
463: eventBuffer[curPos++] = (byte) ((stackFrameIds[frameIdx] >> 24) & 0xFF);
464: eventBuffer[curPos++] = (byte) ((stackFrameIds[frameIdx] >> 16) & 0xFF);
465: eventBuffer[curPos++] = (byte) ((stackFrameIds[frameIdx] >> 8) & 0xFF);
466: eventBuffer[curPos++] = (byte) ((stackFrameIds[frameIdx]) & 0xFF);
467: frameIdx++;
468: }
469:
470: return curPos;
471: }
472: }
|