All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.netbeans.lib.profiler.ProfilerClient Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.netbeans.lib.profiler;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ConnectException;
import java.net.Socket;
import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import org.netbeans.lib.profiler.classfile.ClassRepository;
import org.netbeans.lib.profiler.client.AppStatusHandler;
import org.netbeans.lib.profiler.client.ClientUtils;
import org.netbeans.lib.profiler.client.MonitoredData;
import org.netbeans.lib.profiler.client.RuntimeProfilingPoint;
import org.netbeans.lib.profiler.global.CalibrationDataFileIO;
import org.netbeans.lib.profiler.global.CommonConstants;
import org.netbeans.lib.profiler.global.Platform;
import org.netbeans.lib.profiler.global.ProfilingSessionStatus;
import org.netbeans.lib.profiler.instrumentation.BadLocationException;
import org.netbeans.lib.profiler.instrumentation.InstrumentationException;
import org.netbeans.lib.profiler.instrumentation.Instrumentor;
import org.netbeans.lib.profiler.marker.Marker;
import org.netbeans.lib.profiler.results.EventBufferProcessor;
import org.netbeans.lib.profiler.results.EventBufferResultsProvider;
import org.netbeans.lib.profiler.results.ProfilingResultsDispatcher;
import org.netbeans.lib.profiler.results.coderegion.CodeRegionResultsSnapshot;
import org.netbeans.lib.profiler.results.cpu.CPUCCTProvider;
import org.netbeans.lib.profiler.results.cpu.CPUResultsSnapshot;
import org.netbeans.lib.profiler.results.cpu.FlatProfileProvider;
import org.netbeans.lib.profiler.results.jdbc.JdbcCCTProvider;
import org.netbeans.lib.profiler.results.jdbc.JdbcResultsSnapshot;
import org.netbeans.lib.profiler.results.memory.*;
import org.netbeans.lib.profiler.results.threads.ThreadDump;
import org.netbeans.lib.profiler.utils.MiscUtils;
import org.netbeans.lib.profiler.utils.StringUtils;
import org.netbeans.lib.profiler.wireprotocol.*;


/**
 * The interface between the tool and the profiling back end.
 *
 * @author Tomas Hurka
 * @author Misha Dmitriev
 * @author Adrian Mos
 * @author Ian Formanek
 */
public class ProfilerClient implements CommonConstants {
    //~ Inner Classes ------------------------------------------------------------------------------------------------------------

    /**
     * Thread for execution of commands that, due to limitations of our wire protocol, need to be executed such that
     * the ServerListener thread doesn't stay blocked while these commands are executed. See executeInSeparateThread()
     * above.
     */
    private class SeparateCmdExecutionThread extends Thread {
        //~ Methods --------------------------------------------------------------------------------------------------------------

        public void run() {
            setName(PROFILER_SEPARATE_EXEC_THREAD_NAME);

            synchronized (execInSeparateThreadLock) {
                while (true) {
                    try {
                        execInSeparateThreadLock.wait();
                    } catch (InterruptedException ex) {
                        MiscUtils.internalError("ProfilerClient.SpecialExecutionThread.run()"); // NOI18N
                    }

                    if (execInSeparateThreadCmd == null) {
                        return; // It was a signal to this thread to terminate
                    }

                    Command cmd = execInSeparateThreadCmd;
                    execInSeparateThreadCmd = null;

                    switch (cmd.getType()) {
                        case Command.ROOT_CLASS_LOADED:
                            instrumentMethodGroupFromRoot((RootClassLoadedCommand) cmd);

                            //instrMethodGroupFromRootComplete = true;
                            break;
                        case Command.CLASS_LOADED:
                        case Command.METHOD_INVOKED_FIRST_TIME:
                        case Command.METHOD_LOADED:
                            instrumentMethodGroupFollowUp(cmd);

                            break;
                        case Command.EVENT_BUFFER_DUMPED:
                            EventBufferDumpedCommand bufferDumpedCmd = ((EventBufferDumpedCommand) cmd);
                            byte[] buf = EventBufferProcessor.readDataAndPrepareForProcessing(bufferDumpedCmd);
                            
                            EventBufferResultsProvider.getDefault().dataReady(buf, getCurrentInstrType());
                            sendSimpleRespToServer(true, null);

                            break;
                        case Command.CLASS_LOADER_UNLOADING:

                            // We have to grab the forceObtainedResultsDumpLock to prevent forceObtainedResultsDump() coming in while
                            // we are processing the data, sending the request for dump to the server that currently awaits the
                            // request for jmethodIds, and thus creating a "distributed deadlock".
                            synchronized (ProfilerClient.this) {
                                synchronized (forceObtainedResultsDumpLock) {
                                    if (memCctProvider != null) {
                                        memCctProvider.updateInternals();
                                    }

                                    sendSimpleRespToServer(true, null);
                                }
                            }

                            break;
                    }
                }
            }
        }
    }

    private class ServerListener extends Thread {
        //~ Instance fields ------------------------------------------------------------------------------------------------------

        private final Object startedFlagLock = new Object();

        // @GuardedBy startedFlagLock
        private int startedFlag = 0; // 0 = initial state; 1 = started; -1 = cancelled

        //~ Methods --------------------------------------------------------------------------------------------------------------

        public boolean isRunning() {
            synchronized (startedFlagLock) {
                return startedFlag == 1;
            }
        }

        public void cancel() {
            synchronized (startedFlagLock) {
                startedFlag = -1;
                startedFlagLock.notifyAll();
            }
        }

        public void run() {
            // Wait until we know that the connection is open
            synchronized (startedFlagLock) {
                while (startedFlag == 0) { // while the state hasn't been explicitly changed

                    try {
                        startedFlagLock.wait(500);
                    } catch (InterruptedException e) {
                        startedFlag = -1; // thread has been interrupet = effectively cancelled
                    }
                }

                if (startedFlag == -1) { // cancelled

                    return;
                }
            }
            
            startSeparateCmdExecThread();
            try {
                while (targetVMAlive) {
                    try {
                        Object o = wireIO.receiveCommandOrResponse();

                        //System.out.println(">>> Got response or command from server " + o);
                        if (o == null) {
                            closeConnection();
                        } else {
                            if (o instanceof Command) {
                                handleServerCommand((Command) o);
                            } else {
                                setLastResponse((Response) o);
                            }
                        }
                    } catch (IOException ex) {
                        if (targetVMAlive && !terminateOrDetachCommandIssued) { // It wasn't a normal connection shutdown
                            MiscUtils.printErrorMessage("exception while trying to get response from the target JVM:\n" + ex); // NOI18N
                            closeConnection();

                            //            serverCommandHandler.handleServerCommand(null); // does not seem to do anything
                        }
                    }
                }
            } finally {
                stopSeparateCmdExecThread();
            }
        }

        public void shutdown() {
            synchronized (startedFlagLock) {
                startedFlag = 0;
                startedFlagLock.notifyAll();
            }
        }

        public void startRunning() {
            synchronized (startedFlagLock) {
                startedFlag = 1;
                startedFlagLock.notifyAll();
            }
        }

