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.server;
0042:
0043: import org.netbeans.lib.profiler.global.CommonConstants;
0044: import org.netbeans.lib.profiler.global.Platform;
0045: import org.netbeans.lib.profiler.global.ProfilingPointServerHandler;
0046: import org.netbeans.lib.profiler.global.ProfilingSessionStatus;
0047: import org.netbeans.lib.profiler.global.TransactionalSupport;
0048: import org.netbeans.lib.profiler.server.system.*;
0049: import org.netbeans.lib.profiler.wireprotocol.*;
0050: import java.io.File;
0051: import java.io.FileOutputStream;
0052: import java.io.IOException;
0053: import java.lang.reflect.Method;
0054: import java.text.MessageFormat;
0055: import java.util.ResourceBundle;
0056: import java.util.WeakHashMap;
0057:
0058: /**
0059: * Main interface to the target VM side introspection functionality.
0060: *
0061: * @author Tomas Hurka
0062: * @author Misha Dmitriev
0063: * @author Adrian Mos
0064: * @author Ian Formanek
0065: */
0066: public class ProfilerInterface implements CommonConstants {
0067: //~ Inner Classes ------------------------------------------------------------------------------------------------------------
0068:
0069: private static class HFIRIThread extends Thread {
0070: //~ Constructors ---------------------------------------------------------------------------------------------------------
0071:
0072: HFIRIThread() {
0073: ThreadInfo.addProfilerServerThread(this );
0074: this .setName(PROFILER_SPECIAL_EXEC_THREAD_NAME + " 1"); // NOI18N
0075: setDaemon(true);
0076: }
0077:
0078: //~ Methods --------------------------------------------------------------------------------------------------------------
0079:
0080: public void run() {
0081: RootClassLoadedCommand cmd = new RootClassLoadedCommand(
0082: new String[] { "*FAKE_CLASS_1*", "*FAKE_CLASS_2*" },
0083: new int[] { 0, 0 }, null, 2, new int[] { -1 }, ""); // NOI18N
0084: profilerServer.sendComplexCmdToClient(cmd);
0085:
0086: InstrumentMethodGroupResponse imgr = (InstrumentMethodGroupResponse) profilerServer
0087: .getLastResponse();
0088: ThreadInfo.removeProfilerServerThread(this );
0089: }
0090: }
0091:
0092: private static class InitiateInstThread extends Thread {
0093: //~ Instance fields ------------------------------------------------------------------------------------------------------
0094:
0095: private InitiateInstrumentationCommand cmd;
0096: private boolean targetAppRunning;
0097:
0098: //~ Constructors ---------------------------------------------------------------------------------------------------------
0099:
0100: InitiateInstThread(InitiateInstrumentationCommand cmd,
0101: boolean targetAppRunning) {
0102: ThreadInfo.addProfilerServerThread(this );
0103: this .setName(PROFILER_SPECIAL_EXEC_THREAD_NAME + " 2"); // NOI18N
0104: this .cmd = cmd;
0105: this .targetAppRunning = targetAppRunning;
0106: }
0107:
0108: //~ Methods --------------------------------------------------------------------------------------------------------------
0109:
0110: public void run() {
0111: // We take a serialClientOperationsLock, then turn class load hook on, to prevent possible class loads, that
0112: // will neither get into loadedClassesArray nor be intercepted properly and reported to client by classLoadHook.
0113: // In other words, classes that are loaded before this point get into loadedClassesArray; classes loaded
0114: // afterwards should be individually intercepted and reported to client.
0115: serialClientOperationsLock.beginTrans(true);
0116:
0117: try {
0118: initInstrumentationThread = Thread.currentThread();
0119:
0120: int instrType = cmd.getInstrType();
0121: setCurrentInstrType(instrType);
0122: rootClassNames = cmd.getRootClassNames();
0123: status.startProfilingPointsActive = cmd
0124: .isStartProfilingPointsActive();
0125:
0126: status.profilingPointIDs = cmd.getProfilingPointIDs();
0127:
0128: String[] handlers = cmd.getProfilingPointHandlers();
0129: String[] infos = cmd.getProfilingPointInfos();
0130: status.profilingPointHandlers = ProfilingPointServerHandler
0131: .getInstances(handlers, infos);
0132: computeRootWildcard();
0133: rootClassLoaded = false;
0134:
0135: // the following code is needed to avoid issue 59660: Remote profiling can cause the agent to hang if CPU
0136: // or Code Fragment profiling is used
0137: // see http://profiler.netbeans.org/issues/show_bug.cgi?id=59660
0138: // and http://profiler.netbeans.org/issues/show_bug.cgi?id=61968
0139: try {
0140: Class.forName("java.util.LinkedHashMap"); // NOI18N
0141: Class
0142: .forName("java.util.LinkedHashMap$LinkedHashIterator"); // NOI18N
0143: Class
0144: .forName("java.util.LinkedHashMap$KeyIterator"); // NOI18N
0145: // for take heap dump
0146:
0147: Class
0148: .forName("java.lang.reflect.InvocationTargetException"); // NOI18N
0149: Class.forName("java.lang.InterruptedException");
0150: } catch (ClassNotFoundException e) {
0151: e.printStackTrace(System.err);
0152: }
0153:
0154: // The following code is needed to enforce native method bind for Thread.sleep before instrumentation, so
0155: // that the NativeMethodBind it can be disabled as first thing in instrumentation
0156: // this is needed as a workaround for JDK bug:
0157: // CR 6318850 Updated P3 hotspot/jvmti RedefineClasses() and NativeMethodBind event crash
0158: try {
0159: Thread.sleep(1);
0160: } catch (InterruptedException e) {
0161: } // ignore
0162:
0163: synchronized (this ) {
0164: try {
0165: wait(1);
0166: } catch (InterruptedException e) {
0167: } // ignore
0168: }
0169:
0170: Classes.enableClassLoadHook();
0171:
0172: boolean instrSpawnedThreads = cmd
0173: .getInstrSpawnedThreads();
0174:
0175: if (targetAppRunning
0176: || hasAnyCoreClassNames(cmd.getRootClassNames())
0177: || instrSpawnedThreads
0178: || (instrType == INSTR_OBJECT_ALLOCATIONS)
0179: || (instrType == INSTR_OBJECT_LIVENESS)) {
0180: getLoadedClasses(); // Init loadedClassesArray
0181:
0182: boolean loadedRootClassesExist = false;
0183:
0184: switch (instrType) {
0185: case INSTR_RECURSIVE_FULL:
0186: case INSTR_RECURSIVE_SAMPLED:
0187: // This will look into loadedClassesArray to check if there are any root classes already loaded
0188: loadedRootClassesExist = instrSpawnedThreads ? true
0189: : checkForLoadedRootClasses();
0190:
0191: break;
0192: case INSTR_CODE_REGION:
0193: loadedRootClassesExist = checkForLoadedRootClasses();
0194:
0195: break;
0196: case INSTR_OBJECT_ALLOCATIONS:
0197: case INSTR_OBJECT_LIVENESS:
0198: loadedRootClassesExist = true;
0199:
0200: break;
0201: }
0202:
0203: if (loadedRootClassesExist) { // Root class(es) has been loaded or none is needed - start
0204: // instrumentation-related operations right away
0205: sendRootClassLoadedCommand(false);
0206:
0207: if (!getAndInstrumentClasses(true)) {
0208: disableProfilerHooks();
0209: }
0210:
0211: rootClassLoaded = true; // See the comment in classLoadHook why it's worth setting rootClassLoaded
0212: // to true after the first instrumentation, not before
0213: }
0214: }
0215:
0216: initInstrumentationThread = null;
0217: } finally {
0218: serialClientOperationsLock.endTrans();
0219: }
0220:
0221: ThreadInfo.removeProfilerServerThread(this );
0222: }
0223:
0224: private static void computeRootWildcard() {
0225: rootClassNameWildcard = new boolean[rootClassNames.length];
0226:
0227: for (int i = 0; i < rootClassNames.length; i++) {
0228: int nameLen = rootClassNames[i].length();
0229: rootClassNameWildcard[i] = (nameLen == 0) // default package wildcard
0230: || (rootClassNames[i].charAt(nameLen - 1) == '.'); // ends with "." // NOI18N
0231: }
0232: }
0233: }
0234:
0235: //~ Static fields/initializers -----------------------------------------------------------------------------------------------
0236:
0237: // -----
0238: // I18N String constants
0239: // !!! Warning - do not use ResourceBundle.getBundle here, won't work in context of direct/dynamic attach !!!
0240: // Default EN messages initialized here, will be replaced by localized messages in static initializer
0241: private static String INTERNAL_ERROR_MSG = "Internal error:\nExpected InstrumentMethodGroupResponse, got response of class {0},\nvalue = {1}\nAll instrumentation will be removed"; // NOI18N
0242: private static String UNEXPECTED_EXCEPTION_MSG = "Unexpected exception caught when trying to instrument classes.\nOriginal exception:\n{0}\nStack trace:\n\n{1}"; // NOI18N
0243: private static String INSTRUMENTATION_SUCCESSFUL_MSG = "Deferred instrumentation performed successfully"; // NOI18N
0244: // -----
0245:
0246: static {
0247: ResourceBundle messages = ProfilerServer
0248: .getProfilerServerResourceBundle();
0249:
0250: if (messages != null) {
0251: INTERNAL_ERROR_MSG = messages
0252: .getString("ProfilerInterface_InternalErrorMsg"); // NOI18N
0253: UNEXPECTED_EXCEPTION_MSG = messages
0254: .getString("ProfilerInterface_UnexpectedExceptionMsg"); // NOI18N
0255: INSTRUMENTATION_SUCCESSFUL_MSG = messages
0256: .getString("ProfilerInterface_InstrumentationSuccessfulMsg"); // NOI18N
0257: }
0258: }
0259:
0260: // TODO [release]: change value to FALSE to remove the print code below entirely by compiler
0261: private static final boolean DEBUG = System
0262: .getProperty("org.netbeans.lib.profiler.server.ProfilerInterface.classLoadHook") != null; // NOI18N
0263:
0264: // The lock used to serialize requests from server to client. May be used outside this class.
0265: public static TransactionalSupport serialClientOperationsLock = new TransactionalSupport();
0266: private static ProfilerServer profilerServer;
0267: private static ProfilingSessionStatus status;
0268: private static EventBufferManager evBufManager;
0269: private static Class[] loadedClassesArray; // Temporary array, used to send all loaded class names to client
0270: // on instrumentation initiation.
0271: private static int[] loadedClassesLoaders; // Ditto, for loaders
0272: private static WeakHashMap reflectMethods; // Cache of methods called using reflection
0273: private static boolean targetAppSuspended = false;
0274: private static boolean instrumentReflection = false;
0275: private static int[] packedArrayOffsets;
0276: private static int nSystemThreads;
0277: private static Thread initInstrumentationThread;
0278: private static String[] rootClassNames;
0279: private static boolean[] rootClassNameWildcard;
0280:
0281: // For statistics
0282: static int nClassLoads;
0283:
0284: // For statistics
0285: static int nFirstMethodInvocations;
0286:
0287: // For statistics
0288: static int nEmptyInstrMethodGroupResponses;
0289: static int nNonEmptyInstrMethodGroupResponses;
0290: static int nSingleMethodInstrMethodGroupResponses;
0291: static int nTotalInstrMethods;
0292: static long totalHotswappingTime;
0293: static long minHotswappingTime = 10000000000L;
0294: static long maxHotswappingTime;
0295: static long clientInstrStartTime;
0296: static long clientInstrTime;
0297: static long clientDataProcStartTime;
0298: static long clientDataProcTime;
0299:
0300: //----------------------------------------- Private implementation --------------------------------------------------
0301: private static boolean rootClassLoaded; // has root class been loaded?
0302:
0303: // The following variable addresses the issue of classLoadHook called for a class, that is already registered as
0304: // loaded, (through getAllLoadedClasses) but is actually initialized only when extendConstantPool() is called on it.
0305: // Initialization would cause classLoadHook invocation on this class, and subsequent "second class load event"
0306: // messages in client (which is just confusing). But this may also cause more subtle deadlock bug due to
0307: // classLoadHook() trying to record adjustTime event, while serialClientOperationsLock is held by an outer invocation
0308: // of classLoadHook() or methodInvokedFirstTime(). To avoid all these problems we use this simple way to avoid
0309: // unnecessary classLoadHook() invocations.
0310: private static volatile Thread instrumentMethodGroupCallThread;
0311:
0312: //~ Methods ------------------------------------------------------------------------------------------------------------------
0313:
0314: public static CodeRegionCPUResultsResponse getCodeRegionCPUResults() {
0315: CodeRegionCPUResultsResponse resp = new CodeRegionCPUResultsResponse(
0316: ProfilerRuntimeCPUCodeRegion.getProfilingResults());
0317:
0318: return resp;
0319: }
0320:
0321: public static void setCurrentInstrType(int type) {
0322: boolean isMemoryProfiling = (type == INSTR_OBJECT_ALLOCATIONS)
0323: || (type == INSTR_OBJECT_LIVENESS);
0324:
0325: status.currentInstrType = type;
0326: Classes.setVMObjectAllocEnabled(isMemoryProfiling);
0327: }
0328:
0329: public static int getCurrentInstrType() {
0330: return status.currentInstrType;
0331: }
0332:
0333: public static ThreadLivenessStatusResponse getCurrentThreadLivenessStatus() {
0334: ThreadLivenessStatusResponse resp = new ThreadLivenessStatusResponse(
0335: ThreadInfo.getCurrentLivenessStatus());
0336:
0337: return resp;
0338: }
0339:
0340: public static void setInstrumentReflection(boolean v) {
0341: if (status.targetAppRunning) {
0342: ProfilerRuntimeCPU
0343: .setJavaLangReflectMethodInvokeInterceptEnabled(v);
0344: } else {
0345: instrumentReflection = v;
0346: }
0347: }
0348:
0349: public static MethodNamesResponse getMethodNamesForJMethodIds(
0350: int[] methodIds) {
0351: int nMethods = methodIds.length;
0352: int len = nMethods * 3;
0353: packedArrayOffsets = new int[len];
0354:
0355: System.gc(); // To avoid as much as possible a GC that happens concurrently while the call below is in progress
0356: // (though I am not sure now it's a real problem)
0357:
0358: byte[] packedData = Stacks.getMethodNamesForJMethodIds(
0359: nMethods, methodIds, packedArrayOffsets);
0360: MethodNamesResponse resp = new MethodNamesResponse(packedData,
0361: packedArrayOffsets);
0362:
0363: return resp;
0364: }
0365:
0366: public static int getNPrerecordedSystemThreads() {
0367: return nSystemThreads;
0368: }
0369:
0370: public static ObjectAllocationResultsResponse getObjectAllocationResults() {
0371: status.beginTrans(false);
0372:
0373: try {
0374: ObjectAllocationResultsResponse resp = new ObjectAllocationResultsResponse(
0375: status.getAllocatedInstancesCount(), status
0376: .getNInstrClasses());
0377:
0378: return resp;
0379: } finally {
0380: status.endTrans();
0381: }
0382: }
0383:
0384: public static void setProfilerServer(ProfilerServer server) {
0385: profilerServer = server;
0386: }
0387:
0388: /**
0389: * This method cleans up the data structures managed by this class, but not by various ProfilerRuntimeXXX classes.
0390: * The latter are cleaned up separately, by the following deactivateInjectedCode() method.
0391: */
0392: public static void clearProfilerDataStructures() {
0393: //loadedClassesArray = null;
0394: //loadedClassesCPLengths = null;
0395: //loadedClassesLoaders = null;
0396: //ClassLoaderManager.reset();
0397: //packedArrayOffsets = null;
0398: reflectMethods = null;
0399:
0400: //evBufManager.freeBufferFile();
0401: }
0402:
0403: public static boolean cpuResultsExist() {
0404: return ProfilerRuntime.profiledTargetAppThreadsExist();
0405: }
0406:
0407: /**
0408: * Deactivate the injected code for the current instrumentation type, and clean up all the supporting data structures
0409: * maintained by the corresponding ProfilerRuntimeXXX class.
0410: */
0411: public static void deactivateInjectedCode() {
0412: int instrType = getCurrentInstrType();
0413:
0414: if (instrType == INSTR_NONE) {
0415: return;
0416: }
0417:
0418: disableProfilerHooks();
0419:
0420: switch (instrType) {
0421: case INSTR_CODE_REGION:
0422: ProfilerRuntimeCPUCodeRegion.enableProfiling(false);
0423:
0424: if (rootClassNames != null) {
0425: rootClassNames = null;
0426: }
0427:
0428: break;
0429: case INSTR_RECURSIVE_FULL:
0430: ProfilerRuntimeCPUFullInstr.enableProfiling(false);
0431: ProfilerRuntimeCPU.setTimerTypes(false, false); // Mainly to clean up microstate accounting on Solaris
0432:
0433: break;
0434: case INSTR_RECURSIVE_SAMPLED:
0435: ProfilerRuntimeCPUSampledInstr.enableProfiling(false);
0436: ProfilerRuntimeCPU.setTimerTypes(false, false);
0437:
0438: break;
0439: case INSTR_OBJECT_ALLOCATIONS:
0440: ProfilerRuntimeObjAlloc.enableProfiling(false);
0441:
0442: break;
0443: case INSTR_OBJECT_LIVENESS:
0444: ProfilerRuntimeObjLiveness.enableProfiling(false);
0445:
0446: break;
0447: }
0448:
0449: status.resetInstrClassAndMethodInfo();
0450: setCurrentInstrType(INSTR_NONE);
0451: }
0452:
0453: public static void disableProfilerHooks() {
0454: Classes.disableClassLoadHook();
0455: ProfilerRuntimeCPU
0456: .setJavaLangReflectMethodInvokeInterceptEnabled(false);
0457: ClassLoaderManager.setNotifyToolAboutUnloadedClasses(false);
0458: }
0459:
0460: public static void dumpExistingResults(boolean live) {
0461: if (!live && (getCurrentInstrType() == INSTR_OBJECT_LIVENESS)
0462: && ProfilerRuntimeObjLiveness.getRunGCOnGetResults()) {
0463: GC.runGC();
0464:
0465: try {
0466: Thread.sleep(500);
0467:
0468: // Give WeakReference collector thread a chance to register some (hopefully most of) object GCs
0469: } catch (Exception ex) {
0470: }
0471:
0472: ;
0473: }
0474:
0475: ProfilerRuntime.dumpEventBuffer();
0476: }
0477:
0478: /**
0479: * This method initializes the internal data structures, and also records the profiler's own thread(s), so that
0480: * they are not affected by our suspend/resume operations. If we run in the normal mode, i.e. the target JVM was
0481: * started by the client, specialThread is the current thread, which will then become the target app main thread.
0482: * It should be excluded from the list of the profiler's own threads. If we run in the attached mode, specialThread
0483: * is the only thread that we can reliably characterize as the profiler's own.
0484: */
0485: public static void initProfilerInterface(
0486: ProfilingSessionStatus status, Thread specialThread) {
0487: Timers.initialize();
0488: Classes.initialize();
0489: GC.initialize();
0490: Stacks.initialize();
0491: Threads.initialize();
0492: HeapDump
0493: .initialize(Platform.getJDKVersionNumber() == Platform.JDK_15);
0494: ClassLoaderManager.initialize(profilerServer);
0495: ClassLoaderManager
0496: .addLoader(ClassLoader.getSystemClassLoader());
0497: reflectMethods = new WeakHashMap();
0498:
0499: evBufManager = new EventBufferManager(profilerServer);
0500: ProfilerInterface.status = status;
0501:
0502: // Check that all profiler's own threads are running, and then record them internally, so that target app threads
0503: // are accounted for properly.
0504: while (!Monitors.monitorThreadsStarted()) {
0505: try {
0506: Thread.sleep(50);
0507: } catch (Exception ex) {
0508: }
0509:
0510: ;
0511: }
0512:
0513: if (status.runningInAttachedMode) {
0514: Threads.recordProfilerOwnThreads(false, specialThread);
0515: nSystemThreads = -1; // Indicates that we really don't know how many of these threads
0516: // are VM-own, or system, threads
0517: } else {
0518: nSystemThreads = Threads.recordProfilerOwnThreads(true,
0519: specialThread);
0520: }
0521:
0522: ProfilerRuntime
0523: .init(new ProfilerRuntime.ExternalActionsHandler() {
0524: public void handleFirstTimeMethodInvoke(int methodId) {
0525: firstTimeMethodInvokeHook(methodId);
0526: }
0527:
0528: public void handleReflectiveInvoke(Method method) {
0529: reflectiveMethodInvokeHook(method);
0530: }
0531:
0532: public int handleFirstTimeVMObjectAlloc(
0533: String className, int classLoaderId) {
0534: return firstTimeVMObjectAlloc(className,
0535: classLoaderId);
0536: }
0537:
0538: public void handleEventBufferDump(
0539: byte[] eventBuffer, int startPos,
0540: int curPtrPos) {
0541: serialClientOperationsLock.beginTrans(true);
0542:
0543: try { // So that this event does not interfere with class
0544: // loads / method invocations
0545: clientDataProcStartTime = Timers
0546: .getCurrentTimeInCounts();
0547: evBufManager.eventBufferDumpHook(
0548: eventBuffer, startPos, curPtrPos);
0549: clientDataProcTime += (Timers
0550: .getCurrentTimeInCounts() - clientDataProcStartTime);
0551: } finally {
0552: serialClientOperationsLock.endTrans();
0553: }
0554: }
0555: });
0556: }
0557:
0558: public static void initiateInstrumentation(
0559: final InitiateInstrumentationCommand cmd,
0560: final boolean targetAppRunning) throws Exception {
0561: int instrType = cmd.getInstrType();
0562: String instrClassName = cmd.getRootClassName();
0563:
0564: if (instrClassName.equals("*FAKE_CLASS_FOR_INTERNAL_TEST*")) { // NOI18N
0565: handleFakeInitRecursiveInstrumentationCommand(); // To initialize certain internal classes, see comments
0566: // in handleFake... method
0567:
0568: return;
0569: }
0570:
0571: switch (instrType) {
0572: case INSTR_RECURSIVE_FULL:
0573: case INSTR_RECURSIVE_SAMPLED:
0574: case INSTR_OBJECT_ALLOCATIONS:
0575: case INSTR_OBJECT_LIVENESS:
0576: evBufManager.openBufferFile(EVENT_BUFFER_SIZE_IN_BYTES);
0577: ProfilerRuntime
0578: .createEventBuffer(EVENT_BUFFER_SIZE_IN_BYTES);
0579: status.resetInstrClassAndMethodInfo();
0580:
0581: if ((instrType == INSTR_OBJECT_ALLOCATIONS)
0582: || (instrType == INSTR_OBJECT_LIVENESS)) {
0583: ClassLoaderManager
0584: .setNotifyToolAboutUnloadedClasses(true);
0585: } else {
0586: ClassLoaderManager
0587: .setNotifyToolAboutUnloadedClasses(false);
0588: }
0589:
0590: break;
0591: case INSTR_CODE_REGION:
0592: ProfilerRuntimeCPUCodeRegion.resetProfilerCollectors();
0593:
0594: break;
0595: }
0596:
0597: // We have to perform the following operations in a separate thread, since they may involve further dialog with
0598: // the tool (client), whereas this thread has to return quickly to send the "OK" response to the tool.
0599: new InitiateInstThread(cmd, targetAppRunning).start();
0600: }
0601:
0602: public static void instrumentMethods(
0603: InstrumentMethodGroupCommand cmd) throws Exception {
0604: if (!cmd.isEmpty()) {
0605: try {
0606: instrumentMethodGroupNow(cmd.getBase());
0607: } catch (Exception ex) {
0608: deactivateInjectedCode();
0609: setCurrentInstrType(INSTR_NONE);
0610: throw ex;
0611: }
0612: }
0613:
0614: setCurrentInstrType(cmd.getInstrType());
0615: }
0616:
0617: public static void resetProfilerCollectors() {
0618: ProfilerRuntime.resetProfilerCollectors(getCurrentInstrType());
0619: reflectMethods = new WeakHashMap(); // So that methods that are possibly holding unreachable classes are
0620: // removed and classes allowed to be GCed
0621: }
0622:
0623: public static void resumeTargetApp() {
0624: if (getCurrentInstrType() == INSTR_RECURSIVE_FULL) {
0625: ProfilerRuntimeCPUFullInstr.resumeActiveTimers();
0626: }
0627:
0628: Threads.resumeTargetAppThreads(null);
0629: targetAppSuspended = false;
0630: }
0631:
0632: public static void suspendTargetApp() {
0633: Threads.suspendTargetAppThreads(null);
0634:
0635: if (getCurrentInstrType() == INSTR_RECURSIVE_FULL) {
0636: ProfilerRuntimeCPUFullInstr.suspendActiveTimers();
0637: }
0638:
0639: targetAppSuspended = true;
0640: }
0641:
0642: private static boolean getAndInstrumentClasses(
0643: boolean rootClassInstrumentation) {
0644: Response r = profilerServer.getLastResponse();
0645:
0646: if (!(r instanceof InstrumentMethodGroupResponse)) { // This is an internal error which, hopefully, has been fixed.
0647:
0648: String msg = MessageFormat.format(INTERNAL_ERROR_MSG,
0649: new Object[] { r.getClass(), r });
0650: deactivateInjectedCode();
0651: profilerServer
0652: .sendComplexCmdToClient(new AsyncMessageCommand(
0653: false, msg));
0654:
0655: return false;
0656: }
0657:
0658: InstrumentMethodGroupResponse imgr = (InstrumentMethodGroupResponse) r;
0659: clientInstrTime += (Timers.getCurrentTimeInCounts() - clientInstrStartTime);
0660:
0661: if (!imgr.isOK()) {
0662: return false;
0663: }
0664:
0665: if (imgr.isEmpty()) {
0666: nEmptyInstrMethodGroupResponses++;
0667:
0668: // Don't return immediately, because may have rootClassInstrumentation == true (see above)
0669: } else {
0670: // Do the following update before instrumentation, since if we do this after it, chances are some instrumented
0671: // method in another thread enters e.g. methodEntry() and hits the not-yet-updated invocation array before
0672: // updating has been completed.
0673: updateInstrClassAndMethodNames(imgr.getBase(), true);
0674:
0675: if (rootClassInstrumentation
0676: && (getCurrentInstrType() == INSTR_OBJECT_LIVENESS)) {
0677: // Create a ThreadInfo for the current thread immediately to avoid recursion with trace object allocation calls
0678: ThreadInfo.getThreadInfo();
0679: }
0680:
0681: try {
0682: instrumentMethodGroupNow(imgr.getBase());
0683: } catch (Exception ex) {
0684: //deactivateInjectedCode(); // It looks like it often makes more sense to proceed and get at least some info
0685: profilerServer
0686: .sendComplexCmdToClient(new AsyncMessageCommand(
0687: false, ex.getMessage()));
0688:
0689: return true; // Used to be "return false" (but see comment above).
0690: }
0691: }
0692:
0693: if (rootClassInstrumentation) {
0694: switch (getCurrentInstrType()) {
0695: case INSTR_RECURSIVE_FULL:
0696:
0697: if (instrumentReflection) {
0698: ProfilerRuntimeCPU
0699: .setJavaLangReflectMethodInvokeInterceptEnabled(true);
0700: }
0701:
0702: ProfilerRuntimeCPUFullInstr.enableProfiling(true);
0703:
0704: break;
0705: case INSTR_RECURSIVE_SAMPLED:
0706:
0707: if (instrumentReflection) {
0708: ProfilerRuntimeCPU
0709: .setJavaLangReflectMethodInvokeInterceptEnabled(true);
0710: }
0711:
0712: ProfilerRuntimeCPUSampledInstr.enableProfiling(true);
0713:
0714: break;
0715: case INSTR_CODE_REGION:
0716: ProfilerRuntimeCPUCodeRegion.enableProfiling(true);
0717:
0718: break;
0719: case INSTR_OBJECT_ALLOCATIONS:
0720: ProfilerRuntimeObjAlloc.enableProfiling(true);
0721:
0722: break;
0723: case INSTR_OBJECT_LIVENESS:
0724: ProfilerRuntimeObjLiveness.enableProfiling(true);
0725:
0726: break;
0727: }
0728: }
0729:
0730: return true;
0731: }
0732:
0733: private static boolean isCoreClassName(String name) {
0734: name = name.replace('.', '/'); // NOI18N
0735:
0736: return (name.startsWith("java/") || name.startsWith("sun/") || name
0737: .startsWith("javax/")); // NOI18N
0738: }
0739:
0740: private static void getLoadedClasses() {
0741: Class[] nonSystemClasses;
0742: int nonSystemIndex = 0;
0743:
0744: loadedClassesArray = Classes.getAllLoadedClasses();
0745: nonSystemClasses = new Class[loadedClassesArray.length]; // classes loaded by classloaders other that bootstrap and system
0746: loadedClassesLoaders = new int[loadedClassesArray.length];
0747:
0748: for (int i = 0; i < loadedClassesArray.length; i++) {
0749: Class clazz = loadedClassesArray[i];
0750: loadedClassesLoaders[i] = ClassLoaderManager
0751: .registerLoader(clazz);
0752:
0753: if (loadedClassesLoaders[i] > 0) { // bootstrap classloader has index -1 and system classloader has index 0
0754: nonSystemClasses[nonSystemIndex++] = clazz;
0755: }
0756: }
0757:
0758: if (nonSystemIndex > 0) {
0759: Classes
0760: .cacheLoadedClasses(nonSystemClasses,
0761: nonSystemIndex);
0762: }
0763: }
0764:
0765: private static boolean isRootClass(String className) {
0766: for (int i = 0; i < rootClassNames.length; i++) {
0767: String rootName = rootClassNames[i];
0768:
0769: if (rootClassNameWildcard[i]) {
0770: if (className.startsWith(rootName)) {
0771: if (className.indexOf('.', rootName.length()) == -1) { // not a subpackage
0772:
0773: return true;
0774: }
0775: }
0776: } else if (rootName.equals(className)) {
0777: return true;
0778: }
0779: }
0780:
0781: return false;
0782: }
0783:
0784: private static void appendTypeName(StringBuffer sb, Class type) {
0785: if (type.isArray()) {
0786: do {
0787: sb.append('['); // NOI18N
0788: type = type.getComponentType();
0789: } while (type.isArray());
0790: }
0791:
0792: if (type == Integer.TYPE) {
0793: sb.append('I'); // NOI18N
0794: } else if (type == Boolean.TYPE) {
0795: sb.append('Z'); // NOI18N
0796: } else if (type == Byte.TYPE) {
0797: sb.append('B'); // NOI18N
0798: } else if (type == Character.TYPE) {
0799: sb.append('C'); // NOI18N
0800: } else if (type == Long.TYPE) {
0801: sb.append('J'); // NOI18N
0802: } else if (type == Float.TYPE) {
0803: sb.append('F'); // NOI18N
0804: } else if (type == Double.TYPE) {
0805: sb.append('D'); // NOI18N
0806: } else if (type == Void.TYPE) {
0807: sb.append('V'); // NOI18N
0808: } else {
0809: sb.append('L'); // NOI18N
0810: sb.append(type.getName().replace('.', '/')); // NOI18N
0811: sb.append(';'); // NOI18N
0812: }
0813: }
0814:
0815: private static boolean checkForLoadedRootClasses() {
0816: for (int i = 0; i < loadedClassesArray.length; i++) {
0817: if (isRootClass(loadedClassesArray[i].getName())) {
0818: return true;
0819: }
0820: }
0821:
0822: return false;
0823: }
0824:
0825: /** Called on CLASS_PREPARE JVMTI event */
0826: private static void classLoadHook(Class clazz) {
0827: ThreadInfo threadInfo = ThreadInfo.getThreadInfo();
0828:
0829: threadInfo.inProfilingRuntimeMethod++;
0830:
0831: try {
0832: String className = clazz.getName();
0833:
0834: if (instrumentMethodGroupCallThread == Thread
0835: .currentThread()
0836: || internalClassName(className)) { // See comment at inInstrumentMethodGroupCall
0837: ClassLoaderManager.registerLoader(clazz); // Still register the loader, for reasons related with
0838: // management of jmethodIds
0839:
0840: return;
0841: }
0842:
0843: Thread currentThread = Thread.currentThread();
0844:
0845: if (PROFILER_SERVER_THREAD_NAME.equals(currentThread
0846: .getName())) {
0847: System.err.println(ENGINE_WARNING + "class "
0848: + className + " loaded by "
0849: + PROFILER_SERVER_THREAD_NAME); // NOI18N
0850:
0851: return;
0852: }
0853:
0854: if ((initInstrumentationThread != null)
0855: && (currentThread == initInstrumentationThread)) {
0856: // Looks like on rare occasions we can get this problem - class load hook called when it shouldn't.
0857: // If we are already here, we can't (easily at least) fix this problem, but at least we can warn the user.
0858: System.err
0859: .println(ENGINE_WARNING
0860: + "class load hook invoked at inappropriate time for " // NOI18N
0861: + className + ", loader = "
0862: + clazz.getClassLoader()); // NOI18N
0863: System.err
0864: .println("*** This class will not be instrumented unless you re-run the instrumentation command"); // NOI18N
0865: System.err.println(PLEASE_REPORT_PROBLEM);
0866: System.err
0867: .println("=============================== Stack trace ====================="); // NOI18N
0868: Thread.dumpStack();
0869: System.err
0870: .println("=============================== End stack trace ================="); // NOI18N
0871:
0872: return;
0873: }
0874:
0875: //System.out.println("+++ Class load hook invoked for " + className + ", loader = " + clazz.getClassLoader());
0876: int classLoaderId = ClassLoaderManager
0877: .registerLoader(clazz);
0878: boolean resumeTimer = false;
0879:
0880: if (DEBUG) {
0881: System.err
0882: .println("ProfilerInterface.classLoadHook.DEBUG: "
0883: + className
0884: + ", classLoaderId: "
0885: + classLoaderId); // NOI18N
0886: }
0887:
0888: serialClientOperationsLock.beginTrans(true);
0889:
0890: try {
0891: boolean rootInstrumented = false;
0892: String excMessage = null;
0893: int instrType = getCurrentInstrType();
0894:
0895: if (instrType == INSTR_NONE) {
0896: return; // Instrumentation was turned off in the mean time
0897: }
0898:
0899: // bugfix for issue http://profiler.netbeans.org/issues/show_bug.cgi?id=65968
0900: boolean resumeProfiling = false;
0901:
0902: if (ThreadInfo.profilingSuspended()) {
0903: ThreadInfo.suspendProfiling();
0904: resumeProfiling = true;
0905: }
0906:
0907: try {
0908: if (rootClassLoaded) { // if yes, it means instrumentation has been started
0909: // [ian] why the following if???
0910:
0911: if ((instrType != INSTR_RECURSIVE_FULL)
0912: && (instrType != INSTR_RECURSIVE_SAMPLED)
0913: && (instrType != INSTR_OBJECT_ALLOCATIONS)
0914: && (instrType != INSTR_OBJECT_LIVENESS)) {
0915: if (!((instrType == INSTR_CODE_REGION) && className
0916: .equals(rootClassNames[ProfilingSessionStatus.CODE_REGION_CLASS_IDX]))) {
0917: return; // Nothing to do
0918: }
0919: }
0920:
0921: ThreadInfo ti = null;
0922:
0923: if ((instrType == INSTR_RECURSIVE_FULL)
0924: || (instrType == INSTR_RECURSIVE_SAMPLED)) {
0925: nClassLoads++;
0926: ti = ProfilerRuntimeCPU
0927: .suspendCurrentThreadTimer(); // start blackout period
0928: clientInstrStartTime = Timers
0929: .getCurrentTimeInCounts();
0930: // We'll be unable to call resumeCurrentThreadTimer() right here, since here we are holding serialClientOperationsLock.
0931: // The same lock is acquired when we dump the event buffer. So if here we call resumeTimer(), which calls writeEvent(),
0932: // we can get into a deadlock if some other thread at this time is dumping the event buffer and tries to acquire that lock.
0933: resumeTimer = true; // resume blackout period at the end
0934: }
0935:
0936: // Get cached class file bytes if they are available, i.e. if the class is loaded by a custom classloader
0937: // If remote profiling is used, get these class file bytes from system classpath
0938: // classLoaderId = 0 means that it is a system or bootstrap classloader
0939: byte[] classFileBytes = (classLoaderId > 0) ? Classes
0940: .getCachedClassFileBytes(clazz)
0941: : (status.remoteProfiling ? ClassBytesLoader
0942: .getClassFileBytes(className)
0943: : null);
0944:
0945: // send request to tool to instrument the bytecode
0946: ClassLoadedCommand cmd = new ClassLoadedCommand(
0947: className,
0948: ClassLoaderManager
0949: .getThisAndParentLoaderData(classLoaderId),
0950: classFileBytes, (ti != null) ? ti
0951: .isInCallGraph() : false);
0952: profilerServer.sendComplexCmdToClient(cmd);
0953:
0954: // read response from tool that should contain the instrumented bytecode, and redefine the methods/classes
0955: if (!getAndInstrumentClasses(false)) {
0956: disableProfilerHooks();
0957:
0958: return;
0959: }
0960: } else {
0961: // in total inst scheme for CPU profiling we instrument everything
0962: boolean rootWasLoaded = ((instrType == INSTR_RECURSIVE_FULL) || (instrType == INSTR_RECURSIVE_SAMPLED))
0963: && (status.instrScheme == CommonConstants.INSTRSCHEME_TOTAL);
0964:
0965: // No root classes have been loaded - check if it's one of them
0966: if (!rootWasLoaded && !isRootClass(className)) {
0967: return;
0968: }
0969:
0970: // This is a root class - proceed with requesting client for instrumented code.
0971: nClassLoads++;
0972: clientInstrStartTime = Timers
0973: .getCurrentTimeInCounts();
0974: sendRootClassLoadedCommand(true);
0975:
0976: if (!getAndInstrumentClasses(true)) {
0977: disableProfilerHooks();
0978:
0979: return;
0980: }
0981:
0982: // Note: it is important to have 'rootClassLoaded = true' here, i.e. *after* (not before) the call to getAndInstrumentClasses().
0983: // It looks like some classes returned by getAllLoadedClasses() may be not completely initialized, and thus when we finally
0984: // load them properly in instrumentMethodGroup() before intrumenting, they get initialized and classLoadHook is called for each
0985: // of them. If rootClassLoaded is true, then for each such class a request is sent to the client, which wonders why it got a
0986: // second class load event for the same class. Having rootClassLoaded not set until all such classes are loaded eliminates this
0987: // issue. WARNING: may it happen that some really new class is loaded as a side effect of initializing of the classes described
0988: // above? If so, it will be effectively lost. Need to try to come up with a test to confirm or prove this worry wrong.
0989: rootInstrumented = true;
0990: rootClassLoaded = true;
0991:
0992: // This is done to avoid counting the time spent in instrumentation etc. upon root class load, but before our app (or actually
0993: // data recording) started. That's because we use this internal statistics to calculate/verify the gross run time of the app.
0994: ProfilerCalibrator
0995: .resetInternalStatsCollectors();
0996: }
0997:
0998: if (rootInstrumented || (excMessage != null)) {
0999: AsyncMessageCommand cmd = null;
1000:
1001: if (excMessage == null) {
1002: cmd = new AsyncMessageCommand(true,
1003: INSTRUMENTATION_SUCCESSFUL_MSG); // NOI18N
1004: } else {
1005: cmd = new AsyncMessageCommand(false,
1006: excMessage);
1007: }
1008:
1009: profilerServer.sendComplexCmdToClient(cmd);
1010: }
1011: } finally {
1012: if (resumeProfiling) {
1013: ThreadInfo.resumeProfiling();
1014: }
1015: }
1016: } finally { // end of synchronized(serialClientOperationsLock)
1017: serialClientOperationsLock.endTrans();
1018: }
1019:
1020: if (resumeTimer) {
1021: int instrType = getCurrentInstrType();
1022:
1023: if ((instrType == INSTR_RECURSIVE_FULL)
1024: || (instrType == INSTR_RECURSIVE_SAMPLED)) {
1025: ProfilerRuntimeCPU.resumeCurrentThreadTimer();
1026: }
1027: }
1028: } finally {
1029: threadInfo.inProfilingRuntimeMethod--;
1030: }
1031: }
1032:
1033: private static void firstTimeMethodInvokeHook(int methodId) {
1034: serialClientOperationsLock.beginTrans(true);
1035:
1036: try {
1037: int instrType = getCurrentInstrType();
1038:
1039: if ((instrType != INSTR_RECURSIVE_FULL)
1040: && (instrType != INSTR_RECURSIVE_SAMPLED)) {
1041: return; // Chances are that instrumentation is already stopped
1042: }
1043:
1044: clientInstrStartTime = Timers.getCurrentTimeInCounts();
1045:
1046: MethodInvokedFirstTimeCommand cmd = new MethodInvokedFirstTimeCommand(
1047: methodId);
1048: profilerServer.sendComplexCmdToClient(cmd);
1049:
1050: if (!getAndInstrumentClasses(false)) {
1051: disableProfilerHooks();
1052:
1053: return;
1054: }
1055:
1056: // The following reset is done to avoid counting the time spent in instrumentation before data recording started.
1057: if (nFirstMethodInvocations == 0) {
1058: ProfilerCalibrator.resetInternalStatsCollectors();
1059: }
1060:
1061: nFirstMethodInvocations++;
1062: } finally {
1063: serialClientOperationsLock.endTrans();
1064: }
1065: }
1066:
1067: private static int firstTimeVMObjectAlloc(String className,
1068: int classLoaderId) {
1069: if (internalClassName(className)) {
1070: return -1;
1071: }
1072:
1073: serialClientOperationsLock.beginTrans(true);
1074:
1075: try {
1076: if (classLoaderId > 0) { // neither bootstrap nor system class loader
1077: // get defining classloader
1078: classLoaderId = ClassLoaderManager
1079: .getDefiningLoaderForClass(className,
1080: classLoaderId);
1081: }
1082:
1083: GetClassIdCommand cmd = new GetClassIdCommand(className,
1084: classLoaderId);
1085: profilerServer.sendComplexCmdToClient(cmd);
1086:
1087: GetClassIdResponse resp = (GetClassIdResponse) profilerServer
1088: .getLastResponse();
1089:
1090: if (resp.isOK()) {
1091: return resp.getClassId();
1092: }
1093:
1094: return -1;
1095: } finally {
1096: serialClientOperationsLock.endTrans();
1097: }
1098: }
1099:
1100: private static void handleFakeInitRecursiveInstrumentationCommand() {
1101: // Send a fake RootClassLoadedCommand to the client and get a reply from it. This is done to force initialization
1102: // of all classes related to this operation. If this happens later, it can cause deadlock due to classLoadHook called upon
1103: // loading of some of these classes, when classLoadHook is already locked to "serialize" class load events.
1104: new HFIRIThread().start();
1105: }
1106:
1107: /**
1108: * support for multiple roots needed by EJB work
1109: * will check each class to see if it is a candidate to be a core class
1110: */
1111: private static boolean hasAnyCoreClassNames(String[] classes) {
1112: if (!(classes.length > 0)) {
1113: return false;
1114: }
1115:
1116: for (int i = 0; i < classes.length; i++) {
1117: if (isCoreClassName(classes[i])) {
1118: return true;
1119: }
1120: }
1121:
1122: return false; //none found...
1123: }
1124:
1125: private static void instrumentMethodGroupNow(
1126: InstrumentMethodGroupData imgb) throws Exception {
1127: try {
1128: instrumentMethodGroupCallThread = Thread.currentThread();
1129:
1130: int res = 0;
1131: long time = Timers.getCurrentTimeInCounts();
1132: nNonEmptyInstrMethodGroupResponses++;
1133:
1134: int nClasses = imgb.getNClasses();
1135: String[] instrClassNames = imgb.getMethodClasses();
1136: int[] instrClassLoaders = imgb.getClassLoaderIds();
1137: int nMethods = imgb.getNMethods();
1138:
1139: Class[] clazzes = new Class[nClasses];
1140: byte[][] b = imgb.getReplacementClassFileBytes();
1141: int k = 0;
1142:
1143: for (int i = 0; i < nClasses; i++) {
1144: clazzes[k] = ClassLoaderManager.getLoadedClass(
1145: instrClassNames[i], instrClassLoaders[i]);
1146:
1147: if (clazzes[k] != null) {
1148: if (b[k] == null) {
1149: // An optimization to avoid overhead of creating and sending original class file bytes from client
1150: // to server
1151: if (instrClassLoaders[i] == 0) {
1152: b[k] = ClassBytesLoader
1153: .getClassFileBytes(instrClassNames[i]);
1154: } else {
1155: b[k] = Classes
1156: .getCachedClassFileBytes(clazzes[k]);
1157: }
1158: }
1159:
1160: k++;
1161: } else {
1162: reportUnloadedClass(instrClassNames[i]);
1163:
1164: int classesToMove = nClasses - k - 1;
1165: System.arraycopy(clazzes, k + 1, clazzes, k,
1166: classesToMove);
1167: System.arraycopy(b, k + 1, b, k, classesToMove);
1168: }
1169: }
1170:
1171: if (k < nClasses) {
1172: Class[] oldClazzes = clazzes;
1173: clazzes = new Class[k];
1174: System.arraycopy(oldClazzes, 0, clazzes, 0, k);
1175: }
1176:
1177: Classes.redefineClasses(clazzes, imgb
1178: .getReplacementClassFileBytes());
1179:
1180: time = Timers.getCurrentTimeInCounts() - time;
1181: totalHotswappingTime += time;
1182:
1183: if (time < minHotswappingTime) {
1184: minHotswappingTime = time;
1185: } else if (time > maxHotswappingTime) {
1186: maxHotswappingTime = time;
1187: }
1188:
1189: instrumentMethodGroupCallThread = null;
1190: } catch (Throwable t) {
1191: if (t instanceof Classes.RedefineException) {
1192: int nClasses = imgb.getNClasses();
1193: String[] instrClassNames = imgb.getMethodClasses();
1194: System.err
1195: .println("Profiler Agent Error: Redefinition failed for classes:"); // NOI18N
1196:
1197: for (int i = 0; i < nClasses; i++) {
1198: System.err.println(instrClassNames[i]);
1199: }
1200:
1201: System.err
1202: .println("Profiler Agent Error: with message: "
1203: + ((Classes.RedefineException) t)
1204: .getMessage()); // NOI18N
1205:
1206: byte[][] newBytes = imgb.getReplacementClassFileBytes();
1207:
1208: for (int i = 0; i < nClasses; i++) {
1209: String name = instrClassNames[i];
1210: File outFile = new File(name + ".class"); // NOI18N
1211: System.err.println("Debug: writing class file: "
1212: + name + ", into file: "
1213: + outFile.getPath()); // NOI18N
1214:
1215: try {
1216: FileOutputStream fos = new FileOutputStream(
1217: outFile);
1218: fos.write(newBytes[i]);
1219: fos.close();
1220: } catch (IOException exc) {
1221: System.err.println("error: " + exc
1222: + " writing class file: "
1223: + outFile.getPath()); // NOI18N
1224: }
1225: }
1226:
1227: throw ((Classes.RedefineException) t);
1228: } else {
1229: java.io.StringWriter sw = new java.io.StringWriter();
1230: java.io.PrintWriter pw = new java.io.PrintWriter(sw);
1231: t.printStackTrace(pw);
1232: throw new Exception(MessageFormat.format(
1233: UNEXPECTED_EXCEPTION_MSG, new Object[] { t,
1234: sw.toString() }));
1235: }
1236: } finally {
1237: instrumentMethodGroupCallThread = null;
1238: }
1239: }
1240:
1241: private static boolean internalClassName(String name) {
1242: return (name.startsWith(PROFILER_DOTTED_CLASS_PREFIX) || // WARNING: sun.reflect.* are not really internal classes, but they may create too many problems by being loaded unexpectedly
1243: // by our internal code and causing classLoadHook to be invoked recursively. At least we need sun.reflect.Generated* to be
1244: // dismissed. Others could probably be less of a problem if ClassLoaderManager didn't use reflection.
1245: (name.startsWith("sun.reflect.")
1246: && !name
1247: .startsWith("sun.reflect.GeneratedSerializationConstructorAccessor") && !name
1248: .startsWith("sun.reflect.GeneratedConstructorAccessor")) // NOI18N
1249: || name.startsWith("sun.instrument.") // NOI18N
1250: // FIXME: below is a (hopefully temporary) fix to the strange problem showing up as ClassCircularityError when
1251: // we try to profile PetStore with eager instrumentation scheme on Sun ONE AS 7. This makes the problem go away,
1252: // but its root cause is still unclear to me.
1253: || name.equals("com.sun.enterprise.J2EESecurityManager") // NOI18N
1254: );
1255: }
1256:
1257: private static void reflectiveMethodInvokeHook(Method method) {
1258: serialClientOperationsLock.beginTrans(true);
1259:
1260: try {
1261: if (reflectMethods.containsKey(method)) {
1262: return;
1263: }
1264:
1265: ProfilerRuntimeCPU.suspendCurrentThreadTimer();
1266:
1267: reflectMethods.put(method, null);
1268:
1269: Class clazz = method.getDeclaringClass();
1270: String className = clazz.getName();
1271: String methodName = method.getName();
1272: Class[] paramTypes = method.getParameterTypes();
1273: StringBuffer sb = new StringBuffer();
1274: sb.append('(');
1275:
1276: for (int i = 0; i < paramTypes.length; i++) {
1277: appendTypeName(sb, paramTypes[i]);
1278: }
1279:
1280: sb.append(')');
1281: appendTypeName(sb, method.getReturnType());
1282:
1283: String methodSignature = sb.toString();
1284:
1285: clientInstrStartTime = Timers.getCurrentTimeInCounts();
1286:
1287: MethodLoadedCommand cmd = new MethodLoadedCommand(
1288: className,
1289: ClassLoaderManager.registerLoader(clazz),
1290: methodName, methodSignature);
1291: profilerServer.sendComplexCmdToClient(cmd);
1292:
1293: if (!getAndInstrumentClasses(false)) {
1294: disableProfilerHooks();
1295:
1296: return;
1297: }
1298:
1299: ProfilerRuntimeCPU.resumeCurrentThreadTimer();
1300: } finally {
1301: serialClientOperationsLock.endTrans();
1302: }
1303: }
1304:
1305: private static void reportUnloadedClass(String className) {
1306: System.err.println(ENGINE_WARNING
1307: + "target VM cannot load class to instrument "
1308: + className); // NOI18N
1309: System.err
1310: .println("*** probably it has been unloaded recently"); // NOI18N
1311: }
1312:
1313: private static void sendRootClassLoadedCommand(
1314: boolean doGetLoadedClasses) {
1315: if (doGetLoadedClasses) {
1316: getLoadedClasses(); // Otherwise we know loadedClassesArray has already been initialized
1317: }
1318:
1319: int len = loadedClassesArray.length;
1320: String[] loadedClassNames = new String[len];
1321: int[] loaders = new int[len];
1322: byte[][] cachedClassFileBytes = new byte[len][];
1323: int idx = 0;
1324:
1325: for (int i = 0; i < loadedClassesArray.length; i++) {
1326: String name = loadedClassesArray[i].getName();
1327:
1328: if (name.startsWith("[") || internalClassName(name)) { // NOI18N
1329:
1330: continue; // NOI18N
1331: }
1332:
1333: loadedClassNames[idx] = name;
1334: loaders[idx] = loadedClassesLoaders[i];
1335:
1336: if (loaders[idx] > 0) {
1337: cachedClassFileBytes[idx] = Classes
1338: .getCachedClassFileBytes(loadedClassesArray[i]);
1339: } else if (status.remoteProfiling) { // When we profile remotely, we need to send all available classes to the tool
1340: cachedClassFileBytes[idx] = ClassBytesLoader
1341: .getClassFileBytes(loadedClassesArray[i]
1342: .getName());
1343: }
1344:
1345: idx++;
1346: }
1347:
1348: String bufferFileName = ((getCurrentInstrType() == INSTR_RECURSIVE_FULL)
1349: || (getCurrentInstrType() == INSTR_RECURSIVE_SAMPLED)
1350: || (getCurrentInstrType() == INSTR_OBJECT_ALLOCATIONS) || (getCurrentInstrType() == INSTR_OBJECT_LIVENESS)) ? evBufManager
1351: .getBufferFileName()
1352: : " "; // NOI18N
1353:
1354: RootClassLoadedCommand cmd = new RootClassLoadedCommand(
1355: loadedClassNames, loaders, cachedClassFileBytes, idx,
1356: ClassLoaderManager.getParentLoaderIdTable(),
1357: bufferFileName);
1358: profilerServer.sendComplexCmdToClient(cmd);
1359: loadedClassesArray = null; // Free memory
1360: loadedClassesLoaders = null; // Ditto
1361: }
1362:
1363: private static void updateInstrClassAndMethodNames(
1364: InstrumentMethodGroupData imgb, boolean firstTime) {
1365: status.beginTrans(false);
1366:
1367: try {
1368: switch (getCurrentInstrType()) {
1369: case INSTR_RECURSIVE_FULL:
1370: case INSTR_RECURSIVE_SAMPLED:
1371: status.updateInstrMethodsInfo(imgb.getNClasses(), imgb
1372: .getNMethods(), null, null, null, null, null,
1373: imgb.getInstrMethodLeaf());
1374: ProfilerRuntimeCPU.setInstrMethodsInvoked(status
1375: .getInstrMethodInvoked());
1376:
1377: break;
1378: case INSTR_OBJECT_ALLOCATIONS:
1379: case INSTR_OBJECT_LIVENESS:
1380: status.updateAllocatedInstancesCountInfoInServer(imgb
1381: .getAddInfo());
1382: ProfilerRuntimeMemory
1383: .setAllocatedInstancesCountArray(status
1384: .getAllocatedInstancesCount());
1385:
1386: break;
1387: }
1388: } finally {
1389: status.endTrans();
1390: }
1391: }
1392: }
|