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;
0042:
0043: import org.netbeans.lib.profiler.classfile.ClassRepository;
0044: import org.netbeans.lib.profiler.client.AppStatusHandler;
0045: import org.netbeans.lib.profiler.client.ClientUtils;
0046: import org.netbeans.lib.profiler.client.MonitoredData;
0047: import org.netbeans.lib.profiler.global.CalibrationDataFileIO;
0048: import org.netbeans.lib.profiler.global.CommonConstants;
0049: import org.netbeans.lib.profiler.global.Platform;
0050: import org.netbeans.lib.profiler.global.ProfilingSessionStatus;
0051: import org.netbeans.lib.profiler.instrumentation.BadLocationException;
0052: import org.netbeans.lib.profiler.instrumentation.InstrumentationException;
0053: import org.netbeans.lib.profiler.instrumentation.Instrumentor;
0054: import org.netbeans.lib.profiler.marker.Marker;
0055: import org.netbeans.lib.profiler.results.EventBufferProcessor;
0056: import org.netbeans.lib.profiler.results.EventBufferResultsProvider;
0057: import org.netbeans.lib.profiler.results.ProfilingResultsDispatcher;
0058: import org.netbeans.lib.profiler.results.coderegion.CodeRegionResultsSnapshot;
0059: import org.netbeans.lib.profiler.results.cpu.CPUCCTProvider;
0060: import org.netbeans.lib.profiler.results.cpu.CPUResultsSnapshot;
0061: import org.netbeans.lib.profiler.results.cpu.FlatProfileProvider;
0062: import org.netbeans.lib.profiler.results.cpu.cct.CCTResultsFilter;
0063: import org.netbeans.lib.profiler.results.cpu.cct.TimeCollector;
0064: import org.netbeans.lib.profiler.results.memory.*;
0065: import org.netbeans.lib.profiler.utils.MiscUtils;
0066: import org.netbeans.lib.profiler.utils.StringUtils;
0067: import org.netbeans.lib.profiler.wireprotocol.*;
0068: import java.awt.EventQueue;
0069: import java.io.IOException;
0070: import java.io.ObjectInputStream;
0071: import java.io.ObjectOutputStream;
0072: import java.lang.reflect.InvocationTargetException;
0073: import java.net.ConnectException;
0074: import java.net.Socket;
0075: import java.text.MessageFormat;
0076: import java.util.*;
0077:
0078: /**
0079: * The interface between the tool and the profiling back end.
0080: *
0081: * @author Tomas Hurka
0082: * @author Misha Dmitriev
0083: * @author Adrian Mos
0084: * @author Ian Formanek
0085: */
0086: public class ProfilerClient implements CommonConstants {
0087: //~ Inner Classes ------------------------------------------------------------------------------------------------------------
0088:
0089: /**
0090: * Thread for execution of commands that, due to limitations of our wire protocol, need to be executed such that
0091: * the ServerListener thread doesn't stay blocked while these commands are executed. See executeInSeparateThread()
0092: * above.
0093: */
0094: private class SeparateCmdExecutionThread extends Thread {
0095: //~ Methods --------------------------------------------------------------------------------------------------------------
0096:
0097: public void run() {
0098: setName(PROFILER_SEPARATE_EXEC_THREAD_NAME); // NOI18N
0099:
0100: synchronized (execInSeparateThreadLock) {
0101: while (true) {
0102: try {
0103: execInSeparateThreadLock.wait();
0104: } catch (InterruptedException ex) {
0105: MiscUtils
0106: .internalError("ProfilerClient.SpecialExecutionThread.run()"); // NOI18N
0107: }
0108:
0109: if (execInSeparateThreadCmd == null) {
0110: return; // It was a signal to this thread to terminate (currently not used, though)
0111: }
0112:
0113: Command cmd = execInSeparateThreadCmd;
0114: execInSeparateThreadCmd = null;
0115:
0116: switch (cmd.getType()) {
0117: case Command.ROOT_CLASS_LOADED:
0118: instrumentMethodGroupFromRoot((RootClassLoadedCommand) cmd);
0119:
0120: //instrMethodGroupFromRootComplete = true;
0121: break;
0122: case Command.CLASS_LOADED:
0123: case Command.METHOD_INVOKED_FIRST_TIME:
0124: case Command.METHOD_LOADED:
0125: instrumentMethodGroupFollowUp(cmd);
0126:
0127: break;
0128: case Command.EVENT_BUFFER_DUMPED:
0129:
0130: int bufSize = ((EventBufferDumpedCommand) cmd)
0131: .getBufSize();
0132: EventBufferProcessor
0133: .readDataAndPrepareForProcessing(bufSize);
0134:
0135: List profilePointHits = new ArrayList();
0136: EventBufferResultsProvider.getDefault()
0137: .dataReady(bufSize,
0138: getCurrentInstrType());
0139: // processProfilingResults(bufSize, profilePointHits);
0140: // profilePointHit(profilePointHits);
0141: sendSimpleRespToServer(true, null);
0142:
0143: break;
0144: case Command.CLASS_LOADER_UNLOADING:
0145:
0146: // We have to grab the forceObtainedResultsDumpLock to prevent forceObtainedResultsDump() coming in while
0147: // we are processing the data, sending the request for dump to the server that currently awaits the
0148: // request for jmethodIds, and thus creating a "distributed deadlock".
0149: synchronized (ProfilerClient.this ) {
0150: synchronized (forceObtainedResultsDumpLock) {
0151: if (memCctProvider != null) {
0152: memCctProvider.updateInternals();
0153: }
0154:
0155: sendSimpleRespToServer(true, null);
0156: }
0157: }
0158:
0159: break;
0160: }
0161: }
0162: }
0163: }
0164: }
0165:
0166: private class ServerListener extends Thread {
0167: //~ Instance fields ------------------------------------------------------------------------------------------------------
0168:
0169: private final Object startedFlagLock = new Object();
0170:
0171: // @GuardedBy startedFlagLock
0172: private int startedFlag = 0; // 0 = initial state; 1 = started; -1 = cancelled
0173:
0174: //~ Methods --------------------------------------------------------------------------------------------------------------
0175:
0176: public boolean isRunning() {
0177: synchronized (startedFlagLock) {
0178: return startedFlag == 1;
0179: }
0180: }
0181:
0182: public void cancel() {
0183: synchronized (startedFlagLock) {
0184: startedFlag = -1;
0185: startedFlagLock.notifyAll();
0186: }
0187: }
0188:
0189: public void run() {
0190: // Wait until we know that the connection is open
0191: synchronized (startedFlagLock) {
0192: while (startedFlag == 0) { // while the state hasn't been explicitly changed
0193:
0194: try {
0195: startedFlagLock.wait(500);
0196: } catch (InterruptedException e) {
0197: startedFlag = -1; // thread has been interrupet = effectively cancelled
0198: }
0199: }
0200:
0201: if (startedFlag == -1) { // cancelled
0202:
0203: return;
0204: }
0205: }
0206:
0207: while (targetVMAlive) {
0208: try {
0209: Object o = wireIO.receiveCommandOrResponse();
0210:
0211: //System.out.println(">>> Got response or command from server " + o);
0212: if (o == null) {
0213: closeConnection();
0214: } else {
0215: if (o instanceof Command) {
0216: handleServerCommand((Command) o);
0217: } else {
0218: setLastResponse((Response) o);
0219: }
0220: }
0221: } catch (IOException ex) {
0222: if (targetVMAlive
0223: && !terminateOrDetachCommandIssued) { // It wasn't a normal connection shutdown
0224: MiscUtils
0225: .printErrorMessage("exception while trying to get response from the target JVM:\n"
0226: + ex); // NOI18N
0227: closeConnection();
0228:
0229: // serverCommandHandler.handleServerCommand(null); // does not seem to do anything
0230: }
0231: }
0232: }
0233: }
0234:
0235: public void shutdown() {
0236: synchronized (startedFlagLock) {
0237: startedFlag = 0;
0238: startedFlagLock.notifyAll();
0239: }
0240: }
0241:
0242: public void startRunning() {
0243: synchronized (startedFlagLock) {
0244: startedFlag = 1;
0245: startedFlagLock.notifyAll();
0246: }
0247: }
0248:
0249: private void handleServerCommand(final Command cmd) {
0250: switch (cmd.getType()) {
0251: case Command.SHUTDOWN_INITIATED:
0252: status.targetAppRunning = false;
0253:
0254: // Get and save the latest results and the internal statistics before the target VM goes away
0255: (new Thread() {
0256: public void run() {
0257: try {
0258: int instrType = getCurrentInstrType();
0259:
0260: if (currentInstrTypeIsRecursiveCPUProfiling()
0261: || currentInstrTypeIsMemoryProfiling()) {
0262: forceObtainedResultsDump();
0263: }
0264:
0265: // In case of memory profiling, fetch additional data from the VM - names for all jmethodIDs and
0266: // object count
0267: if (currentInstrTypeIsMemoryProfiling()) {
0268: savedAllocatedObjectsCountResults = getAllocatedObjectsCountResults();
0269:
0270: // if (mcgb != null) {
0271: // mcgb.getNamesForJMethodIds();
0272: // }
0273: }
0274:
0275: status.savedInternalStats = getInternalStats();
0276:
0277: appStatusHandler.handleShutdown();
0278:
0279: sendSimpleCmdToServer(Command.SHUTDOWN_OK);
0280: } catch (ClientUtils.TargetAppOrVMTerminated ex) { /* Ignore silently */
0281: }
0282: }
0283: }).start();
0284:
0285: break;
0286: case Command.SHUTDOWN_COMPLETED:
0287: targetVMAlive = false;
0288: status.targetAppRunning = false;
0289: EventBufferProcessor.removeEventBufferFile();
0290:
0291: break;
0292: case Command.ROOT_CLASS_LOADED:
0293: executeInSeparateThread(cmd);
0294:
0295: break;
0296: case Command.CLASS_LOADED:
0297: case Command.METHOD_INVOKED_FIRST_TIME:
0298: case Command.METHOD_LOADED:
0299: executeInSeparateThread(cmd);
0300:
0301: break;
0302: case Command.EVENT_BUFFER_DUMPED:
0303: readAndProcessProfilingResults((EventBufferDumpedCommand) cmd);
0304:
0305: break;
0306: case Command.CLASS_LOADER_UNLOADING:
0307: executeInSeparateThread(cmd);
0308:
0309: break;
0310: case Command.RESULTS_AVAILABLE:
0311: resultsStart = System.currentTimeMillis();
0312:
0313: break;
0314: case Command.GET_CLASSID:
0315:
0316: GetClassIdCommand cidCmd = (GetClassIdCommand) cmd;
0317: int classId = instrumentor.getClassId(cidCmd
0318: .getClassName(), cidCmd.getClassLoaderId());
0319: sendComplexRespToServer(new GetClassIdResponse(
0320: classId != -1, classId));
0321:
0322: break;
0323: case Command.STILL_ALIVE:
0324: break;
0325: }
0326:
0327: if (!targetVMAlive) {
0328: closeConnection();
0329: }
0330:
0331: serverCommandHandler.handleServerCommand(cmd);
0332: }
0333: }
0334:
0335: //~ Static fields/initializers -----------------------------------------------------------------------------------------------
0336:
0337: // -----
0338: // I18N String constants
0339: private static final ResourceBundle messages = ResourceBundle
0340: .getBundle("org.netbeans.lib.profiler.Bundle"); // NOI18N
0341: private static final String CANNOT_OPEN_SERVER_TEMPFILE_MSG = messages
0342: .getString("ProfilerClient_CannotOpenServerTempFileMsg"); // NOI18N
0343: private static final String PERFORMING_INSTRUMENTATION_STRING = messages
0344: .getString("ProfilerClient_PerformingInstrumentationString"); // NOI18N
0345: private static final String INVALID_CODE_REGION_MSG = messages
0346: .getString("ProfilerClient_InvalidCodeRegionMsg"); // NOI18N
0347: private static final String CLASS_NOT_FOUND_MSG = messages
0348: .getString("ProfilerClient_ClassNotFoundMsg"); // NOI18N
0349: private static final String OUT_OF_MEMORY_MSG = messages
0350: .getString("ProfilerClient_OutOfMemoryMsg"); // NOI18N
0351: private static final String INCORRECT_AGENT_VERSION_MSG = messages
0352: .getString("ProfilerClient_IncorrectAgentVersionMsg"); // NOI18N
0353: private static final String ERROR_GETTING_CALIBRATION_DATA_MSG = messages
0354: .getString("ProfilerClient_ErrorGettingCalibrationDataMsg"); // NOI18N
0355: private static final String MUST_CALIBRATE_FIRST_MSG = messages
0356: .getString("ProfilerClient_MustCalibrateFirstMsg"); // NOI18N
0357: private static final String MUST_CALIBRATE_FIRST_SHORT_MSG = messages
0358: .getString("ProfilerClient_MustCalibrateFirstShortMsg"); // NOI18N
0359: private static final String INSTRUMENTATION_LIMIT_REACHED_MSG = messages
0360: .getString("ProfilerClient_InstrumentationLimitReachedMsg"); // NOI18N
0361: private static final String CORRUPTED_TARGET_CALIBRATION_DATA_MSG = messages
0362: .getString("ProfilerClient_CorruptedTargetCalibrationDataMsg"); // NOI18N
0363: private static final String CONNECT_VM_MSG = messages
0364: .getString("ProfilerClient_ConnectVmMsg"); // NOI18N
0365: private static final String TARGET_JVM_ERROR_MSG = messages
0366: .getString("ProfilerClient_TargetJvmErrorMsg"); // NOI18N
0367: private static final String UNSUPPORTED_JVM_MSG = messages
0368: .getString("ProfilerClient_UnsupportedJvmMsg"); // NOI18N
0369: // -----
0370: private static final String a = "AAQ";
0371:
0372: //~ Instance fields ----------------------------------------------------------------------------------------------------------
0373:
0374: private AppStatusHandler.ServerCommandHandler serverCommandHandler;
0375: private AppStatusHandler appStatusHandler;
0376: private CCTResultsFilter markFilter;
0377: private CPUCCTProvider cpuCctProvider;
0378: private Command execInSeparateThreadCmd;
0379: private FlatProfileProvider flatProvider;
0380: private InitiateInstrumentationCommand commandOnStartup = null;
0381: private Instrumentor instrumentor;
0382: private MemoryCCTProvider memCctProvider;
0383: private Object execInSeparateThreadLock = new Object();
0384: private Object forceObtainedResultsDumpLock = new Object(); // To make dump processing and other commands mutually
0385: // exclusive
0386:
0387: /*instrMethodGroupFromRootComplete, */
0388: private Object instrumentationLock = new Object(); // To make sure all instrumentation-related operations
0389: // happen serially
0390: private Object responseLock = new Object();
0391: private Object wdLock = new Object();
0392: private ObjectInputStream socketIn;
0393: private ObjectOutputStream socketOut;
0394: private ProfilerEngineSettings settings;
0395: private ProfilingSessionStatus status;
0396: private Response lastResponse;
0397: private SeparateCmdExecutionThread separateCmdExecThread;
0398: private ServerListener serverListener;
0399:
0400: //--------------------- Connection management --------------------
0401: private Socket clientSocket;
0402: private TimeCollector timeCollector;
0403: private WireIO wireIO;
0404:
0405: /**
0406: * Needed to make memory profiling results available after app/VM shutdown
0407: *
0408: * Note that we don't have anything like getMemoryProfilingResult() here - essentially because we don't have memory
0409: * results snapshots yet. Those, in turn, are not implemented because of performance concerns (reproducing our,
0410: * potentially huge, hash table containing all tracked object, plus the call trees for these object allocations,
0411: * every time that the user hits "Get results" seems scary). So instead of snapshots, we give the user various
0412: * aspects of (constantly updated) memory profiling data on demand. Methods that return it are public ones in
0413: * ObjAllocCallGraphBuilder, ObjLivenessCallGraphBuilder, and MemoryCallGraphBuilder.
0414: * The getAllocatedObjectsCountResults() method below provides only one aspect of the memory profiling data.
0415: */
0416: private int[] savedAllocatedObjectsCountResults;
0417: private volatile boolean connectionWithServerOpen; // Used just to prevent double entry into closeConnection()
0418: private volatile boolean forceObtainedResultsDumpCalled;
0419: private volatile boolean handlingEventBufferDump;
0420: private volatile boolean instrMethodsLimitReported;
0421: private boolean serverClassesInitialized;
0422: private boolean shouldDisplayDialog = true;
0423: private volatile boolean targetVMAlive;
0424: private volatile boolean terminateOrDetachCommandIssued;
0425: private int currentAgentId = -1;
0426: private long instrProcessingTime;
0427: private long resultsStart;
0428:
0429: //~ Constructors -------------------------------------------------------------------------------------------------------------
0430:
0431: public ProfilerClient(ProfilerEngineSettings settings,
0432: ProfilingSessionStatus status, AppStatusHandler ash,
0433: AppStatusHandler.ServerCommandHandler sch) {
0434: this .settings = settings;
0435: this .status = status;
0436: appStatusHandler = ash;
0437: serverCommandHandler = sch;
0438: instrumentor = new Instrumentor(status, settings);
0439:
0440: if (separateCmdExecThread == null) {
0441: separateCmdExecThread = new SeparateCmdExecutionThread();
0442: separateCmdExecThread.setDaemon(true);
0443: separateCmdExecThread.start();
0444: }
0445:
0446: EventBufferProcessor.initialize(this );
0447: EventBufferResultsProvider.getDefault().addDispatcher(
0448: ProfilingResultsDispatcher.getDefault());
0449: }
0450:
0451: //~ Methods ------------------------------------------------------------------------------------------------------------------
0452:
0453: /**
0454: * Returns the array where element at index I is the total number of allocated objects for the class with I id.
0455: * The relevant counters are kept at the server side and returned to the tool on demand, here.
0456: */
0457: public synchronized int[] getAllocatedObjectsCountResults()
0458: throws ClientUtils.TargetAppOrVMTerminated {
0459: if (!targetVMAlive) {
0460: if (savedAllocatedObjectsCountResults != null) {
0461: return savedAllocatedObjectsCountResults;
0462: } else {
0463: throw new ClientUtils.TargetAppOrVMTerminated(
0464: ClientUtils.TargetAppOrVMTerminated.VM);
0465: }
0466: }
0467:
0468: savedAllocatedObjectsCountResults = null;
0469: checkForTargetVMAlive();
0470: sendSimpleCmdToServer(Command.GET_OBJECT_ALLOCATION_RESULTS);
0471:
0472: ObjectAllocationResultsResponse resp = (ObjectAllocationResultsResponse) getAndCheckLastResponse("Unknown problem when trying to get allocated object count results." // NOI18N
0473: ); // NOI18N
0474:
0475: return resp.getResults();
0476: }
0477:
0478: /**
0479: * Returns the snapshot of current multi-method CPU profiling results
0480: *
0481: * @return CPU Results snapshot
0482: * @throws ClientUtils.TargetAppOrVMTerminated
0483: * In case the profiled application has already terminated
0484: * @throws CPUResultsSnapshot.NoDataAvailableException
0485: * If no data are available yet
0486: */
0487: public synchronized CPUResultsSnapshot getCPUProfilingResultsSnapshot()
0488: throws ClientUtils.TargetAppOrVMTerminated,
0489: CPUResultsSnapshot.NoDataAvailableException {
0490: return getCPUProfilingResultsSnapshot(true);
0491: }
0492:
0493: /**
0494: * Returns the snapshot of current multi-method CPU profiling results
0495: *
0496: * @param dump true to fetch latest events from server, false otherwise (use only available data)
0497: * @return CPU Results snapshot
0498: * @throws ClientUtils.TargetAppOrVMTerminated
0499: * In case the profiled application has already terminated
0500: * @throws CPUResultsSnapshot.NoDataAvailableException
0501: * If no data are available yet
0502: */
0503: public synchronized CPUResultsSnapshot getCPUProfilingResultsSnapshot(
0504: boolean dump) throws ClientUtils.TargetAppOrVMTerminated,
0505: CPUResultsSnapshot.NoDataAvailableException {
0506: checkForTargetVMAlive();
0507:
0508: if (dump) {
0509: if (!forceObtainedResultsDump(false, 5)) {
0510: return null;
0511: }
0512: }
0513:
0514: return new CPUResultsSnapshot(resultsStart, System
0515: .currentTimeMillis(), cpuCctProvider, status);
0516: }
0517:
0518: /**
0519: * Returns the snapshot of current code region profiling results
0520: */
0521: public synchronized CodeRegionResultsSnapshot getCodeRegionProfilingResultsSnapshot()
0522: throws ClientUtils.TargetAppOrVMTerminated {
0523: checkForTargetVMAlive();
0524: sendSimpleCmdToServer(Command.GET_CODE_REGION_CPU_RESULTS);
0525:
0526: CodeRegionCPUResultsResponse resp = (CodeRegionCPUResultsResponse) getAndCheckLastResponse("Unknown problem when trying to get code region CPU results." // NOI18N
0527: ); // NOI18N
0528:
0529: return new CodeRegionResultsSnapshot(resultsStart, System
0530: .currentTimeMillis(), resp.getResults(),
0531: status.timerCountsInSecond[0]);
0532: }
0533:
0534: public int getCurrentAgentId() {
0535: return currentAgentId;
0536: }
0537:
0538: public void setCurrentInstrType(int type) {
0539: status.currentInstrType = type;
0540: }
0541:
0542: public int getCurrentInstrType() {
0543: return status.currentInstrType;
0544: }
0545:
0546: /**
0547: * Determine which of the currently tracked threads are dead or alive. If the VM is not running, just returns null -
0548: * it's clear that all threads are dead then.
0549: */
0550: public synchronized byte[] getCurrentThreadsLivenessStatus() {
0551: try {
0552: checkForTargetVMAlive();
0553: sendSimpleCmdToServer(Command.GET_THREAD_LIVENESS_STATUS);
0554:
0555: ThreadLivenessStatusResponse resp = (ThreadLivenessStatusResponse) getAndCheckLastResponse("Unknown problem when trying to get thread liveness information." // NOI18N
0556: ); // NOI18N
0557:
0558: return resp.getStatus();
0559: } catch (ClientUtils.TargetAppOrVMTerminated ex) {
0560: if (serverListener.isRunning()) { // The possibly problematic situation is not known yet
0561: MiscUtils
0562: .printErrorMessage("in getCurrentThreadLivenessStatus(), caught exception: "
0563: + ex); // NOI18N
0564: }
0565:
0566: return null;
0567: }
0568: }
0569:
0570: /**
0571: * For the class with the given name and the initiating class loader (see Java Language/JVM Spec for definitions),
0572: * find out and return the defining class loader. Both class loaders are internal class loader ids.
0573: */
0574: public synchronized int getDefiningClassLoaderId(String className,
0575: int initiatingLoaderId)
0576: throws ClientUtils.TargetAppOrVMTerminated {
0577: checkForTargetVMAlive();
0578:
0579: GetDefiningClassLoaderCommand cmd = new GetDefiningClassLoaderCommand(
0580: className, initiatingLoaderId);
0581: sendComplexCmdToServer(cmd);
0582:
0583: DefiningLoaderResponse resp = (DefiningLoaderResponse) getAndCheckLastResponse("Unknown problem when trying to get a defining loader for class" // NOI18N
0584: ); // NOI18N
0585:
0586: return resp.getLoaderId();
0587: }
0588:
0589: public FlatProfileProvider getFlatProfileProvider() {
0590: return flatProvider;
0591: }
0592:
0593: public long getInstrProcessingTime() {
0594: return instrProcessingTime;
0595: }
0596:
0597: //---------------- Internal statistics and other target VM information obtaining ----------------
0598: public synchronized InternalStatsResponse getInternalStats()
0599: throws ClientUtils.TargetAppOrVMTerminated {
0600: checkForTargetVMAlive();
0601: sendSimpleCmdToServer(Command.GET_INTERNAL_STATS);
0602:
0603: InternalStatsResponse resp = (InternalStatsResponse) getLastResponse();
0604:
0605: return resp;
0606: }
0607:
0608: public CCTResultsFilter getMarkFilter() {
0609: return markFilter;
0610: }
0611:
0612: public MemoryCCTProvider getMemoryCCTProvider() {
0613: return memCctProvider;
0614: }
0615:
0616: /**
0617: * Returns the snapshot of current Memory profiling results
0618: *
0619: * @return Memory Results snapshot
0620: * @throws ClientUtils.TargetAppOrVMTerminated
0621: * In case the profiled application has already terminated
0622: */
0623: public synchronized MemoryResultsSnapshot getMemoryProfilingResultsSnapshot()
0624: throws ClientUtils.TargetAppOrVMTerminated {
0625: return getMemoryProfilingResultsSnapshot(true);
0626: }
0627:
0628: /**
0629: * Returns the snapshot of current Memory profiling results
0630: *
0631: * @param dump true to fetch latest events from server, false otherwise (use only available data)
0632: * @return Memory Results snapshot
0633: * @throws ClientUtils.TargetAppOrVMTerminated
0634: * In case the profiled application has already terminated
0635: */
0636: public synchronized MemoryResultsSnapshot getMemoryProfilingResultsSnapshot(
0637: boolean dump) throws ClientUtils.TargetAppOrVMTerminated {
0638: checkForTargetVMAlive();
0639:
0640: if (dump) {
0641: if (!forceObtainedResultsDump(false, 5)) {
0642: return null;
0643: }
0644: }
0645:
0646: memCctProvider.beginTrans(false);
0647:
0648: try {
0649: memCctProvider.updateInternals();
0650:
0651: if (getCurrentInstrType() == INSTR_OBJECT_ALLOCATIONS) {
0652: return new AllocMemoryResultsSnapshot(resultsStart,
0653: System.currentTimeMillis(), memCctProvider,
0654: this );
0655: } else {
0656: return new LivenessMemoryResultsSnapshot(resultsStart,
0657: System.currentTimeMillis(), memCctProvider,
0658: this );
0659: }
0660: } finally {
0661: memCctProvider.endTrans();
0662: }
0663: }
0664:
0665: public Marker getMethodMarker() {
0666: return settings.getMethodMarker();
0667: }
0668:
0669: /**
0670: * Called to obtain method names for jMethodIds, that we do not know method names of.
0671: * This method is typically called when results are to be displayed, but also in case some classes are unloaded
0672: * in the profiled application, as in this case we would lost method names for already accumulated results.
0673: * <p/>
0674: * Assumption is that jMethodId is never reused inside the JVM.
0675: *
0676: * @param methodIds array of jMethodIds that we do not have names for
0677: * @return the 3xn array, containing triplets of {class name, method name, method signature} strings for
0678: * given jmethodIds
0679: */
0680: public synchronized String[][] getMethodNamesForJMethodIds(
0681: int[] methodIds) throws ClientUtils.TargetAppOrVMTerminated {
0682: checkForTargetVMAlive();
0683:
0684: GetMethodNamesForJMethodIdsCommand cmd = new GetMethodNamesForJMethodIdsCommand(
0685: methodIds);
0686: sendComplexCmdToServer(cmd);
0687:
0688: MethodNamesResponse resp = (MethodNamesResponse) getAndCheckLastResponse("Unknown problem when trying to get method names for jmethodIds" // NOI18N
0689: ); // NOI18N
0690:
0691: return StringUtils.convertPackedStringsIntoStringArrays(resp
0692: .getPackedData(), resp.getPackedArrayOffsets(), 3);
0693: }
0694:
0695: public synchronized MonitoredData getMonitoredData() {
0696: try {
0697: checkForTargetVMAlive();
0698: sendSimpleCmdToServer(Command.GET_MONITORED_NUMBERS);
0699:
0700: Response resp = getAndCheckLastResponse("Unknown problem when trying to get memory numbers."); // NOI18N
0701:
0702: try {
0703: MonitoredNumbersResponse mresp = (MonitoredNumbersResponse) resp;
0704:
0705: return MonitoredData.getMonitoredData(mresp);
0706: } catch (ClassCastException ex) {
0707: // FIXME: this diagnostics stuff should be ultimately removed once the root cause of the problem is understood
0708: MiscUtils
0709: .printErrorMessage("caught ClassCastException in getMonitoredNumbers. The real class of resp is " // NOI18N
0710: + resp.getClass().getName()
0711: + ", resp = " + resp // NOI18N
0712: );
0713: throw ex;
0714: }
0715: } catch (ClientUtils.TargetAppOrVMTerminated ex) {
0716: if (serverListener.isRunning()) { // The possibly problematic situation is not known yet
0717: MiscUtils
0718: .printErrorMessage("in getMonitoredData(), caught exception: "
0719: + ex); // NOI18N
0720: }
0721:
0722: return null;
0723: }
0724: }
0725:
0726: /**
0727: * @return ProfilerEngineSettings current profiler engine settings
0728: */
0729: public ProfilerEngineSettings getSettings() {
0730: return settings;
0731: }
0732:
0733: /**
0734: * We are using this essentially to let user know when the longest part of what happens after hitting e.g.
0735: * "Instrument Object Allocation", is complete. Otherwise it might be difficult to figure out what's going on
0736: */
0737:
0738: // public boolean isInstrMethodGroupFromRootComplete() { return instrMethodGroupFromRootComplete; }
0739: // public CPUCallGraphBuilder getCPUCallGraphBuilder() { return ccgb; }
0740: // public MemoryCallGraphBuilder getMemoryCallGraphBuilder() { return mcgb; }
0741: public ObjectInputStream getSocketInputStream() {
0742: return socketIn;
0743: }
0744:
0745: public ProfilingSessionStatus getStatus() {
0746: return status;
0747: }
0748:
0749: public TimeCollector getTimeCollector() {
0750: return timeCollector;
0751: }
0752:
0753: public synchronized boolean cpuResultsExist()
0754: throws ClientUtils.TargetAppOrVMTerminated {
0755: checkForTargetVMAlive();
0756: sendSimpleCmdToServer(Command.CPU_RESULTS_EXIST);
0757:
0758: Response resp = getAndCheckLastResponse("Unknown problem when trying to check for CPU profiling results." // NOI18N
0759: ); // NOI18N
0760:
0761: return resp.yes();
0762: }
0763:
0764: public boolean currentInstrTypeIsMemoryProfiling() {
0765: return ((status.currentInstrType == INSTR_OBJECT_ALLOCATIONS) || (status.currentInstrType == INSTR_OBJECT_LIVENESS));
0766: }
0767:
0768: public boolean currentInstrTypeIsRecursiveCPUProfiling() {
0769: return ((status.currentInstrType == INSTR_RECURSIVE_FULL) || (status.currentInstrType == INSTR_RECURSIVE_SAMPLED));
0770: }
0771:
0772: /**
0773: * Removes instrumentation for classes with ids such that unprofiledClassStatusArray[id] == false.
0774: * For these classes, no memory profiling data will be generated anymore.
0775: */
0776: public void deinstrumentMemoryProfiledClasses(
0777: boolean[] unprofiledClassStatusArray)
0778: throws InstrumentationException,
0779: ClientUtils.TargetAppOrVMTerminated {
0780: synchronized (instrumentationLock) {
0781: if (getCurrentInstrType() == INSTR_NONE) {
0782: return;
0783: }
0784:
0785: Response resp = null;
0786: checkForTargetAppRunning();
0787:
0788: long curTime = System.currentTimeMillis();
0789: InstrumentMethodGroupCommand cmd = instrumentor
0790: .getCommandToUnprofileClasses(unprofiledClassStatusArray);
0791:
0792: if (!cmd.isEmpty()) {
0793: synchronized (this ) {
0794: // System.out.println("*** Profiler Engine: deinstrumentMemoryProfiledClasses() produced command:"); cmd.dump();
0795: sendComplexCmdToServer(cmd);
0796: instrProcessingTime += (System.currentTimeMillis() - curTime);
0797: resp = getLastResponse();
0798: }
0799:
0800: if (!resp.isOK()) {
0801: throw new InstrumentationException(resp
0802: .getErrorMessage());
0803: }
0804: }
0805: }
0806: }
0807:
0808: public synchronized void detachFromTargetJVM()
0809: throws ClientUtils.TargetAppOrVMTerminated {
0810: checkForTargetVMAlive();
0811: terminateOrDetachCommandIssued = true;
0812: sendSimpleCmdToServer(Command.DETACH);
0813:
0814: try {
0815: getLastResponse();
0816: } finally {
0817: closeConnection();
0818: EventBufferProcessor.removeEventBufferFile(); // Try again, just in case closeConnection returned without calling it
0819: }
0820: }
0821:
0822: /**
0823: * This is called in all modes, direct invoke or attachment, to establish connection with the target VM
0824: * @param attachMode 0 = no attach, 1 = direct attach, 2 = dynamic attach
0825: */
0826: public boolean establishConnectionWithServer(int attachMode,
0827: boolean calibrationOnlyRun) {
0828: // Make sure we initialize this field early - it may be changed once we connect to the JVM and find out its
0829: // real version.
0830: status.targetJDKVersionString = settings
0831: .getTargetJDKVersionString();
0832:
0833: return connectToServer(attachMode, calibrationOnlyRun);
0834: }
0835:
0836: /**
0837: * Tells the server to send the contents of its data buffer to the tool immediately, no matter whether it's
0838: * full or not.
0839: */
0840: public boolean forceObtainedResultsDump()
0841: throws ClientUtils.TargetAppOrVMTerminated {
0842: return forceObtainedResultsDump(false, 0);
0843: }
0844:
0845: public boolean forceObtainedResultsDump(boolean liveResults,
0846: int retries) throws ClientUtils.TargetAppOrVMTerminated {
0847: boolean dumped = false;
0848: int retryCounter = retries;
0849:
0850: do {
0851: dumped = forceObtainedResultsDump(liveResults);
0852:
0853: if (!dumped) {
0854: try {
0855: Thread.sleep(200);
0856: } catch (InterruptedException e) {
0857: break;
0858: }
0859: }
0860: } while (!dumped && (--retryCounter > 0));
0861:
0862: return dumped;
0863: }
0864:
0865: /**
0866: * Tells the server to send the contents of its data buffer to the tool immediately, no matter whether it's
0867: * full or not.
0868: */
0869: public boolean forceObtainedResultsDump(boolean liveResults)
0870: throws ClientUtils.TargetAppOrVMTerminated {
0871: // The locks below are in the special order, to prevent deadlocks
0872: synchronized (this ) {
0873: synchronized (forceObtainedResultsDumpLock) {
0874: if (handlingEventBufferDump) {
0875: return true; // If dump handling is already in progress, don't force the second dump
0876: }
0877:
0878: // no reason (and may be dangerous) to send another force dump command
0879: checkForTargetVMAlive();
0880: forceObtainedResultsDumpCalled = true;
0881: sendSimpleCmdToServer(liveResults ? Command.DUMP_EXISTING_RESULTS_LIVE
0882: : Command.DUMP_EXISTING_RESULTS);
0883:
0884: DumpResultsResponse resp = (DumpResultsResponse) getLastResponse(); // NOI18N
0885:
0886: if (resp.yes()) {
0887: status.dumpAbsTimeStamp = resp
0888: .getDumpAbsTimeStamp();
0889: } else {
0890: if (ProfilerLogger.isDebug()) {
0891: ProfilerLogger
0892: .debug("Force Obtained Results - Received Dump Error "); // NOI18N
0893: }
0894: }
0895:
0896: forceObtainedResultsDumpCalled = false;
0897:
0898: return resp.yes();
0899: }
0900: }
0901: }
0902:
0903: /**
0904: * This should be called to initiate code region instrumentation for specified code region.
0905: * The data is remembered or sent to the server immediately if TA is already running. The actual instrumentation
0906: * starts when server informs the tool that the class to be instrumented is loaded.
0907: */
0908: public void initiateCodeRegionInstrumentation(
0909: ClientUtils.SourceCodeSelection[] s)
0910: throws ClassNotFoundException, BadLocationException,
0911: InstrumentationException, IOException, ClassFormatError,
0912: ClientUtils.TargetAppOrVMTerminated {
0913: synchronized (instrumentationLock) {
0914: removeAllInstrumentation();
0915:
0916: if (status.targetAppRunning && status.remoteProfiling) {
0917: if (!getCalibrationData(true)) {
0918: return;
0919: }
0920: }
0921:
0922: instrumentor.setStatusInfoFromSourceCodeSelection(s);
0923: instrumentor.setSavedSourceCodeSelection(s);
0924:
0925: String className = instrumentor.getRootClassNames()[ProfilingSessionStatus.CODE_REGION_CLASS_IDX]
0926: .replace('/', '.'); // NOI18N
0927: InitiateInstrumentationCommand cmd = new InitiateInstrumentationCommand(
0928: INSTR_CODE_REGION, className, false,
0929: status.startProfilingPointsActive);
0930: commandOnStartup = cmd;
0931:
0932: setCurrentInstrType(INSTR_CODE_REGION);
0933:
0934: if (status.targetAppRunning) {
0935: sendSetInstrumentationParamsCmd(false);
0936:
0937: String errorMessage = sendCommandAndGetResponse(commandOnStartup);
0938:
0939: if (errorMessage != null) {
0940: appStatusHandler.displayWarning(errorMessage);
0941: }
0942: }
0943: }
0944: }
0945:
0946: /**
0947: * This should be called to initiate memory profiling instrumentation of specified type (object allocation or
0948: * object liveness).
0949: * The data is remembered or sent to the server immediately if TA is already running. The actual instrumentation
0950: * starts when the TA is started and the first class of this app is loaded, or immediately if TA is already running.
0951: */
0952: public void initiateMemoryProfInstrumentation(int instrType)
0953: throws ClientUtils.TargetAppOrVMTerminated,
0954: InstrumentationException {
0955: synchronized (instrumentationLock) {
0956: removeAllInstrumentation();
0957:
0958: // Set this root class name irrespective of whether the target app has been started or not.
0959: // If it's not yet started, then indeed instrumentation should be triggered by main class load event - otherwise
0960: // the first loaded class that we register in the server is some reflection class loaded in process of main()
0961: // invocation. It causes recursive invocations of classLoadHook() (because it also uses some reflection), thus
0962: // screwing up the instrumentation procedure.
0963: // If the target app is already running, then instrumentation starts immediately and isn't triggered by a class
0964: // load event. However, if the same cmd that we build here is then re-used as commandOnStartup, it should again
0965: // contain rootClassName.
0966: String rootClassName = settings.getMainClassName();
0967: InitiateInstrumentationCommand cmd = new InitiateInstrumentationCommand(
0968: instrType, rootClassName, false,
0969: status.startProfilingPointsActive);
0970: cmd
0971: .setProfilingPoints(settings
0972: .getRuntimeProfilingPoints());
0973: commandOnStartup = cmd;
0974:
0975: // switch (instrType) {
0976: // case INSTR_OBJECT_ALLOCATIONS:
0977: // mcgb = new ObjAllocCallGraphBuilder(this);
0978: // break;
0979: // case INSTR_OBJECT_LIVENESS:
0980: // mcgb = new ObjLivenessCallGraphBuilder(this);
0981: // break;
0982: // }
0983:
0984: // See initiateRecursiveCPUProfInstrumentation for why it's important to setCurrentInstrType() early
0985: setCurrentInstrType(instrType);
0986:
0987: if (status.targetAppRunning) {
0988: sendSetInstrumentationParamsCmd(false);
0989:
0990: String errorMessage = sendCommandAndGetResponse(commandOnStartup);
0991:
0992: if (errorMessage != null) {
0993: appStatusHandler.displayWarning(errorMessage);
0994: }
0995: }
0996:
0997: //instrMethodGroupFromRootComplete = false;
0998: }
0999: }
1000:
1001: /**
1002: * This should be called to initiate CPU profiling instrumentation starting from specified root method(s).
1003: * The data is remembered or sent to the server immediately if TA is already running. The actual instrumentation
1004: * starts when server informs the tool that one of the classes to be instrumented is loaded.
1005: */
1006: public void initiateRecursiveCPUProfInstrumentation(
1007: ClientUtils.SourceCodeSelection[] s)
1008: throws ClassNotFoundException, BadLocationException,
1009: InstrumentationException, IOException, ClassFormatError,
1010: ClientUtils.TargetAppOrVMTerminated {
1011: // System.out.println("Initiating CPU instrumentation");
1012: // for(int i=0;i<s.length;i++) {
1013: // System.out.println(s[i]);
1014: // }
1015: synchronized (instrumentationLock) {
1016: removeAllInstrumentation();
1017:
1018: if (status.targetAppRunning && status.remoteProfiling) {
1019: if (!getCalibrationData(true)) {
1020: return;
1021: }
1022: }
1023:
1024: instrumentor.setStatusInfoFromSourceCodeSelection(s);
1025:
1026: boolean instrSpawnedThreads = settings
1027: .getInstrumentSpawnedThreads();
1028:
1029: String[] rootClassNames = instrumentor.getRootClassNames();
1030: int instrType = (settings.getCPUProfilingType() == CPU_INSTR_FULL) ? INSTR_RECURSIVE_FULL
1031: : INSTR_RECURSIVE_SAMPLED;
1032: InitiateInstrumentationCommand cmd = new InitiateInstrumentationCommand(
1033: instrType, rootClassNames, instrSpawnedThreads,
1034: status.startProfilingPointsActive);
1035: cmd
1036: .setProfilingPoints(settings
1037: .getRuntimeProfilingPoints());
1038: commandOnStartup = cmd;
1039: status.setTimerTypes(settings.getAbsoluteTimerOn(),
1040: settings.getThreadCPUTimerOn());
1041:
1042: // the following code is moved to the CPUCallGraphBuilder.startup() method
1043: // switch (instrType) {
1044: // case INSTR_RECURSIVE_FULL:
1045: // ccgb = new FullInstrCPUCallGraphBuilder(this);
1046: // break;
1047: // case INSTR_RECURSIVE_SAMPLED:
1048: // ccgb = new SampledInstrCPUCallGraphBuilder(this);
1049: // break;
1050: // }
1051:
1052: // It's important that we set current instr type *before* we make the following call. That's because,
1053: // if targetAppRunning, at the server side all the operations in reaction to the commandOnStartup are performed
1054: // in a separate thread. It appears that that thread may quickly send back the response with loaded classes etc..,
1055: // and that may happen *before* we get response below and set currentInstrType. This fixes that
1056: // (essentially, race condition) bug.
1057: setCurrentInstrType(instrType);
1058:
1059: if (status.targetAppRunning) {
1060: sendSetInstrumentationParamsCmd(false);
1061:
1062: String errorMessage = sendCommandAndGetResponse(commandOnStartup);
1063:
1064: if (errorMessage != null) {
1065: appStatusHandler.displayWarning(errorMessage);
1066: }
1067: }
1068:
1069: // CPUResultsDispatcher.getInstance().setProfilerClient(this); // initialize CPUResultsDispatcher
1070: //instrMethodGroupFromRootComplete = false;
1071: }
1072: }
1073:
1074: public synchronized boolean memoryResultsExist() {
1075: return (getMemoryCCTProvider() != null)
1076: && (getMemoryCCTProvider().getStacksForClasses() != null);
1077: }
1078:
1079: /*
1080: * A callback method to be executed at the beginning of profiling;
1081: * will disappear once the snapshot generation routine is rewritten
1082: */
1083: public void registerCPUCCTProvider(CPUCCTProvider provider) {
1084: cpuCctProvider = provider;
1085: }
1086:
1087: public void registerFlatProfileProvider(FlatProfileProvider provider) {
1088: flatProvider = provider;
1089: }
1090:
1091: public void registerMarkFilter(CCTResultsFilter filter) {
1092: markFilter = filter;
1093: }
1094:
1095: public void registerMemoryCCTProvider(MemoryCCTProvider provider) {
1096: memCctProvider = provider;
1097: }
1098:
1099: public void registerTimeCollector(TimeCollector collector) {
1100: timeCollector = collector;
1101: }
1102:
1103: public void removeAllInstrumentation(boolean cleanupClient)
1104: throws InstrumentationException {
1105: synchronized (instrumentationLock) {
1106: if (getCurrentInstrType() == INSTR_NONE) {
1107: return;
1108: }
1109:
1110: commandOnStartup = null;
1111:
1112: if (cleanupClient) {
1113: status.resetInstrClassAndMethodInfo();
1114: }
1115:
1116: try {
1117: clearPreviousInstrumentationInServer();
1118: } catch (ClientUtils.TargetAppOrVMTerminated ex) { /* So be it */
1119: }
1120:
1121: setCurrentInstrType(INSTR_NONE);
1122:
1123: //instrMethodGroupFromRootComplete = true; // False means we are awaiting this event or it's being processed
1124: }
1125: }
1126:
1127: public void removeAllInstrumentation()
1128: throws InstrumentationException {
1129: removeAllInstrumentation(true);
1130: }
1131:
1132: /**
1133: * If the target VM is terminated, cleans up all localy cached data so that they can be
1134: * GCd from the memory. If the TA is running, this method does nothing.
1135: */
1136: public void resetClientData() {
1137: if (targetJVMIsAlive()) {
1138: return; // we should not do any of the following in this case
1139: }
1140:
1141: status.resetInstrClassAndMethodInfo();
1142: instrumentor.resetPerVMInstanceData();
1143:
1144: // CPUResultsDispatcher.getInstance().stop();
1145: // mcgb = null;
1146: // CPUResultsDispatcher.getInstance().shutdown();
1147: }
1148:
1149: public synchronized void resetProfilerCollectors()
1150: throws ClientUtils.TargetAppOrVMTerminated {
1151: checkForTargetVMAlive();
1152: sendSimpleCmdToServer(Command.RESET_PROFILER_COLLECTORS);
1153: getAndCheckLastResponse("Unknown problem when trying to reset profiler collectors."); // NOI18N
1154: }
1155:
1156: public synchronized void resumeTargetAppThreads()
1157: throws ClientUtils.TargetAppOrVMTerminated {
1158: checkForTargetAppRunning();
1159: sendSimpleCmdToServer(Command.RESUME_TARGET_APP);
1160: getAndCheckLastResponse("Unknown problem when trying to resume app threads."); // NOI18N
1161: }
1162:
1163: public synchronized void runGC()
1164: throws ClientUtils.TargetAppOrVMTerminated {
1165: checkForTargetVMAlive();
1166: sendSimpleCmdToServer(Command.RUN_GC);
1167: getAndCheckLastResponse("Unknown problem when trying to run GC"); // NOI18N
1168: }
1169:
1170: public void sendSetInstrumentationParamsCmd(boolean changeableOnly)
1171: throws ClientUtils.TargetAppOrVMTerminated {
1172: SetChangeableInstrParamsCommand cmd = new SetChangeableInstrParamsCommand(
1173: settings.getNProfiledThreadsLimit(), settings
1174: .getSamplingInterval(), settings
1175: .getAllocTrackEvery(), settings
1176: .getAllocStackTraceLimit(), settings
1177: .getRunGCOnGetResultsInMemoryProfiling(),
1178: settings.getExcludeWaitTime(), settings
1179: .getExcludeWaitTime());
1180:
1181: String errorMessage = sendCommandAndGetResponse(cmd);
1182:
1183: if (errorMessage != null) {
1184: appStatusHandler.displayWarning(errorMessage);
1185: }
1186:
1187: if (!changeableOnly) {
1188: SetUnchangeableInstrParamsCommand cmd1 = new SetUnchangeableInstrParamsCommand(
1189: settings.getAbsoluteTimerOn(), settings
1190: .getThreadCPUTimerOn(), settings
1191: .getInstrScheme(), settings
1192: .getCodeRegionCPUResBufSize());
1193: errorMessage = sendCommandAndGetResponse(cmd1);
1194:
1195: if (errorMessage != null) {
1196: appStatusHandler.displayWarning(errorMessage);
1197: }
1198: }
1199: }
1200:
1201: /**
1202: * This method is called both when the application is started by the tool, and when the tool attaches to a running
1203: * application.
1204: * It's called *after* the establishConnectionWithServer() above.
1205: * sendExplicitStartCommand actually determines the mode - it's true if we really start the VM as opposed to
1206: * attaching.
1207: */
1208: public boolean startTargetApp(boolean sendExplicitStartCommand)
1209: throws ClientUtils.TargetAppOrVMTerminated,
1210: ClientUtils.TargetAppFailedToStart {
1211: status.resetInstrClassAndMethodInfo();
1212: instrumentor.resetPerVMInstanceData();
1213: status.setTimerTypes(settings.getAbsoluteTimerOn(), settings
1214: .getThreadCPUTimerOn());
1215: serverCommandHandler.handleServerCommand(null); // To reset the displayed figures
1216: checkForTargetVMAlive();
1217: instrProcessingTime = 0;
1218: instrMethodsLimitReported = false;
1219:
1220: // Special treatment of the case when instrumentation type is changed between runs by simply
1221: // switching a button in Settings
1222: if (currentInstrTypeIsRecursiveCPUProfiling()) {
1223: setCurrentInstrType((settings.getCPUProfilingType() == CPU_INSTR_FULL) ? INSTR_RECURSIVE_FULL
1224: : INSTR_RECURSIVE_SAMPLED);
1225:
1226: if (commandOnStartup != null) {
1227: commandOnStartup.setInstrType(getCurrentInstrType());
1228: }
1229: }
1230:
1231: if (commandOnStartup != null) {
1232: // Always set the current instrumentation parameters first
1233: sendSetInstrumentationParamsCmd(false);
1234:
1235: switch (getCurrentInstrType()) {
1236: case INSTR_CODE_REGION:
1237:
1238: if (status.remoteProfiling && !getCalibrationData(true)) {
1239: try {
1240: terminateTargetJVM();
1241: } catch (ClientUtils.TargetAppOrVMTerminated e) {
1242: }
1243:
1244: return false;
1245: }
1246:
1247: break;
1248: case INSTR_RECURSIVE_FULL:
1249: case INSTR_RECURSIVE_SAMPLED:
1250:
1251: if (status.remoteProfiling && !getCalibrationData(true)) {
1252: try {
1253: terminateTargetJVM();
1254: } catch (ClientUtils.TargetAppOrVMTerminated e) {
1255: }
1256:
1257: return false;
1258: }
1259:
1260: if (settings.getInstrumentMethodInvoke()) {
1261: String error = sendSimpleCommandAndGetResponse(Command.INSTRUMENT_REFLECTION);
1262:
1263: if (error != null) {
1264: throw new ClientUtils.TargetAppFailedToStart(
1265: error);
1266: }
1267: }
1268:
1269: // the following code is moved to the CPUCallGraphBuilder.startup() method
1270: // if (getCurrentInstrType() == INSTR_RECURSIVE_FULL) {
1271: // ccgb = new FullInstrCPUCallGraphBuilder(this);
1272: // } else {
1273: // ccgb = new SampledInstrCPUCallGraphBuilder(this);
1274: // }
1275: // CPUResultsDispatcher.getInstance().setProfilerClient(this); // initialize CPUResultsDispatcher
1276: break;
1277:
1278: // case INSTR_OBJECT_ALLOCATIONS:
1279: // mcgb = new ObjAllocCallGraphBuilder(this);
1280: // break;
1281: // case INSTR_OBJECT_LIVENESS:
1282: // mcgb = new ObjLivenessCallGraphBuilder(this);
1283: // break;
1284: }
1285:
1286: String errorMessage = sendCommandAndGetResponse(commandOnStartup);
1287:
1288: if (errorMessage != null) {
1289: appStatusHandler
1290: .displayWarning("Profiler Agent Error: "
1291: + errorMessage); // NOI18N
1292: }
1293:
1294: commandOnStartup = null;
1295: } else {
1296: // Needed to e.g. prevent initiateInstrumentation() called later from attempting to
1297: // remove instrumentation from VM.
1298: setCurrentInstrType(INSTR_NONE);
1299: }
1300:
1301: if (sendExplicitStartCommand) {
1302: String error = sendSimpleCommandAndGetResponse(Command.START_TARGET_APP);
1303:
1304: if (error != null) {
1305: throw new ClientUtils.TargetAppFailedToStart(error);
1306: }
1307: }
1308:
1309: status.targetAppRunning = true;
1310: checkForInstrMethodsLimitReached();
1311: EventBufferResultsProvider.getDefault().startup(this );
1312:
1313: return true;
1314: }
1315:
1316: //---------------- Target Application Thread Management ----------------
1317: public synchronized void suspendTargetAppThreads()
1318: throws ClientUtils.TargetAppOrVMTerminated {
1319: checkForTargetAppRunning();
1320: sendSimpleCmdToServer(Command.SUSPEND_TARGET_APP);
1321: getAndCheckLastResponse("Unknown problem when trying to suspend app threads."); // NOI18N
1322: }
1323:
1324: public synchronized boolean takeHeapDump(String outputFile)
1325: throws ClientUtils.TargetAppOrVMTerminated {
1326: checkForTargetVMAlive();
1327: sendComplexCmdToServer(new TakeHeapDumpCommand(outputFile));
1328:
1329: Response resp = getAndCheckLastResponse("takeHeapDump."); // NOI18N
1330:
1331: return resp.isOK();
1332: }
1333:
1334: public boolean targetAppIsRunning() {
1335: return status.targetAppRunning;
1336: }
1337:
1338: public boolean targetJVMIsAlive() {
1339: return targetVMAlive;
1340: }
1341:
1342: //---------------- Target Application/JVM Status Management ----------------
1343: public synchronized void terminateTargetJVM()
1344: throws ClientUtils.TargetAppOrVMTerminated {
1345: checkForTargetVMAlive();
1346: terminateOrDetachCommandIssued = true;
1347: sendSimpleCmdToServer(Command.TERMINATE_TARGET_JVM);
1348:
1349: if (!getLastResponse().isOK()) {
1350: throw new ClientUtils.TargetAppOrVMTerminated(
1351: ClientUtils.TargetAppOrVMTerminated.VM,
1352: "Target JVM terminated or not responding" // NOI18N
1353: );
1354: }
1355:
1356: closeConnection();
1357: }
1358:
1359: private synchronized Response getAndCheckLastResponse(
1360: String errMessage)
1361: throws ClientUtils.TargetAppOrVMTerminated {
1362: Response resp = getLastResponse();
1363:
1364: if (!resp.isOK()) {
1365: MiscUtils
1366: .printErrorMessage("error in getAndCheckLastResponse: for "
1367: + resp
1368: + " got error message: " // NOI18N
1369: + resp.getErrorMessage()
1370: + " and context message " + errMessage // NOI18N
1371: ); // NOI18N
1372: }
1373:
1374: return resp;
1375: }
1376:
1377: private synchronized boolean getCalibrationData(
1378: boolean getStoredData)
1379: throws ClientUtils.TargetAppOrVMTerminated {
1380: int cmdType = getStoredData ? Command.GET_STORED_CALIBRATION_DATA
1381: : Command.RUN_CALIBRATION_AND_GET_DATA;
1382: sendSimpleCmdToServer(cmdType);
1383:
1384: Response resp = getLastResponse();
1385:
1386: if (!resp.isOK()) {
1387: String msg = resp.getErrorMessage();
1388:
1389: if (getStoredData) {
1390: msg = MessageFormat.format(
1391: CORRUPTED_TARGET_CALIBRATION_DATA_MSG,
1392: new Object[] { msg });
1393: }
1394:
1395: appStatusHandler.displayError(msg);
1396:
1397: return false;
1398: }
1399:
1400: CalibrationDataResponse cdr = (CalibrationDataResponse) resp;
1401: status.methodEntryExitCallTime = cdr
1402: .getMethodEntryExitCallTime();
1403: status.methodEntryExitInnerTime = cdr
1404: .getMethodEntryExitInnerTime();
1405: status.methodEntryExitOuterTime = cdr
1406: .getMethodEntryExitOuterTime();
1407: status.timerCountsInSecond = cdr.getTimerCountsInSecond();
1408:
1409: return true;
1410: }
1411:
1412: private void setLastResponse(Response r) {
1413: synchronized (responseLock) {
1414: lastResponse = r;
1415:
1416: try {
1417: responseLock.notify();
1418: } catch (IllegalMonitorStateException ex) {
1419: MiscUtils
1420: .internalError("ProfilerClient.setLastResponse()"); // NOI18N
1421: }
1422: }
1423: }
1424:
1425: private synchronized Response getLastResponse()
1426: throws ClientUtils.TargetAppOrVMTerminated {
1427: Response res;
1428:
1429: checkForTargetVMAlive();
1430: synchronized (responseLock) {
1431: while (lastResponse == null) {
1432: long start = System.currentTimeMillis();
1433:
1434: try {
1435: responseLock.wait(60000);
1436: } catch (InterruptedException ex) {
1437: MiscUtils
1438: .internalError("InterruptedException in ProfilerClient.getLastResponse()"); // NOI18N
1439: }
1440:
1441: // If we have been waiting for above number of milliseconds and got no response, assume that we timed out
1442: // and target JVM is dead
1443: if (!targetVMAlive) {
1444: status.targetAppRunning = false;
1445: targetVMAlive = false;
1446: throw new ClientUtils.TargetAppOrVMTerminated(
1447: ClientUtils.TargetAppOrVMTerminated.VM);
1448: } else if (lastResponse == null
1449: && wireIO.wasAlive() < start) { // timed out
1450: if (!appStatusHandler
1451: .confirmWaitForConnectionReply()) {
1452: status.targetAppRunning = false;
1453: targetVMAlive = false;
1454: throw new ClientUtils.TargetAppOrVMTerminated(
1455: ClientUtils.TargetAppOrVMTerminated.VM);
1456: }
1457: }
1458: }
1459: res = lastResponse;
1460: lastResponse = null;
1461: }
1462:
1463: return res;
1464: }
1465:
1466: /**
1467: * Set at least some of the properties related to execution of the target JVM.
1468: * If we attach to the target VM on-the-fly, we need to get everything from it.
1469: * Otherwise, there are still some properties that we can guess in principle, but which we better ask the VM
1470: * for, such as Java extension class path dirs and Java boot class path.
1471: */
1472: private boolean setVMProperties(VMPropertiesResponse resp,
1473: boolean terminateOnError) {
1474: if (resp.getAgentVersion() != CommonConstants.CURRENT_AGENT_VERSION) {
1475: appStatusHandler
1476: .displayWarning(INCORRECT_AGENT_VERSION_MSG);
1477: }
1478:
1479: // Check if the VM version is supported by the Profiler
1480: String jdkVersionString = resp.getJDKVersionString();
1481:
1482: if (!MiscUtils.isSupportedRunningJVMVersion(jdkVersionString)) {
1483: String message = MessageFormat.format(UNSUPPORTED_JVM_MSG,
1484: new Object[] { jdkVersionString });
1485: appStatusHandler.displayErrorAndWaitForConfirm(message);
1486:
1487: try {
1488: if (terminateOnError) {
1489: terminateTargetJVM();
1490: } else {
1491: detachFromTargetJVM();
1492: }
1493: } catch (ClientUtils.TargetAppOrVMTerminated ex) {
1494: }
1495:
1496: return false;
1497: }
1498:
1499: // Check the VM version, and if it doesn't match the one set in the tool, check if we have saved calibration data
1500: // for this VM version
1501: String jdkVersionName = Platform
1502: .getJDKVersionString(jdkVersionString);
1503: settings.setTargetJDKVersionString(jdkVersionName);
1504: status.targetJDKVersionString = jdkVersionName;
1505: status.fullTargetJDKVersionString = jdkVersionString;
1506: currentAgentId = resp.getAgentId();
1507:
1508: if (!status.remoteProfiling) {
1509: int res = CalibrationDataFileIO
1510: .readSavedCalibrationData(status);
1511:
1512: if (res < 0) { // Fatal error with reading saved file data - report the details
1513:
1514: String message = MessageFormat.format(
1515: ERROR_GETTING_CALIBRATION_DATA_MSG,
1516: new Object[] { CalibrationDataFileIO
1517: .getErrorMessage() });
1518: appStatusHandler.displayErrorAndWaitForConfirm(message);
1519:
1520: return false;
1521: } else if (res > 0) { // Saved data file doesn't exist - notify the user and stop
1522: appStatusHandler
1523: .displayErrorWithDetailsAndWaitForConfirm(
1524: MUST_CALIBRATE_FIRST_SHORT_MSG,
1525: MUST_CALIBRATE_FIRST_MSG);
1526:
1527: try {
1528: if (terminateOnError) {
1529: terminateTargetJVM();
1530: } else {
1531: detachFromTargetJVM();
1532: }
1533: } catch (ClientUtils.TargetAppOrVMTerminated ex) {
1534: }
1535:
1536: return false;
1537: }
1538: }
1539:
1540: status.jvmArguments = resp.getJVMArguments();
1541: status.javaCommand = resp.getJavaCommand();
1542: status.targetMachineOSName = resp.getTargetMachineOSName();
1543: status.maxHeapSize = resp.getMaxHeapSize();
1544: status.startupTimeMillis = resp.getStartupTimeMillis();
1545: status.startupTimeInCounts = resp.getStartupTimeInCounts();
1546:
1547: if (!status.remoteProfiling) {
1548: settings.setWorkingDir(resp.getWorkingDir());
1549: settings.setVMClassPaths(resp.getJavaClassPath(), resp
1550: .getJavaExtDirs(), resp.getBootClassPath());
1551: ClassRepository.initClassPaths(settings.getWorkingDir(),
1552: settings.getVMClassPaths());
1553: }
1554:
1555: return true;
1556: }
1557:
1558: /**
1559: * Check if we can't instrument more methods because the 64K limit is reachec
1560: */
1561: private void checkForInstrMethodsLimitReached() {
1562: if ((status.getStartingMethodId() >= 65535)
1563: && !instrMethodsLimitReported
1564: && status.targetAppRunning) {
1565: appStatusHandler
1566: .displayWarningAndWaitForConfirm(INSTRUMENTATION_LIMIT_REACHED_MSG);
1567: instrMethodsLimitReported = true;
1568: }
1569: }
1570:
1571: private void checkForTargetAppRunning()
1572: throws ClientUtils.TargetAppOrVMTerminated {
1573: if (!status.targetAppRunning) {
1574: serverCommandHandler.handleServerCommand(null);
1575: throw new ClientUtils.TargetAppOrVMTerminated(
1576: ClientUtils.TargetAppOrVMTerminated.APP);
1577: }
1578: }
1579:
1580: private void checkForTargetVMAlive()
1581: throws ClientUtils.TargetAppOrVMTerminated {
1582: if (!targetVMAlive) {
1583: serverCommandHandler.handleServerCommand(null);
1584: throw new ClientUtils.TargetAppOrVMTerminated(
1585: ClientUtils.TargetAppOrVMTerminated.VM);
1586: }
1587: }
1588:
1589: //-------------------------------- Private implementation ------------------------------------------
1590: private void clearPreviousInstrumentationInServer()
1591: throws InstrumentationException,
1592: ClientUtils.TargetAppOrVMTerminated {
1593: Response resp = null;
1594: checkForTargetAppRunning();
1595:
1596: // First send the command that will make the application stop emitting events
1597: // But avoid doing that while we are processing the data at the client side, since it looks like when these two
1598: // things happen at the same time, it's likely to cause problems.
1599: // This is a quick fix. Probably a more solid solution is needed.
1600: if (handlingEventBufferDump) {
1601: while (handlingEventBufferDump) {
1602: try {
1603: Thread.sleep(20);
1604: } catch (Exception ex) {
1605: }
1606: }
1607: }
1608:
1609: String error = sendSimpleCommandAndGetResponse(Command.DEACTIVATE_INJECTED_CODE);
1610:
1611: if (error != null) {
1612: throw new InstrumentationException(error);
1613: }
1614:
1615: long curTime = System.currentTimeMillis();
1616:
1617: // Now actually de-instrument the instrumented methods
1618: InstrumentMethodGroupCommand cmd = instrumentor
1619: .createClearAllInstrumentationCommand();
1620:
1621: synchronized (this ) {
1622: sendComplexCmdToServer(cmd);
1623: instrProcessingTime += (System.currentTimeMillis() - curTime);
1624: resp = getLastResponse();
1625: }
1626:
1627: if (!resp.isOK()) {
1628: throw new InstrumentationException(resp.getErrorMessage());
1629: }
1630: }
1631:
1632: private void closeConnection() {
1633: if (!serverListener.isRunning()) {
1634: return;
1635: }
1636:
1637: try {
1638: status.targetAppRunning = false;
1639: targetVMAlive = false;
1640: serverListener.shutdown();
1641: setLastResponse(null); // This is important, in case smb. is waiting for the response
1642: socketOut.close();
1643: socketIn.close();
1644: clientSocket.close();
1645:
1646: // This is kind of "black magic", that is needed when we hit "Run" without explicitly terminating
1647: // the previous target JVM. If this pause is not made here, then for some reason we get:
1648: // "SocketException: Connection reset by peer: JVM_recv in socket input stream read" in connectToServer().
1649: // I don't like this way of dealing with this problem - need to investigate why it really happens
1650: try {
1651: Thread.sleep(400);
1652: } catch (InterruptedException e) {
1653: }
1654:
1655: //serverCommandHandler.handleServerCommand(null); // does not seem to do anything
1656: } catch (IOException ex) {
1657: // Don't do anything
1658: } finally {
1659: EventBufferResultsProvider.getDefault().shutdown();
1660: EventBufferProcessor.removeEventBufferFile();
1661: }
1662: }
1663:
1664: private boolean connectToServer(int attachMode,
1665: boolean calibrationOnlyRun) {
1666: AppStatusHandler.AsyncDialog waitDialog;
1667: status.targetAppRunning = false;
1668: targetVMAlive = false;
1669: terminateOrDetachCommandIssued = false;
1670:
1671: String taHost = (attachMode == 1) ? settings.getRemoteHost()
1672: : ""; // NOI18N
1673:
1674: if (taHost.equals("") || taHost.equals("localhost")
1675: || taHost.equals("127.0.0.1")) { // NOI18N
1676: status.remoteProfiling = false;
1677: taHost = "127.0.0.1"; // NOI18N
1678: } else {
1679: status.remoteProfiling = true;
1680: }
1681:
1682: final String host = taHost;
1683: final int port = settings.getPortNo();
1684: waitDialog = appStatusHandler.getAsyncDialogInstance(
1685: CONNECT_VM_MSG, true, true);
1686: waitDialog.display();
1687:
1688: boolean waitForDisplayAndCloseIt = true;
1689: boolean waitForConnection = true;
1690: int noOfCycles = 600; // Timeout is set to 150 sec
1691:
1692: try {
1693: serverListener = new ServerListener();
1694: serverListener.start();
1695:
1696: while (waitForConnection) {
1697: try {
1698: clientSocket = new Socket(host, port);
1699: clientSocket.setSoTimeout(0); // ATTENTION: timeout may be found useful eventually...
1700: clientSocket.setTcpNoDelay(true); // Necessary at least on Solaris to avoid delays in e.g. readInt() etc.
1701: socketOut = new ObjectOutputStream(clientSocket
1702: .getOutputStream());
1703: socketIn = new ObjectInputStream(clientSocket
1704: .getInputStream());
1705: wireIO = new WireIO(socketOut, socketIn);
1706:
1707: waitForConnection = false;
1708: targetVMAlive = true; // This is in fact an assumption
1709: serverListener.startRunning();
1710: } catch (ConnectException ex) {
1711: // ex.printStackTrace (System.err);
1712: try {
1713: Thread.sleep(250);
1714: } catch (InterruptedException iex) {
1715: }
1716:
1717: if (waitDialog.cancelPressed()) {
1718: waitForConnection = false;
1719: serverListener.cancel();
1720: }
1721:
1722: if (--noOfCycles == 0) {
1723: MiscUtils
1724: .printWarningMessage("timed out while trying to connect to the target JVM."); // NOI18N
1725: waitForConnection = false;
1726: serverListener.cancel();
1727: }
1728: }
1729: }
1730: } catch (Exception ex) { // SocketException, UnknownHostException, IOException
1731: MiscUtils
1732: .printErrorMessage("exception while trying to connect to the target JVM:\n"
1733: + ex); // NOI18N
1734: } finally {
1735: waitDialog.close();
1736: }
1737:
1738: if (!serverListener.isRunning()) {
1739: MiscUtils
1740: .printErrorMessage("connection with server not open"); // NOI18N
1741:
1742: return false;
1743: }
1744:
1745: // Now check the connection and do other preparation work
1746: try {
1747: String error = sendSimpleCommandAndGetResponse(Command.CHECK_CONNECTION);
1748:
1749: if (error != null) {
1750: targetVMAlive = false;
1751: MiscUtils
1752: .printErrorMessage("got error message from agent:"
1753: + error); // NOI18N
1754:
1755: return false;
1756: }
1757:
1758: if (calibrationOnlyRun) {
1759: // System.err.println("G1");
1760: boolean res = getCalibrationData(false);
1761:
1762: // System.err.println("G2: "+res);
1763: try {
1764: terminateTargetJVM();
1765: } catch (ClientUtils.TargetAppOrVMTerminated e) {
1766: ProfilerLogger
1767: .warning("terminateTargetJVM failed with TargetAppOrVMTerminated exception:"); // NOI18N
1768: ProfilerLogger.log(e);
1769:
1770: // this is OK here
1771: }
1772:
1773: // System.err.println("G3");
1774: return res;
1775: }
1776:
1777: boolean terminateOnError = attachMode == 1; // in case of direct attach we don't want to have a JVM process hanging around waiting for the client to connect
1778: // Get VM properties
1779:
1780: synchronized (this ) {
1781: sendSimpleCmdToServer(Command.GET_VM_PROPERTIES);
1782:
1783: Response aResponse = getLastResponse();
1784:
1785: if (!(aResponse instanceof VMPropertiesResponse)) {
1786: System.err.println("SEVERE: Received "
1787: + aResponse.getClass().getName() + "("
1788: + aResponse.toString()
1789: + ") instead of VMPropertiesResponse");
1790: }
1791:
1792: if (!setVMProperties((VMPropertiesResponse) aResponse,
1793: terminateOnError)) {
1794: return false;
1795: }
1796: }
1797:
1798: // Send a command to initiate the fake RootClassLoadedCommand cycle, that forces initialization of some internal
1799: // server classes
1800: serverClassesInitialized = false;
1801: // Note that here we can't use normal getCmd(), since this shared object could already have been initialized with
1802: // real data.
1803: error = sendCommandAndGetResponse(new InitiateInstrumentationCommand(
1804: INSTR_RECURSIVE_FULL,
1805: "*FAKE_CLASS_FOR_INTERNAL_TEST*", false) // NOI18N
1806: ); // NOI18N
1807:
1808: if (error != null) {
1809: MiscUtils
1810: .printErrorMessage("got error message from agent:"
1811: + error); // NOI18N
1812: targetVMAlive = false;
1813:
1814: return false;
1815: }
1816:
1817: noOfCycles = 20;
1818:
1819: while (!serverClassesInitialized && (--noOfCycles > 0)) {
1820: try {
1821: Thread.sleep(100);
1822: } catch (InterruptedException ex) {
1823: }
1824: }
1825:
1826: if (!serverClassesInitialized) {
1827: MiscUtils
1828: .printErrorMessage("timed out while trying to initialize internals in the target JVM."); // NOI18N
1829: targetVMAlive = false;
1830:
1831: return false;
1832: }
1833:
1834: try {
1835: Thread.sleep(100); // To make sure everything has finished on the server side.
1836: } catch (InterruptedException ex) {
1837: }
1838: } catch (ClientUtils.TargetAppOrVMTerminated ex) {
1839: targetVMAlive = false;
1840: MiscUtils.printErrorMessage("target app terminated:"
1841: + ex.getMessage()); // NOI18N
1842:
1843: return false;
1844: }
1845:
1846: return true;
1847: }
1848:
1849: /**
1850: * Some commands, e.g. those related to instrumentation, are executed in a separate thread, since they may in turn
1851: * send requests and await response from the server. Thus the listener thread, that calls this method, should be made
1852: * available quickly so that it can listen for the server again.
1853: */
1854: private void executeInSeparateThread(Command cmd) {
1855: synchronized (execInSeparateThreadLock) {
1856: execInSeparateThreadCmd = cmd;
1857:
1858: try {
1859: execInSeparateThreadLock.notify();
1860: } catch (IllegalMonitorStateException ex) {
1861: MiscUtils
1862: .internalError("ProfilerClient.executeInSeparateThread()"); // NOI18N
1863: }
1864: }
1865: }
1866:
1867: private boolean handleFakeClassLoad(RootClassLoadedCommand cmd) {
1868: if (cmd.getAllLoadedClassNames()[0].equals("*FAKE_CLASS_1*")) { // NOI18N
1869: sendComplexRespToServer(new InstrumentMethodGroupResponse(
1870: new String[] { "*FAKE_CLASS_1*", "*FAKE_CLASS_2*" },
1871: new int[] { 0, 0 }, new byte[][] { { 0 }, { 0 } },
1872: null, 0));
1873: serverClassesInitialized = true;
1874:
1875: return true;
1876: } else {
1877: return false;
1878: }
1879: }
1880:
1881: private void handleIOExceptionOnSend(IOException ex)
1882: throws ClientUtils.TargetAppOrVMTerminated {
1883: checkForTargetVMAlive();
1884: // For now, assume that the server went away. TODO [misha] - can it happen for any other reason?
1885: appStatusHandler
1886: .displayError(MessageFormat.format(
1887: TARGET_JVM_ERROR_MSG, new Object[] { ex
1888: .getMessage() }));
1889: closeConnection();
1890: throw new ClientUtils.TargetAppOrVMTerminated(
1891: ClientUtils.TargetAppOrVMTerminated.VM);
1892: }
1893:
1894: private void instrumentMethodGroupFollowUp(Command cmd) {
1895: synchronized (instrumentationLock) {
1896: long curTime = System.currentTimeMillis();
1897: InstrumentMethodGroupResponse imgr = instrumentor
1898: .createFollowUpInstrumentMethodGroupResponse(cmd);
1899: instrProcessingTime += (System.currentTimeMillis() - curTime);
1900: //if (imgr != null && ! imgr.isEmpty()) {
1901: // System.err.println("*** Profiler Engine: instrumentMethodGroupFollowUp() produced response:");
1902: // imgr.dump();
1903: // }
1904: sendComplexRespToServer(imgr);
1905: }
1906:
1907: checkForInstrMethodsLimitReached();
1908: }
1909:
1910: private void instrumentMethodGroupFromRoot(
1911: final RootClassLoadedCommand cmd) {
1912: AppStatusHandler.AsyncDialog waitDialog = null;
1913:
1914: synchronized (instrumentationLock) {
1915: try {
1916: InstrumentMethodGroupResponse imgr = null;
1917:
1918: if (!serverClassesInitialized) { // Check if it is a fake command from server, used to just pre-initialize
1919: // some internal server classes
1920:
1921: if (handleFakeClassLoad(cmd)) {
1922: return;
1923: }
1924: }
1925:
1926: if (currentInstrTypeIsRecursiveCPUProfiling()
1927: || currentInstrTypeIsMemoryProfiling()) {
1928: if (!EventBufferProcessor.bufFileExists()) {
1929: if (!EventBufferProcessor
1930: .setEventBufferFile(cmd
1931: .getEventBufferFileName())) {
1932: appStatusHandler
1933: .displayError(MessageFormat
1934: .format(
1935: CANNOT_OPEN_SERVER_TEMPFILE_MSG,
1936: new Object[] { cmd
1937: .getEventBufferFileName() }));
1938: sendComplexRespToServer(new InstrumentMethodGroupResponse(
1939: null));
1940:
1941: return;
1942: }
1943: }
1944:
1945: JMethodIdTable.reset();
1946: }
1947:
1948: appStatusHandler.pauseLiveUpdates();
1949:
1950: if (status.targetAppRunning) {
1951: waitDialog = appStatusHandler
1952: .getAsyncDialogInstance(
1953: PERFORMING_INSTRUMENTATION_STRING,
1954: true, false);
1955: waitDialog.display();
1956:
1957: // if (waitDialog != null) {
1958: // final AppStatusHandler.AsyncDialog dialog = waitDialog;
1959: // if (EventQueue.isDispatchThread()) {
1960: // dialog.display();
1961: // } else {
1962: // EventQueue.invokeLater(new Runnable() {
1963: // public void run() {
1964: // dialog.display();
1965: // }
1966: // });
1967: // }
1968: // }
1969: }
1970:
1971: // If the application is not running yet, it means that instrumentation is performed on startup. In that case,
1972: // it typically takes very short time, so there is no real need to display this progress dialog. Additionally,
1973: // the AWT Event Queue thread may be blocked in the call to startTargetApp, in which case this whole thing will
1974: // hang (due to getAsyncDialogInstance NB implementation waiting on this thread's lock).
1975: try {
1976: long curTime = System.currentTimeMillis();
1977: imgr = instrumentor
1978: .createInitialInstrumentMethodGroupResponse(cmd);
1979: instrProcessingTime += (System.currentTimeMillis() - curTime);
1980: } catch (BadLocationException ex) {
1981: imgr = new InstrumentMethodGroupResponse(null);
1982: // Can currently happen only for INSTR_CODE_REGION
1983: appStatusHandler
1984: .displayError(INVALID_CODE_REGION_MSG);
1985: } catch (ClassNotFoundException ex) {
1986: imgr = new InstrumentMethodGroupResponse(null);
1987:
1988: if (getCurrentInstrType() == INSTR_CODE_REGION) {
1989: appStatusHandler
1990: .displayError(MessageFormat
1991: .format(CLASS_NOT_FOUND_MSG,
1992: new Object[] { ex
1993: .getMessage() }));
1994: } else {
1995: MiscUtils
1996: .printErrorMessage("problem in instrumentMethodGroupFromRoot: "
1997: + ex); // NOI18N
1998: }
1999: } catch (Exception e) {
2000: MiscUtils
2001: .printErrorMessage("in instrumentMethodGroupFromRoot(), caught exception: "
2002: + e); // NOI18N
2003: }
2004:
2005: //if (imgr != null ! imgr.isEmpty()) {
2006: // System.err.println("*** Profiler Engine: instrumentMethodGroupFromRoot() produced response:");
2007: // imgr.dump(); }
2008: // else System.err.println("*** Profiler Engine: instrumentMethodGroupFromRoot() produced empty response");
2009: sendComplexRespToServer(imgr);
2010: } finally {
2011: if (waitDialog != null) {
2012: final AppStatusHandler.AsyncDialog dialog = waitDialog;
2013:
2014: if (EventQueue.isDispatchThread()) {
2015: dialog.close();
2016: } else {
2017: EventQueue.invokeLater(new Runnable() {
2018: public void run() {
2019: dialog.close();
2020: }
2021: });
2022: }
2023: }
2024:
2025: appStatusHandler.resumeLiveUpdates();
2026: }
2027: }
2028: }
2029:
2030: /**
2031: * Upon receipt of the BUFFER_FULL command from the server, read and process the buffer contents
2032: */
2033: private void readAndProcessProfilingResults(
2034: EventBufferDumpedCommand cmd) {
2035: int bufSize = cmd.getBufSize();
2036:
2037: if (bufSize == 0) { // zero size may happen when dump is forced when there is actually no new information generated
2038: sendSimpleRespToServer(true, null);
2039:
2040: return;
2041: }
2042:
2043: handlingEventBufferDump = true;
2044:
2045: int instrType = getCurrentInstrType();
2046:
2047: // Results of memory profiling can be processed concurrently to take advantage of a possible multiprocessor machine.
2048: // Similarly, during remote profiling any results can be processed concurrently, since processing on a different
2049: // machine will not disturb execution timing on the TA machine. But for local CPU profiling we better not disturb
2050: // execution further by introducing yet another concurrent CPU-bound thread. Note also that if this command is
2051: // received as a result of the forced dump (as opposed to the normal one due to buffer overflow), the data should
2052: // be processed synchronously to avoid e.g. a "no results" report when there are already some.
2053:
2054: // update [ian] In case of remote profiling we actually can not process the results in concurrently,
2055: // since there would suddenly be 2 pieces of code that simultaneously read from the socket stream
2056: // leading to issue 59660: JFluid: error writing collected data to the socket
2057: // see http://www.netbeans.org/issues/show_bug.cgi?id=59660 for details
2058: if (currentInstrTypeIsMemoryProfiling()
2059: && !status.remoteProfiling
2060: && !forceObtainedResultsDumpCalled) {
2061: // Note that the call below may block, waiting for separarateCmdExecThread to finish its current job.
2062: // That means that nothing in readResultsFromBuffer() that this command eventually calls, is allowed to
2063: // send a command to the server and await a response. If that happens, the communication thread will be
2064: // unavailable for reading server's response (because it's waiting here), effectively causing a deadlock.
2065: executeInSeparateThread(cmd);
2066: handlingEventBufferDump = false;
2067: } else {
2068: // Process profiling results synchronously in case of:
2069: // - remote profiling
2070: // - explicite Get results (forceObtainedResultsDumpCalled)
2071: // - CPU or Code Fragment profiling
2072: EventBufferProcessor
2073: .readDataAndPrepareForProcessing(bufSize);
2074: EventBufferResultsProvider.getDefault().dataReady(bufSize,
2075: instrType);
2076: handlingEventBufferDump = false;
2077: sendSimpleRespToServer(true, null);
2078: forceObtainedResultsDumpCalled = false;
2079: }
2080: }
2081:
2082: /**
2083: * @param cmd Command to send
2084: * @return null if command was confirmed OK from Agent, Error message otherwise
2085: * @throws ClientUtils.TargetAppOrVMTerminated
2086: *
2087: */
2088: private synchronized String sendCommandAndGetResponse(Command cmd)
2089: throws ClientUtils.TargetAppOrVMTerminated {
2090: sendComplexCmdToServer(cmd);
2091:
2092: Response resp = getLastResponse();
2093:
2094: if (!resp.isOK()) {
2095: MiscUtils
2096: .printErrorMessage("error in sendCommandAndGetResponse: for cmd = "
2097: + cmd // NOI18N
2098: + " and resp = "
2099: + resp
2100: + " got error message: "
2101: + resp.getErrorMessage() // NOI18N
2102: );
2103:
2104: return resp.getErrorMessage();
2105: } else {
2106: return null;
2107: }
2108: }
2109:
2110: private void sendComplexCmdToServer(Command cmd)
2111: throws ClientUtils.TargetAppOrVMTerminated {
2112: try {
2113: wireIO.sendComplexCommand(cmd);
2114: } catch (IOException ex) {
2115: handleIOExceptionOnSend(ex);
2116: }
2117: }
2118:
2119: private void sendComplexRespToServer(Response resp) {
2120: try {
2121: wireIO.sendComplexResponse(resp);
2122: } catch (IOException ex) {
2123: MiscUtils
2124: .printErrorMessage("exception when trying to send a response: "
2125: + ex); // NOI18N
2126:
2127: try {
2128: handleIOExceptionOnSend(ex);
2129: } catch (ClientUtils.TargetAppOrVMTerminated ex1) { /* All done already */
2130: }
2131: }
2132: }
2133:
2134: private void sendSimpleCmdToServer(int cmdType)
2135: throws ClientUtils.TargetAppOrVMTerminated {
2136: try {
2137: wireIO.sendSimpleCommand(cmdType);
2138: } catch (IOException ex) {
2139: handleIOExceptionOnSend(ex);
2140: }
2141: }
2142:
2143: /**
2144: * @param cmd Command to send
2145: * @return null if command was confirmed OK from Agent, Error message otherwise
2146: * @throws ClientUtils.TargetAppOrVMTerminated
2147: *
2148: */
2149: private synchronized String sendSimpleCommandAndGetResponse(
2150: int cmdType) throws ClientUtils.TargetAppOrVMTerminated {
2151: sendSimpleCmdToServer(cmdType);
2152:
2153: Response resp = getLastResponse();
2154:
2155: if (!resp.isOK()) {
2156: MiscUtils
2157: .printErrorMessage("error in sendCommandAndGetResponse: for cmdType = "
2158: + cmdType // NOI18N
2159: + " and resp = "
2160: + resp
2161: + " got error message: "
2162: + resp.getErrorMessage() // NOI18N
2163: );
2164:
2165: return resp.getErrorMessage();
2166: } else {
2167: return null;
2168: }
2169: }
2170:
2171: private void sendSimpleRespToServer(boolean val, String errorMessage) {
2172: try {
2173: wireIO.sendSimpleResponse(val, errorMessage);
2174: } catch (IOException ex) {
2175: try {
2176: handleIOExceptionOnSend(ex);
2177: } catch (ClientUtils.TargetAppOrVMTerminated ex1) { /* All done already */
2178: }
2179: }
2180: }
2181: }
|