        private void handleServerCommand(final Command cmd) {
            switch (cmd.getType()) {
                case Command.SHUTDOWN_INITIATED:
                    status.targetAppRunning = false;

                    // Get and save the latest results and the internal statistics before the target VM goes away
                    (new Thread() {
                            public void run() {
                                try {
                                    if (currentInstrTypeIsRecursiveCPUProfiling() || currentInstrTypeIsMemoryProfiling()) {
                                        forceObtainedResultsDump(false, 15);
                                    }

                                    // In case of memory profiling, fetch additional data from the VM - names for all jmethodIDs and
                                    // object count
                                    if (currentInstrTypeIsMemoryProfiling()) {
                                        savedAllocatedObjectsCountResults = getAllocatedObjectsCountResults();
                                        // #204978: methodIds must be loaded from instead of 
                                        // the MemoryCallGraphBuilder'shutdown' method where it is too late
                                        if (memCctProvider instanceof MemoryCallGraphBuilder) {
                                            ((MemoryCallGraphBuilder)memCctProvider).updateInternals();
                                        }
                                    }

                                    status.savedInternalStats = getInternalStats();

                                    appStatusHandler.handleShutdown();

                                    sendSimpleCmdToServer(Command.SHUTDOWN_OK);
                                } catch (ClientUtils.TargetAppOrVMTerminated ex) { /* Ignore silently */
                                }
                            }
                        }).start();

                    break;
                case Command.SHUTDOWN_COMPLETED:
                    targetVMAlive = false;
                    status.targetAppRunning = false;
                    EventBufferProcessor.removeEventBufferFile();

                    break;
                case Command.ROOT_CLASS_LOADED:
                    executeInSeparateThread(cmd);

                    break;
                case Command.CLASS_LOADED:
                case Command.METHOD_INVOKED_FIRST_TIME:
                case Command.METHOD_LOADED:
                    executeInSeparateThread(cmd);

                    break;
                case Command.EVENT_BUFFER_DUMPED:
                    EventBufferDumpedCommand ebdCmd = (EventBufferDumpedCommand) cmd;
                    String bufferName = ebdCmd.getEventBufferFileName();
                    if (bufferName.length() > 0) {
                        if (!EventBufferProcessor.bufFileExists()) {
                            if (!EventBufferProcessor.setEventBufferFile(bufferName)) {
                                appStatusHandler.displayError(MessageFormat.format(CANNOT_OPEN_SERVER_TEMPFILE_MSG,
                                                                                   new Object[] { ebdCmd.getEventBufferFileName() }));
                            }
                        }
                        JMethodIdTable.reset();
                    }
                    readAndProcessProfilingResults(ebdCmd);

                    break;
                case Command.CLASS_LOADER_UNLOADING:
                    executeInSeparateThread(cmd);

                    break;
                case Command.RESULTS_AVAILABLE:
                    resultsStart = System.currentTimeMillis();

                    break;
                case Command.GET_CLASSID:

                    GetClassIdCommand cidCmd = (GetClassIdCommand) cmd;
                    int classId = instrumentor.getClassId(cidCmd.getClassName(), cidCmd.getClassLoaderId());
                    sendComplexRespToServer(new GetClassIdResponse(classId != -1, classId));

                    break;
                case Command.STILL_ALIVE:
                    break;
            }

            if (!targetVMAlive) {
                closeConnection();
            }

            serverCommandHandler.handleServerCommand(cmd);
        }
    }

    //~ Static fields/initializers -----------------------------------------------------------------------------------------------

    // -----
    // I18N String constants
    private static final String CANNOT_OPEN_SERVER_TEMPFILE_MSG;
    private static final String PERFORMING_INSTRUMENTATION_STRING;
    private static final String INVALID_CODE_REGION_MSG;
    private static final String CLASS_NOT_FOUND_MSG;
    private static final String OUT_OF_MEMORY_MSG;
    private static final String INCORRECT_AGENT_VERSION_MSG;
    private static final String ERROR_GETTING_CALIBRATION_DATA_MSG;
    private static final String MUST_CALIBRATE_FIRST_MSG;
    private static final String MUST_CALIBRATE_FIRST_SHORT_MSG;
    private static final String INSTRUMENTATION_LIMIT_REACHED_MSG;
    private static final String CORRUPTED_TARGET_CALIBRATION_DATA_MSG;
    private static final String CONNECT_VM_MSG;
    private static final String TARGET_JVM_ERROR_MSG;
    private static final String UNSUPPORTED_JVM_MSG;

    static {
        ResourceBundle messages = ResourceBundle.getBundle("org.netbeans.lib.profiler.Bundle"); // NOI18N
        CANNOT_OPEN_SERVER_TEMPFILE_MSG = messages.getString("ProfilerClient_CannotOpenServerTempFileMsg"); // NOI18N
        PERFORMING_INSTRUMENTATION_STRING = messages.getString("ProfilerClient_PerformingInstrumentationString"); // NOI18N
        INVALID_CODE_REGION_MSG = messages.getString("ProfilerClient_InvalidCodeRegionMsg"); // NOI18N
        CLASS_NOT_FOUND_MSG = messages.getString("ProfilerClient_ClassNotFoundMsg"); // NOI18N
        OUT_OF_MEMORY_MSG = messages.getString("ProfilerClient_OutOfMemoryMsg"); // NOI18N
        INCORRECT_AGENT_VERSION_MSG = messages.getString("ProfilerClient_IncorrectAgentVersionMsg"); // NOI18N
        ERROR_GETTING_CALIBRATION_DATA_MSG = messages.getString("ProfilerClient_ErrorGettingCalibrationDataMsg"); // NOI18N
        MUST_CALIBRATE_FIRST_MSG = messages.getString("ProfilerClient_MustCalibrateFirstMsg"); // NOI18N
        MUST_CALIBRATE_FIRST_SHORT_MSG = messages.getString("ProfilerClient_MustCalibrateFirstShortMsg"); // NOI18N
        INSTRUMENTATION_LIMIT_REACHED_MSG = messages.getString("ProfilerClient_InstrumentationLimitReachedMsg"); // NOI18N
        CORRUPTED_TARGET_CALIBRATION_DATA_MSG = messages.getString("ProfilerClient_CorruptedTargetCalibrationDataMsg"); // NOI18N
        CONNECT_VM_MSG = messages.getString("ProfilerClient_ConnectVmMsg"); // NOI18N
        TARGET_JVM_ERROR_MSG = messages.getString("ProfilerClient_TargetJvmErrorMsg"); // NOI18N
        UNSUPPORTED_JVM_MSG = messages.getString("ProfilerClient_UnsupportedJvmMsg"); // NOI18N
    }
    
    //~ Instance fields ----------------------------------------------------------------------------------------------------------

    private AppStatusHandler.ServerCommandHandler serverCommandHandler;
    private AppStatusHandler appStatusHandler;
    private CPUCCTProvider cpuCctProvider;
    private Command execInSeparateThreadCmd;
    private FlatProfileProvider flatProvider;
    private InitiateProfilingCommand commandOnStartup = null;
    private Instrumentor instrumentor;
    private MemoryCCTProvider memCctProvider;
    private JdbcCCTProvider jdbcCctProvider;
    private final Object execInSeparateThreadLock = new Object();
    private final Object forceObtainedResultsDumpLock = new Object(); // To make dump processing and other commands mutually
                                                                // exclusive

    /*instrMethodGroupFromRootComplete, */
    private final Object instrumentationLock = new Object(); // To make sure all instrumentation-related operations
                                                       // happen serially
    private final Object responseLock = new Object();
    private ObjectInputStream socketIn;
    private ObjectOutputStream socketOut;
    private ProfilerEngineSettings settings;
    private ProfilingSessionStatus status;
    private volatile Response lastResponse;
    private SeparateCmdExecutionThread separateCmdExecThread;
    private ServerListener serverListener;
    private HeapHistogramManager histogramManager;

    //--------------------- Connection management --------------------
    private Socket clientSocket;
    private WireIO wireIO;

    /**
     * Needed to make memory profiling results available after app/VM shutdown
     *
     * Note that we don't have anything like getMemoryProfilingResult() here - essentially because we don't have memory
     * results snapshots yet. Those, in turn, are not implemented because of performance concerns (reproducing our,
     * potentially huge, hash table containing all tracked object, plus the call trees for these object allocations,
     * every time that the user hits "Get results" seems scary). So instead of snapshots, we give the user various
     * aspects of (constantly updated) memory profiling data on demand. Methods that return it are public ones in
     * ObjAllocCallGraphBuilder, ObjLivenessCallGraphBuilder, and MemoryCallGraphBuilder.
     * The getAllocatedObjectsCountResults() method below provides only one aspect of the memory profiling data.
     */
    private int[] savedAllocatedObjectsCountResults;
    private volatile boolean forceObtainedResultsDumpCalled;
    private volatile boolean handlingEventBufferDump;
    private volatile boolean instrMethodsLimitReported;
    private boolean serverClassesInitialized;
    private volatile boolean targetVMAlive;
    private volatile boolean terminateOrDetachCommandIssued;
    private int currentAgentId = -1;
    private long instrProcessingTime;
    private long resultsStart;

    //~ Constructors -------------------------------------------------------------------------------------------------------------

    public ProfilerClient(ProfilerEngineSettings settings, ProfilingSessionStatus status, AppStatusHandler ash,
                          AppStatusHandler.ServerCommandHandler sch) {
        this.settings = settings;
        this.status = status;
        appStatusHandler = ash;
        serverCommandHandler = sch;
        instrumentor = new Instrumentor(status, settings);
        histogramManager = new HeapHistogramManager(settings);
        EventBufferProcessor.initialize(this);
        EventBufferResultsProvider.getDefault().addDispatcher(ProfilingResultsDispatcher.getDefault());
    }

    //~ Methods ------------------------------------------------------------------------------------------------------------------

    /**
     * Returns the array where element at index I is the total number of allocated objects for the class with I id.
     * The relevant counters are kept at the server side and returned to the tool on demand, here.
     */
    public synchronized int[] getAllocatedObjectsCountResults()
        throws ClientUtils.TargetAppOrVMTerminated {
        if (!targetVMAlive) {
            if (savedAllocatedObjectsCountResults != null) {
                return savedAllocatedObjectsCountResults;
            } else {
                throw new ClientUtils.TargetAppOrVMTerminated(ClientUtils.TargetAppOrVMTerminated.VM);
            }
        }

        savedAllocatedObjectsCountResults = null;
        checkForTargetVMAlive();
        sendSimpleCmdToServer(Command.GET_OBJECT_ALLOCATION_RESULTS);

        ObjectAllocationResultsResponse resp = (ObjectAllocationResultsResponse) getAndCheckLastResponse("Unknown problem when trying to get allocated object count results."); // NOI18N

        return resp.getResults();
    }

    /**
     * Returns the snapshot of current multi-method CPU profiling results
     *
     * @return CPU Results snapshot
     * @throws ClientUtils.TargetAppOrVMTerminated
     *          In case the profiled application has already terminated
     * @throws CPUResultsSnapshot.NoDataAvailableException
     *          If no data are available yet
     */
    public synchronized CPUResultsSnapshot getCPUProfilingResultsSnapshot()
        throws ClientUtils.TargetAppOrVMTerminated, CPUResultsSnapshot.NoDataAvailableException {
        return getCPUProfilingResultsSnapshot(true);
    }

    /**
     * Returns the snapshot of current multi-method CPU profiling results
     *
     * @param dump true to fetch latest events from server, false otherwise (use only available data)
     * @return CPU Results snapshot
     * @throws ClientUtils.TargetAppOrVMTerminated
     *          In case the profiled application has already terminated
     * @throws CPUResultsSnapshot.NoDataAvailableException
     *          If no data are available yet
     */
    public CPUResultsSnapshot getCPUProfilingResultsSnapshot(boolean dump)
        throws ClientUtils.TargetAppOrVMTerminated, CPUResultsSnapshot.NoDataAvailableException {
        checkForTargetVMAlive();

        if (dump) {
            if (!forceObtainedResultsDump(false, 5)) {
                return null;
            }
        }
        synchronized (this) {
            int len;
            boolean twoTimeStamps;
            String[] instrClassNames, instrMethodNames, instrMethodSigs;
            try {
                status.beginTrans(false);
                twoTimeStamps = status.collectingTwoTimeStamps();
                len = status.getNInstrMethods();
                instrClassNames = new String[len];
                System.arraycopy(status.getInstrMethodClasses(), 0, instrClassNames, 0, len);
                instrMethodNames = new String[len];
                System.arraycopy(status.getInstrMethodNames(), 0, instrMethodNames, 0, len);
                instrMethodSigs = new String[len];
                System.arraycopy(status.getInstrMethodSignatures(), 0, instrMethodSigs, 0, len);
                return new CPUResultsSnapshot(resultsStart, System.currentTimeMillis(), cpuCctProvider, twoTimeStamps, instrClassNames, instrMethodNames, instrMethodSigs, len);
            } finally {
                status.endTrans();
            }
        }
    }

    /**
     * Returns the snapshot of current code region profiling results
     */
    public synchronized CodeRegionResultsSnapshot getCodeRegionProfilingResultsSnapshot()
        throws ClientUtils.TargetAppOrVMTerminated {
        checkForTargetVMAlive();
        sendSimpleCmdToServer(Command.GET_CODE_REGION_CPU_RESULTS);

        CodeRegionCPUResultsResponse resp = (CodeRegionCPUResultsResponse) getAndCheckLastResponse("Unknown problem when trying to get code region CPU results."); // NOI18N

        return new CodeRegionResultsSnapshot(resultsStart, System.currentTimeMillis(), resp.getResults(),
                                             status.timerCountsInSecond[0]);
    }

    public int getCurrentAgentId() {
        return currentAgentId;
    }

    public void setCurrentInstrType(int type) {
        status.currentInstrType = type;
    }

    public int getCurrentInstrType() {
        return status.currentInstrType;
    }

    /**
     * Determine which of the currently tracked threads are dead or alive. If the VM is not running, just returns null -
     * it's clear that all threads are dead then.
     */
    public synchronized byte[] getCurrentThreadsLivenessStatus() {
        try {
            checkForTargetVMAlive();
            sendSimpleCmdToServer(Command.GET_THREAD_LIVENESS_STATUS);

            ThreadLivenessStatusResponse resp = (ThreadLivenessStatusResponse) getAndCheckLastResponse("Unknown problem when trying to get thread liveness information."); // NOI18N

            return resp.getStatus();
        } catch (ClientUtils.TargetAppOrVMTerminated ex) {
            if (serverListener.isRunning()) { // The possibly problematic situation is not known yet
                ProfilerLogger.log("in getCurrentThreadLivenessStatus(), caught exception: " + ex); // NOI18N
            }

            return null;
        }
    }

    /**
     * For the class with the given name and the initiating class loader (see Java Language/JVM Spec for definitions),
     * find out and return the defining class loader. Both class loaders are internal class loader ids.
     */
    public synchronized int getDefiningClassLoaderId(String className, int initiatingLoaderId)
                                              throws ClientUtils.TargetAppOrVMTerminated {
        checkForTargetVMAlive();

        GetDefiningClassLoaderCommand cmd = new GetDefiningClassLoaderCommand(className, initiatingLoaderId);
        sendComplexCmdToServer(cmd);

        DefiningLoaderResponse resp = (DefiningLoaderResponse) getAndCheckLastResponse("Unknown problem when trying to get a defining loader for class"); // NOI18N

        return resp.getLoaderId();
    }

    public FlatProfileProvider getFlatProfileProvider() {
        return flatProvider;
    }

    public long getInstrProcessingTime() {
        return instrProcessingTime;
    }

    //---------------- Internal statistics and other target VM information obtaining ----------------
    public synchronized InternalStatsResponse getInternalStats()
        throws ClientUtils.TargetAppOrVMTerminated {
        checkForTargetVMAlive();
        sendSimpleCmdToServer(Command.GET_INTERNAL_STATS);

        InternalStatsResponse resp = (InternalStatsResponse) getLastResponse();

        return resp;
    }

    public MemoryCCTProvider getMemoryCCTProvider() {
        return memCctProvider;
    }

    /**
     * Returns the snapshot of current Memory profiling results
     *
     * @return Memory Results snapshot
     * @throws ClientUtils.TargetAppOrVMTerminated
     *          In case the profiled application has already terminated
     */
    public MemoryResultsSnapshot getMemoryProfilingResultsSnapshot()
        throws ClientUtils.TargetAppOrVMTerminated {
        return getMemoryProfilingResultsSnapshot(true);
    }

    /**
     * Returns the snapshot of current Memory profiling results
     *
     * @param dump true to fetch latest events from server, false otherwise (use only available data)
     * @return Memory Results snapshot
     * @throws ClientUtils.TargetAppOrVMTerminated
     *          In case the profiled application has already terminated
     */
    public MemoryResultsSnapshot getMemoryProfilingResultsSnapshot(boolean dump)
        throws ClientUtils.TargetAppOrVMTerminated {
        checkForTargetVMAlive();
        int instrType = getCurrentInstrType();
        
        if (instrType == INSTR_NONE_MEMORY_SAMPLING) {
            if (settings.getRunGCOnGetResultsInMemoryProfiling()) {
                runGC();
            }
            return new SampledMemoryResultsSnapshot(resultsStart, System.currentTimeMillis(), this);
        }
        if (dump) {
            if (!forceObtainedResultsDump(false, 5)) {
                return null;
            }
        }

        synchronized (this) {
            memCctProvider.beginTrans(false);

            try {
                memCctProvider.updateInternals();

                if (instrType == INSTR_OBJECT_ALLOCATIONS) {
                    return new AllocMemoryResultsSnapshot(resultsStart, System.currentTimeMillis(), memCctProvider, this);
                } else {
                    return new LivenessMemoryResultsSnapshot(resultsStart, System.currentTimeMillis(), memCctProvider, this);
                }
            } finally {
                memCctProvider.endTrans();
            }
        }
    }

   /**
     * Returns the snapshot of current JDBC(Selects) profiling results
     *
     * @return JDBC Results snapshot
     * @throws ClientUtils.TargetAppOrVMTerminated
     *          In case the profiled application has already terminated
     */
    public JdbcResultsSnapshot getJdbcProfilingResultsSnapshot()
        throws ClientUtils.TargetAppOrVMTerminated {
        return getJdbcProfilingResultsSnapshot(true);
    }

    /**
     * Returns the snapshot of current Memory profiling results
     *
     * @param dump true to fetch latest events from server, false otherwise (use only available data)
     * @return Memory Results snapshot
     * @throws ClientUtils.TargetAppOrVMTerminated
     *          In case the profiled application has already terminated
     */
    public JdbcResultsSnapshot getJdbcProfilingResultsSnapshot(boolean dump)
        throws ClientUtils.TargetAppOrVMTerminated {
        checkForTargetVMAlive();
        if (dump) {
            if (!forceObtainedResultsDump(false, 5)) {
                return null;
            }
        }

        synchronized (this) {
            return new JdbcResultsSnapshot(resultsStart, System.currentTimeMillis(), jdbcCctProvider, this);
        }
    }
    
    public Marker getMethodMarker() {
        return settings.getMethodMarker();
    }

    /**
     * Called to obtain method names for jMethodIds, that we do not know method names of.
     * This method is typically called when results are to be displayed, but also in case some classes are unloaded
     * in the profiled application, as in this case we would lost method names for already accumulated results.
     * 

* Assumption is that jMethodId is never reused inside the JVM. * * @param methodIds array of jMethodIds that we do not have names for * @return the 4xn array, containing quadruplets of {class name, method name, method signature, native flag} strings for * given jmethodIds */ public synchronized String[][] getMethodNamesForJMethodIds(int[] methodIds) throws ClientUtils.TargetAppOrVMTerminated { final int PACKEDARR_ITEMS = 4; // must match PACKEDARR_ITEMS in Stacks.c checkForTargetVMAlive(); GetMethodNamesForJMethodIdsCommand cmd = new GetMethodNamesForJMethodIdsCommand(methodIds); sendComplexCmdToServer(cmd); MethodNamesResponse resp = (MethodNamesResponse) getAndCheckLastResponse("Unknown problem when trying to get method names for jmethodIds"); // NOI18N return StringUtils.convertPackedStringsIntoStringArrays(resp.getPackedData(), resp.getPackedArrayOffsets(), PACKEDARR_ITEMS); } public synchronized HeapHistogram getHeapHistogram() throws ClientUtils.TargetAppOrVMTerminated { HeapHistogramResponse resp; checkForTargetVMAlive(); sendSimpleCmdToServer(Command.GET_HEAP_HISTOGRAM); resp = (HeapHistogramResponse) getAndCheckLastResponse("Unknown problem when trying to get heap histogram"); // NOI18N return histogramManager.getHistogram(resp); } public synchronized ThreadDump takeThreadDump() throws ClientUtils.TargetAppOrVMTerminated { ThreadDumpResponse resp; checkForTargetVMAlive(); sendSimpleCmdToServer(Command.TAKE_THREAD_DUMP); resp = (ThreadDumpResponse) getAndCheckLastResponse("Unknown problem when trying to take thread dump"); // NOI18N return new ThreadDump(resp.isJDK15(), resp.getTime(), resp.getThreads()); } public synchronized byte[][] getCachedClassFileBytes(String[] classes, int[] classLoaderIds) throws ClientUtils.TargetAppOrVMTerminated { GetClassFileBytesResponse resp; checkForTargetVMAlive(); GetClassFileBytesCommand cmd = new GetClassFileBytesCommand(classes, classLoaderIds); sendComplexCmdToServer(cmd); resp = (GetClassFileBytesResponse) getAndCheckLastResponse("Unknown problem when trying to get cached class file bytes"); // NOI18N return resp.getClassBytes(); } public synchronized MonitoredData getMonitoredData() { try { checkForTargetVMAlive(); sendSimpleCmdToServer(Command.GET_MONITORED_NUMBERS); Response resp = getAndCheckLastResponse("Unknown problem when trying to get memory numbers."); // NOI18N try { MonitoredNumbersResponse mresp = (MonitoredNumbersResponse) resp; return MonitoredData.getMonitoredData(mresp); } catch (ClassCastException ex) { // FIXME: this diagnostics stuff should be ultimately removed once the root cause of the problem is understood MiscUtils.printErrorMessage("caught ClassCastException in getMonitoredNumbers. The real class of resp is " // NOI18N + resp.getClass().getName() + ", resp = " + resp // NOI18N ); throw ex; } } catch (ClientUtils.TargetAppOrVMTerminated ex) { if (serverListener.isRunning()) { // The possibly problematic situation is not known yet ProfilerLogger.log("in getMonitoredData(), caught exception: " + ex); // NOI18N } return null; } } /** * @return ProfilerEngineSettings current profiler engine settings */ public ProfilerEngineSettings getSettings() { return settings; } /** * We are using this essentially to let user know when the longest part of what happens after hitting e.g. * "Instrument Object Allocation", is complete. Otherwise it might be difficult to figure out what's going on */ // public boolean isInstrMethodGroupFromRootComplete() { return instrMethodGroupFromRootComplete; } // public CPUCallGraphBuilder getCPUCallGraphBuilder() { return ccgb; } // public MemoryCallGraphBuilder getMemoryCallGraphBuilder() { return mcgb; } public ObjectInputStream getSocketInputStream() { return socketIn; } public ProfilingSessionStatus getStatus() { return status; } public synchronized boolean cpuResultsExist() throws ClientUtils.TargetAppOrVMTerminated { checkForTargetVMAlive(); sendSimpleCmdToServer(Command.CPU_RESULTS_EXIST); Response resp = getAndCheckLastResponse("Unknown problem when trying to check for CPU profiling results."); // NOI18N return resp.yes(); } public boolean currentInstrTypeIsMemoryProfiling() { return ((status.currentInstrType == INSTR_OBJECT_ALLOCATIONS) || (status.currentInstrType == INSTR_OBJECT_LIVENESS)); } public boolean currentInstrTypeIsRecursiveCPUProfiling() { return ((status.currentInstrType == INSTR_RECURSIVE_FULL) || (status.currentInstrType == INSTR_RECURSIVE_SAMPLED)); } /** * Removes instrumentation for classes with ids such that unprofiledClassStatusArray[id] == false. * For these classes, no memory profiling data will be generated anymore. */ public void deinstrumentMemoryProfiledClasses(boolean[] unprofiledClassStatusArray) throws InstrumentationException, ClientUtils.TargetAppOrVMTerminated { synchronized (instrumentationLock) { if (getCurrentInstrType() == INSTR_NONE || getCurrentInstrType() == INSTR_NONE_SAMPLING) { return; } Response resp; checkForTargetAppRunning(); long curTime = System.currentTimeMillis(); InstrumentMethodGroupCommand cmd = instrumentor.getCommandToUnprofileClasses(unprofiledClassStatusArray); if (!cmd.isEmpty()) { synchronized (this) { // System.out.println("*** Profiler Engine: deinstrumentMemoryProfiledClasses() produced command:"); cmd.dump(); sendComplexCmdToServer(cmd); instrProcessingTime += (System.currentTimeMillis() - curTime); resp = getLastResponse(); } if (!resp.isOK()) { throw new InstrumentationException(resp.getErrorMessage()); } } } } public void prepareDetachFromTargetJVM() throws ClientUtils.TargetAppOrVMTerminated { while(true) { // active waiting with released lock, this prevents deadlock if getDefiningClassLoaderId is // called simultanously synchronized(this) { sendSimpleCmdToServer(Command.PREPARE_DETACH); Response resp = getAndCheckLastResponse("prepareDetachFromTargetJVM"); if(!resp.isOK()) { return; } if(resp.yes()) { break; } } try { Thread.sleep(2000); } catch (InterruptedException ex) { MiscUtils.printWarningMessage("Interrupted while waiting for prepare detach"); } } } public synchronized void detachFromTargetJVM() throws ClientUtils.TargetAppOrVMTerminated { checkForTargetVMAlive(); terminateOrDetachCommandIssued = true; sendSimpleCmdToServer(Command.DETACH); try { getLastResponse(); } finally { closeConnection(); EventBufferProcessor.removeEventBufferFile(); // Try again, just in case closeConnection returned without calling it } } /** * This is called in all modes, direct invoke or attachment, to establish connection with the target VM * @param attachMode 0 = no attach, 1 = direct attach, 2 = dynamic attach * @param calibrationOnlyRun connection in calibration mode only * @param cancel shared cancel flag */ public boolean establishConnectionWithServer(int attachMode, boolean calibrationOnlyRun, AtomicBoolean cancel) { // Make sure we initialize this field early - it may be changed once we connect to the JVM and find out its // real version. status.targetJDKVersionString = settings.getTargetJDKVersionString(); return connectToServer(attachMode, calibrationOnlyRun, cancel); } /** * Tells the server to send the contents of its data buffer to the tool immediately, no matter whether it's * full or not. */ public boolean forceObtainedResultsDump() throws ClientUtils.TargetAppOrVMTerminated { return forceObtainedResultsDump(false, 0); } public boolean forceObtainedResultsDump(boolean liveResults, int retries) throws ClientUtils.TargetAppOrVMTerminated { boolean dumped = false; int retryCounter = retries; do { dumped = forceObtainedResultsDump(liveResults); if (!dumped) { try { Thread.sleep(200); } catch (InterruptedException e) { break; } } } while (!dumped && (--retryCounter > 0)); // fix for Issue #135532 if (dumped) { try { Thread.sleep(200); } catch (InterruptedException e) { } } return dumped; } /** * Tells the server to send the contents of its data buffer to the tool immediately, no matter whether it's * full or not. */ public boolean forceObtainedResultsDump(boolean liveResults) throws ClientUtils.TargetAppOrVMTerminated { // The locks below are in the special order, to prevent deadlocks synchronized (this) { synchronized (forceObtainedResultsDumpLock) { if (handlingEventBufferDump) { return true; // If dump handling is already in progress, don't force the second dump } // no reason (and may be dangerous) to send another force dump command checkForTargetVMAlive(); forceObtainedResultsDumpCalled = true; sendSimpleCmdToServer(liveResults ? Command.DUMP_EXISTING_RESULTS_LIVE : Command.DUMP_EXISTING_RESULTS); DumpResultsResponse resp = (DumpResultsResponse) getLastResponse(); if (resp.yes()) { status.dumpAbsTimeStamp = resp.getDumpAbsTimeStamp(); } else { if (ProfilerLogger.isDebug()) { ProfilerLogger.debug("Force Obtained Results - Received Dump Error "); // NOI18N } } forceObtainedResultsDumpCalled = false; return resp.yes(); } } } /** * This should be called to initiate code region instrumentation for specified code region. * The data is remembered or sent to the server immediately if TA is already running. The actual instrumentation * starts when server informs the tool that the class to be instrumented is loaded. */ public void initiateCodeRegionInstrumentation(ClientUtils.SourceCodeSelection[] s) throws ClassNotFoundException, BadLocationException, InstrumentationException, IOException, ClassFormatError, ClientUtils.TargetAppOrVMTerminated { synchronized (instrumentationLock) { removeAllInstrumentation(); if (status.targetAppRunning && status.remoteProfiling) { if (!getCalibrationData(true)) { return; } } instrumentor.setStatusInfoFromSourceCodeSelection(s); instrumentor.setSavedSourceCodeSelection(s); String className = instrumentor.getRootClassNames()[ProfilingSessionStatus.CODE_REGION_CLASS_IDX].replace('/', '.'); // NOI18N InitiateProfilingCommand cmd = new InitiateProfilingCommand(INSTR_CODE_REGION, className, false, status.startProfilingPointsActive); commandOnStartup = cmd; setCurrentInstrType(INSTR_CODE_REGION); if (status.targetAppRunning) { sendSetInstrumentationParamsCmd(false); String errorMessage = sendCommandAndGetResponse(commandOnStartup); if (errorMessage != null) { appStatusHandler.displayWarning(errorMessage); } } } } public void initiateMonitoring() throws ClientUtils.TargetAppOrVMTerminated, InstrumentationException { synchronized (instrumentationLock) { removeAllInstrumentation(); InitiateProfilingCommand cmd = new InitiateProfilingCommand(INSTR_NONE); commandOnStartup = cmd; // just to be consistent, since removeAllInstrumentation() // sets instrumentation type to INSTR_NONE setCurrentInstrType(INSTR_NONE); if (status.targetAppRunning) { sendSetInstrumentationParamsCmd(false); String errorMessage = sendCommandAndGetResponse(commandOnStartup); if (errorMessage != null) { appStatusHandler.displayWarning(errorMessage); } } } } /** * This should be called to initiate memory profiling instrumentation of specified type (object allocation or * object liveness). * The data is remembered or sent to the server immediately if TA is already running. The actual instrumentation * starts when the TA is started and the first class of this app is loaded, or immediately if TA is already running. */ public void initiateMemoryProfInstrumentation(int instrType) throws ClientUtils.TargetAppOrVMTerminated, InstrumentationException { synchronized (instrumentationLock) { removeAllInstrumentation(); if (instrType == INSTR_NONE_MEMORY_SAMPLING) { commandOnStartup = new InitiateProfilingCommand(INSTR_NONE_MEMORY_SAMPLING); } else { // Set this root class name irrespective of whether the target app has been started or not. // If it's not yet started, then indeed instrumentation should be triggered by main class load event - otherwise // the first loaded class that we register in the server is some reflection class loaded in process of main() // invocation. It causes recursive invocations of classLoadHook() (because it also uses some reflection), thus // screwing up the instrumentation procedure. // If the target app is already running, then instrumentation starts immediately and isn't triggered by a class // load event. However, if the same cmd that we build here is then re-used as commandOnStartup, it should again // contain rootClassName. String[] rootClassNames = new String[]{settings.getMainClassName()}; commandOnStartup = createInitiateInstrumnetation(instrType, rootClassNames, false, status.startProfilingPointsActive); // switch (instrType) { // case INSTR_OBJECT_ALLOCATIONS: // mcgb = new ObjAllocCallGraphBuilder(this); // break; // case INSTR_OBJECT_LIVENESS: // mcgb = new ObjLivenessCallGraphBuilder(this); // break; // } // See initiateRecursiveCPUProfInstrumentation for why it's important to setCurrentInstrType() early } setCurrentInstrType(instrType); if (status.targetAppRunning) { sendSetInstrumentationParamsCmd(false); String errorMessage = sendCommandAndGetResponse(commandOnStartup); if (errorMessage != null) { appStatusHandler.displayWarning(errorMessage); } } //instrMethodGroupFromRootComplete = false; } } /** * This should be called to initiate CPU profiling instrumentation starting from specified root method(s). * The data is remembered or sent to the server immediately if TA is already running. The actual instrumentation * starts when server informs the tool that one of the classes to be instrumented is loaded. */ public void initiateRecursiveCPUProfInstrumentation(ClientUtils.SourceCodeSelection[] s) throws ClassNotFoundException, BadLocationException, InstrumentationException, IOException, ClassFormatError, ClientUtils.TargetAppOrVMTerminated { // System.out.println("Initiating CPU instrumentation"); // for(int i=0;i 0) { // Saved data file doesn't exist - notify the user and stop appStatusHandler.displayErrorWithDetailsAndWaitForConfirm(MUST_CALIBRATE_FIRST_SHORT_MSG, MUST_CALIBRATE_FIRST_MSG); try { if (terminateOnError) { terminateTargetJVM(); } else { detachFromTargetJVM(); } } catch (ClientUtils.TargetAppOrVMTerminated ex) { } return false; } } status.jvmArguments = resp.getJVMArguments(); status.javaCommand = resp.getJavaCommand(); status.targetMachineOSName = resp.getTargetMachineOSName(); status.maxHeapSize = resp.getMaxHeapSize(); status.startupTimeMillis = resp.getStartupTimeMillis(); status.startupTimeInCounts = resp.getStartupTimeInCounts(); status.canInstrumentConstructor = resp.canInstrumentConstructor(); if (!status.remoteProfiling && settings.getTargetJDKVersionString() != CommonConstants.JDK_19_STRING && settings.getTargetJDKVersionString() != CommonConstants.JDK_110_BEYOND_STRING) { settings.setWorkingDir(resp.getWorkingDir()); settings.setVMClassPaths(resp.getJavaClassPath(), resp.getJavaExtDirs(), resp.getBootClassPath()); ClassRepository.initClassPaths(settings.getWorkingDir(), settings.getVMClassPaths()); } return true; } /** * Check if we can't instrument more methods because the 64K limit is reached */ private void checkForInstrMethodsLimitReached() { if ((status.getStartingMethodId() >= 65535) && !instrMethodsLimitReported && status.targetAppRunning) { appStatusHandler.displayWarningAndWaitForConfirm(INSTRUMENTATION_LIMIT_REACHED_MSG); instrMethodsLimitReported = true; } } private void checkForTargetAppRunning() throws ClientUtils.TargetAppOrVMTerminated { if (!status.targetAppRunning) { serverCommandHandler.handleServerCommand(null); throw new ClientUtils.TargetAppOrVMTerminated(ClientUtils.TargetAppOrVMTerminated.APP); } } private void checkForTargetVMAlive() throws ClientUtils.TargetAppOrVMTerminated { if (!targetVMAlive) { serverCommandHandler.handleServerCommand(null); throw new ClientUtils.TargetAppOrVMTerminated(ClientUtils.TargetAppOrVMTerminated.VM); } } //-------------------------------- Private implementation ------------------------------------------ private void clearPreviousInstrumentationInServer() throws InstrumentationException, ClientUtils.TargetAppOrVMTerminated { Response resp; checkForTargetAppRunning(); // First send the command that will make the application stop emitting events // But avoid doing that while we are processing the data at the client side, since it looks like when these two // things happen at the same time, it's likely to cause problems. // This is a quick fix. Probably a more solid solution is needed. if (handlingEventBufferDump) { while (handlingEventBufferDump) { try { Thread.sleep(20); } catch (Exception ex) { } } } String error = sendSimpleCommandAndGetResponse(Command.DEACTIVATE_INJECTED_CODE); if (error != null) { throw new InstrumentationException(error); } long curTime = System.currentTimeMillis(); // Now actually de-instrument the instrumented methods InstrumentMethodGroupCommand cmd = instrumentor.createClearAllInstrumentationCommand(); synchronized (this) { sendComplexCmdToServer(cmd); instrProcessingTime += (System.currentTimeMillis() - curTime); resp = getLastResponse(); } if (!resp.isOK()) { throw new InstrumentationException(resp.getErrorMessage()); } } private void closeConnection() { if (!serverListener.isRunning()) { return; } try { status.targetAppRunning = false; targetVMAlive = false; serverListener.shutdown(); setLastResponse(null); // This is important, in case smb. is waiting for the response socketOut.close(); socketIn.close(); clientSocket.close(); // This is kind of "black magic", that is needed when we hit "Run" without explicitly terminating // the previous target JVM. If this pause is not made here, then for some reason we get: // "SocketException: Connection reset by peer: JVM_recv in socket input stream read" in connectToServer(). // I don't like this way of dealing with this problem - need to investigate why it really happens try { Thread.sleep(400); } catch (InterruptedException e) { } //serverCommandHandler.handleServerCommand(null); // does not seem to do anything } catch (IOException ex) { // Don't do anything } finally { EventBufferResultsProvider.getDefault().shutdown(); EventBufferProcessor.removeEventBufferFile(); } } private boolean connectToServer(int attachMode, boolean calibrationOnlyRun, final AtomicBoolean cancel) { status.targetAppRunning = false; targetVMAlive = false; terminateOrDetachCommandIssued = false; String taHost = (attachMode == 1) ? settings.getRemoteHost() : ""; // NOI18N if (taHost.isEmpty()) { status.remoteProfiling = false; taHost = "127.0.0.1"; // NOI18N } else { status.remoteProfiling = true; } final String host = taHost; final int port = settings.getPortNo(); int noOfCycles = 600; // Timeout is set to 150 sec Runnable cancelHandler = new Runnable() { public void run() { cancel.set(true); serverListener.cancel(); } }; AppStatusHandler.AsyncDialog waitDialog = appStatusHandler.getAsyncDialogInstance(CONNECT_VM_MSG, true, cancelHandler); try { serverListener = new ServerListener(); waitDialog.display(); serverListener.start(); while (!cancel.get()) { try { clientSocket = new Socket(host, port); clientSocket.setSoTimeout(0); // ATTENTION: timeout may be found useful eventually... clientSocket.setTcpNoDelay(true); // Necessary at least on Solaris to avoid delays in e.g. readInt() etc. socketOut = new ObjectOutputStream(clientSocket.getOutputStream()); socketIn = new ObjectInputStream(clientSocket.getInputStream()); wireIO = new WireIO(socketOut, socketIn); targetVMAlive = true; // This is in fact an assumption serverListener.startRunning(); break; } catch (ConnectException ex) { // ex.printStackTrace (System.err); try { Thread.sleep(250); } catch (InterruptedException iex) { } if (--noOfCycles == 0) { MiscUtils.printWarningMessage("timed out while trying to connect to the target JVM."); // NOI18N serverListener.cancel(); break; } } } } catch (Exception ex) { // SocketException, UnknownHostException, IOException MiscUtils.printErrorMessage("exception while trying to connect to the target JVM:\n" + ex); // NOI18N } finally { waitDialog.close(); } if (!serverListener.isRunning()) { MiscUtils.printErrorMessage("connection with server not open"); // NOI18N return false; } // Now check the connection and do other preparation work try { String error = sendSimpleCommandAndGetResponse(Command.CHECK_CONNECTION); if (error != null) { targetVMAlive = false; MiscUtils.printErrorMessage("got error message from agent:" + error); // NOI18N return false; } if (calibrationOnlyRun) { // System.err.println("G1"); boolean res = getCalibrationData(false); // System.err.println("G2: "+res); try { terminateTargetJVM(); } catch (ClientUtils.TargetAppOrVMTerminated e) { ProfilerLogger.log("terminateTargetJVM failed with TargetAppOrVMTerminated exception:"); // NOI18N ProfilerLogger.log(e.getMessage()); // this is OK here } // System.err.println("G3"); return res; } boolean terminateOnError = attachMode != 2; // in case of direct attach we don't want to have a JVM process hanging around waiting for the client to connect // Get VM properties synchronized (this) { sendSimpleCmdToServer(Command.GET_VM_PROPERTIES); Response aResponse = getLastResponse(); if (!(aResponse instanceof VMPropertiesResponse)) { System.err.println("SEVERE: Received " + aResponse.getClass().getName() + "(" + aResponse.toString() // NOI18N + ") instead of VMPropertiesResponse"); // NOI18N } if (!setVMProperties((VMPropertiesResponse) aResponse, terminateOnError)) { return false; } } // Send a command to initiate the fake RootClassLoadedCommand cycle, that forces initialization of some internal // server classes serverClassesInitialized = false; // Note that here we can't use normal getCmd(), since this shared object could already have been initialized with // real data. error = sendCommandAndGetResponse(new InitiateProfilingCommand(INSTR_RECURSIVE_FULL, "*FAKE_CLASS_FOR_INTERNAL_TEST*") // NOI18N ); if (error != null) { MiscUtils.printErrorMessage("got error message from agent:" + error); // NOI18N targetVMAlive = false; return false; } noOfCycles = 20; while (!serverClassesInitialized && (--noOfCycles > 0)) { try { Thread.sleep(100); } catch (InterruptedException ex) { } } if (!serverClassesInitialized) { MiscUtils.printErrorMessage("timed out while trying to initialize internals in the target JVM."); // NOI18N targetVMAlive = false; return false; } try { Thread.sleep(100); // To make sure everything has finished on the server side. } catch (InterruptedException ex) { } } catch (ClientUtils.TargetAppOrVMTerminated ex) { targetVMAlive = false; MiscUtils.printWarningMessage("target app terminated:" + ex.getMessage()); // NOI18N return false; } return true; } /** * Some commands, e.g. those related to instrumentation, are executed in a separate thread, since they may in turn * send requests and await response from the server. Thus the listener thread, that calls this method, should be made * available quickly so that it can listen for the server again. */ private void executeInSeparateThread(Command cmd) { synchronized (execInSeparateThreadLock) { execInSeparateThreadCmd = cmd; try { execInSeparateThreadLock.notify(); } catch (IllegalMonitorStateException ex) { MiscUtils.internalError("ProfilerClient.executeInSeparateThread()"); // NOI18N } } } private void startSeparateCmdExecThread() { assert separateCmdExecThread == null; SeparateCmdExecutionThread t = new SeparateCmdExecutionThread(); t.setDaemon(true); t.start(); separateCmdExecThread = t; } private void stopSeparateCmdExecThread() { assert separateCmdExecThread != null; executeInSeparateThread(null); // stop thread separateCmdExecThread = null; } private boolean handleFakeClassLoad(RootClassLoadedCommand cmd) { if (cmd.getAllLoadedClassNames()[0].equals("*FAKE_CLASS_1*")) { // NOI18N sendComplexRespToServer(new InstrumentMethodGroupResponse(new String[] { "*FAKE_CLASS_1*", "*FAKE_CLASS_2*" }, new int[] { 0, 0 }, new byte[][] { { 0 }, { 0 } }, null, 0)); serverClassesInitialized = true; return true; } else { return false; } } private void handleIOExceptionOnSend(IOException ex) throws ClientUtils.TargetAppOrVMTerminated { checkForTargetVMAlive(); // For now, assume that the server went away. TODO [misha] - can it happen for any other reason? appStatusHandler.displayError(MessageFormat.format(TARGET_JVM_ERROR_MSG, new Object[] { ex.getMessage() })); closeConnection(); throw new ClientUtils.TargetAppOrVMTerminated(ClientUtils.TargetAppOrVMTerminated.VM); } private void instrumentMethodGroupFollowUp(Command cmd) { synchronized (instrumentationLock) { long curTime = System.currentTimeMillis(); InstrumentMethodGroupResponse imgr = instrumentor.createFollowUpInstrumentMethodGroupResponse(cmd); instrProcessingTime += (System.currentTimeMillis() - curTime); //if (imgr != null && ! imgr.isEmpty()) { // System.err.println("*** Profiler Engine: instrumentMethodGroupFollowUp() produced response:"); // imgr.dump(); // } sendComplexRespToServer(imgr); } checkForInstrMethodsLimitReached(); } private void instrumentMethodGroupFromRoot(final RootClassLoadedCommand cmd) { synchronized (instrumentationLock) { AppStatusHandler.AsyncDialog waitDialog = null; try { InstrumentMethodGroupResponse imgr; if (!serverClassesInitialized) { // Check if it is a fake command from server, used to just pre-initialize // some internal server classes if (handleFakeClassLoad(cmd)) { return; } } appStatusHandler.pauseLiveUpdates(); if (status.targetAppRunning) { waitDialog = appStatusHandler.getAsyncDialogInstance(PERFORMING_INSTRUMENTATION_STRING, true, null); waitDialog.display(); } // If the application is not running yet, it means that instrumentation is performed on startup. In that case, // it typically takes very short time, so there is no real need to display this progress dialog. Additionally, // the AWT Event Queue thread may be blocked in the call to startTargetApp, in which case this whole thing will // hang (due to getAsyncDialogInstance NB implementation waiting on this thread's lock). try { long curTime = System.currentTimeMillis(); imgr = instrumentor.createInitialInstrumentMethodGroupResponse(cmd); instrProcessingTime += (System.currentTimeMillis() - curTime); } catch (BadLocationException ex) { imgr = new InstrumentMethodGroupResponse(null); // Can currently happen only for INSTR_CODE_REGION appStatusHandler.displayError(INVALID_CODE_REGION_MSG); } catch (ClassNotFoundException ex) { imgr = new InstrumentMethodGroupResponse(null); if (getCurrentInstrType() == INSTR_CODE_REGION) { appStatusHandler.displayError(MessageFormat.format(CLASS_NOT_FOUND_MSG, new Object[] { ex.getMessage() })); } else { MiscUtils.printErrorMessage("problem in instrumentMethodGroupFromRoot: " + ex); // NOI18N } } //if (imgr != null ! imgr.isEmpty()) { // System.err.println("*** Profiler Engine: instrumentMethodGroupFromRoot() produced response:"); // imgr.dump(); } // else System.err.println("*** Profiler Engine: instrumentMethodGroupFromRoot() produced empty response"); sendComplexRespToServer(imgr); } finally { if (waitDialog != null) { waitDialog.close(); } appStatusHandler.resumeLiveUpdates(); } } } /** * Upon receipt of the BUFFER_FULL command from the server, read and process the buffer contents */ private void readAndProcessProfilingResults(EventBufferDumpedCommand cmd) { int bufSize = cmd.getBufSize(); if (bufSize == 0) { // zero size may happen when dump is forced when there is actually no new information generated sendSimpleRespToServer(true, null); return; } handlingEventBufferDump = true; // Results of memory/CPU profiling can be processed concurrently to take advantage of a possible multiprocessor machine. // Similarly, during remote profiling any results can be processed concurrently, since processing on a different // machine will not disturb execution timing on the TA machine. Note also that if this command is // received as a result of the forced dump (as opposed to the normal one due to buffer overflow), the data should // be processed synchronously to avoid e.g. a "no results" report when there are already some. // update [ian] In case of remote profiling we actually can not process the results in concurrently, // since there would suddenly be 2 pieces of code that simultaneously read from the socket stream // leading to issue 59660: JFluid: error writing collected data to the socket // see http://www.netbeans.org/issues/show_bug.cgi?id=59660 for details if (!status.remoteProfiling && !forceObtainedResultsDumpCalled) { // Note that the call below may block, waiting for separarateCmdExecThread to finish its current job. // That means that nothing in readResultsFromBuffer() that this command eventually calls, is allowed to // send a command to the server and await a response. If that happens, the communication thread will be // unavailable for reading server's response (because it's waiting here), effectively causing a deadlock. executeInSeparateThread(cmd); handlingEventBufferDump = false; } else { // Process profiling results synchronously in case of: // - remote profiling // - explicite Get results (forceObtainedResultsDumpCalled) byte[] buf = EventBufferProcessor.readDataAndPrepareForProcessing(cmd); EventBufferResultsProvider.getDefault().dataReady(buf, getCurrentInstrType()); handlingEventBufferDump = false; sendSimpleRespToServer(true, null); forceObtainedResultsDumpCalled = false; } } /** * @param cmd Command to send * @return null if command was confirmed OK from Agent, Error message otherwise * @throws ClientUtils.TargetAppOrVMTerminated * */ private synchronized String sendCommandAndGetResponse(Command cmd) throws ClientUtils.TargetAppOrVMTerminated { sendComplexCmdToServer(cmd); Response resp = getLastResponse(); if (!resp.isOK()) { MiscUtils.printErrorMessage("error in sendCommandAndGetResponse: for cmd = " + cmd // NOI18N + " and resp = " + resp + " got error message: " + resp.getErrorMessage() // NOI18N ); return resp.getErrorMessage(); } else { return null; } } private void sendComplexCmdToServer(Command cmd) throws ClientUtils.TargetAppOrVMTerminated { try { wireIO.sendComplexCommand(cmd); } catch (IOException ex) { handleIOExceptionOnSend(ex); } } private void sendComplexRespToServer(Response resp) { try { wireIO.sendComplexResponse(resp); } catch (IOException ex) { MiscUtils.printErrorMessage("exception when trying to send a response: " + ex); // NOI18N try { handleIOExceptionOnSend(ex); } catch (ClientUtils.TargetAppOrVMTerminated ex1) { /* All done already */ } } } private void sendSimpleCmdToServer(int cmdType) throws ClientUtils.TargetAppOrVMTerminated { try { wireIO.sendSimpleCommand(cmdType); } catch (IOException ex) { handleIOExceptionOnSend(ex); } } /** * @param cmd Command to send * @return null if command was confirmed OK from Agent, Error message otherwise * @throws ClientUtils.TargetAppOrVMTerminated * */ private synchronized String sendSimpleCommandAndGetResponse(int cmdType) throws ClientUtils.TargetAppOrVMTerminated { sendSimpleCmdToServer(cmdType); Response resp = getLastResponse(); if (!resp.isOK()) { MiscUtils.printErrorMessage("error in sendCommandAndGetResponse: for cmdType = " + cmdType // NOI18N + " and resp = " + resp + " got error message: " + resp.getErrorMessage() // NOI18N ); return resp.getErrorMessage(); } else { return null; } } private void sendSimpleRespToServer(boolean val, String errorMessage) { try { wireIO.sendSimpleResponse(val, errorMessage); } catch (IOException ex) { try { handleIOExceptionOnSend(ex); } catch (ClientUtils.TargetAppOrVMTerminated ex1) { /* All done already */ } } } private InitiateProfilingCommand createInitiateInstrumnetation(int instrType, String[] classNames, boolean instrSpawnedThreads, boolean startProfilingPointsActive) { RuntimeProfilingPoint points[] = settings.getRuntimeProfilingPoints(); String[] profilingPointHandlers = new String[points.length]; String[] profilingPointInfos = new String[points.length]; int[] profilingPointIDs = new int[points.length]; Arrays.sort(points); // ProfilerRuntime uses Arrays.binarySearch for (int i = 0; i < points.length; i++) { RuntimeProfilingPoint point = points[i]; profilingPointIDs[i] = point.getId(); profilingPointHandlers[i] = point.getServerHandlerClass(); profilingPointInfos[i] = point.getServerInfo(); } return new InitiateProfilingCommand(instrType,classNames, profilingPointIDs,profilingPointHandlers,profilingPointInfos, instrSpawnedThreads,startProfilingPointsActive); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy