0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: * The Original Software is NetBeans. The Initial Developer of the Original
0026: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0027: * Microsystems, Inc. All Rights Reserved.
0028: *
0029: * If you wish your version of this file to be governed by only the CDDL
0030: * or only the GPL Version 2, indicate your decision by adding
0031: * "[Contributor] elects to include this software in this distribution
0032: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0033: * single choice of license, a recipient has the option to distribute
0034: * your version of this file under either the CDDL, the GPL Version 2 or
0035: * to extend the choice of license to its licensees as provided above.
0036: * However, if you add GPL Version 2 code and therefore, elected the GPL
0037: * Version 2 license, then the option applies only if the new code is
0038: * made subject to such option by the copyright holder.
0039: */
0040:
0041: package org.netbeans.lib.profiler.server;
0042:
0043: import org.netbeans.lib.profiler.global.CalibrationDataFileIO;
0044: import org.netbeans.lib.profiler.global.CommonConstants;
0045: import org.netbeans.lib.profiler.global.Platform;
0046: import org.netbeans.lib.profiler.global.ProfilingSessionStatus;
0047: import org.netbeans.lib.profiler.server.system.Classes;
0048: import org.netbeans.lib.profiler.server.system.GC;
0049: import org.netbeans.lib.profiler.server.system.HeapDump;
0050: import org.netbeans.lib.profiler.server.system.Threads;
0051: import org.netbeans.lib.profiler.server.system.Timers;
0052: import org.netbeans.lib.profiler.wireprotocol.*;
0053: import java.io.*;
0054: import java.lang.InterruptedException;
0055: import java.lang.reflect.InvocationTargetException;
0056: import java.lang.reflect.Method;
0057: import java.lang.reflect.Modifier;
0058: import java.net.*;
0059: import java.text.MessageFormat;
0060: import java.util.*;
0061:
0062: /**
0063: * This class contains functionality for starting (attaching to) the Target Application (TA), and for
0064: * communication between the profiling back end and the tool (server and client).
0065: *
0066: * @author Tomas Hurka
0067: * @author Misha Dmitriev
0068: * @author Ian Formanek
0069: */
0070: public class ProfilerServer extends Thread implements CommonConstants {
0071: //~ Inner Classes ------------------------------------------------------------------------------------------------------------
0072:
0073: private static class AttachDynamicThread extends Thread {
0074: //~ Instance fields ------------------------------------------------------------------------------------------------------
0075:
0076: private int activateCode;
0077:
0078: //~ Constructors ---------------------------------------------------------------------------------------------------------
0079:
0080: AttachDynamicThread(int activateCode) {
0081: this .setName(PROFILER_SPECIAL_EXEC_THREAD_NAME + " 5"); // NOI18N
0082: this .activateCode = activateCode;
0083: }
0084:
0085: //~ Methods --------------------------------------------------------------------------------------------------------------
0086:
0087: public void run() {
0088: try {
0089: doActivate(activateCode);
0090: } catch (Throwable ex) {
0091: System.err
0092: .println("Profiler dynamic attach initialization failed due to:"); //NOI18N
0093: ex.printStackTrace();
0094: }
0095: }
0096: }
0097:
0098: // Copied from org.openide.util.NbBundle
0099: // Does not support branding!
0100: private static class LocaleIterator extends Object implements
0101: Iterator {
0102: //~ Instance fields ------------------------------------------------------------------------------------------------------
0103:
0104: /**
0105: * current locale, and initial locale
0106: */
0107: private Locale initLocale;
0108:
0109: /**
0110: * current locale, and initial locale
0111: */
0112: private Locale locale;
0113:
0114: /**
0115: * the branding string in use
0116: */
0117: private String branding;
0118:
0119: /**
0120: * current sufix which will be returned in next calling nextElement
0121: */
0122: private String current;
0123:
0124: /**
0125: * this flag means, if default locale is in progress
0126: */
0127: private boolean defaultInProgress = false;
0128:
0129: /**
0130: * this flag means, if empty sufix was exported yet
0131: */
0132: private boolean empty = false;
0133:
0134: //~ Constructors ---------------------------------------------------------------------------------------------------------
0135:
0136: /**
0137: * Creates new LocaleIterator for given locale.
0138: *
0139: * @param locale given Locale
0140: */
0141: public LocaleIterator(Locale locale) {
0142: this .locale = this .initLocale = locale;
0143:
0144: if (locale.equals(Locale.getDefault())) {
0145: defaultInProgress = true;
0146: }
0147:
0148: current = '_' + locale.toString(); // NOI18N
0149:
0150: // if (brandingToken == null) {
0151: branding = null;
0152:
0153: // } else {
0154: // branding = "_" + brandingToken; // NOI18N
0155: // }
0156:
0157: //System.err.println("Constructed: " + this);
0158: }
0159:
0160: //~ Methods --------------------------------------------------------------------------------------------------------------
0161:
0162: /**
0163: * Tests if there is any sufix.
0164: */
0165: public boolean hasNext() {
0166: return (current != null);
0167: }
0168:
0169: /**
0170: * @return next sufix.
0171: * @throws NoSuchElementException if there is no more locale sufix.
0172: */
0173: public Object next() throws NoSuchElementException {
0174: if (current == null) {
0175: throw new NoSuchElementException();
0176: }
0177:
0178: final String ret;
0179:
0180: if (branding == null) {
0181: ret = current;
0182: } else {
0183: ret = branding + current;
0184: }
0185:
0186: int lastUnderbar = current.lastIndexOf('_'); // NOI18N
0187:
0188: if (lastUnderbar == 0) {
0189: if (empty) {
0190: reset();
0191: } else {
0192: current = ""; // NOI18N
0193: empty = true;
0194: }
0195: } else {
0196: if (lastUnderbar == -1) {
0197: if (defaultInProgress) {
0198: reset();
0199: } else {
0200: // [PENDING] stuff with trying the default locale
0201: // after the real one does not actually seem to work...
0202: locale = Locale.getDefault();
0203: current = '_' + locale.toString(); // NOI18N
0204: defaultInProgress = true;
0205: }
0206: } else {
0207: current = current.substring(0, lastUnderbar);
0208: }
0209: }
0210:
0211: //System.err.println("Returning: `" + ret + "' from: " + this);
0212: return ret;
0213: }
0214:
0215: public void remove() throws UnsupportedOperationException {
0216: throw new UnsupportedOperationException();
0217: }
0218:
0219: /**
0220: * Finish a series.
0221: * If there was a branding prefix, restart without that prefix
0222: * (or with a shorter prefix); else finish.
0223: */
0224: private void reset() {
0225: if (branding != null) {
0226: current = '_' + initLocale.toString(); // NOI18N
0227:
0228: int idx = branding.lastIndexOf('_'); // NOI18N
0229:
0230: if (idx == 0) {
0231: branding = null;
0232: } else {
0233: branding = branding.substring(0, idx);
0234: }
0235:
0236: empty = false;
0237: } else {
0238: current = null;
0239: }
0240: }
0241: }
0242:
0243: /**
0244: * A shutdown wait thread
0245: */
0246: private static class ShutdownWaitThread extends Thread {
0247: //~ Constructors ---------------------------------------------------------------------------------------------------------
0248:
0249: public ShutdownWaitThread() {
0250: setName(PROFILER_SPECIAL_EXEC_THREAD_NAME + " 7"); // NOI18N
0251: }
0252:
0253: //~ Methods --------------------------------------------------------------------------------------------------------------
0254:
0255: public void run() {
0256: if (preemptExit && connectionOpen) {
0257: profilerServer
0258: .sendSimpleCmdToClient(Command.SHUTDOWN_INITIATED);
0259: waitForShutdownOK();
0260: cleanupOnShutdown();
0261:
0262: // ... and proceed with shutdown
0263: }
0264: }
0265: }
0266:
0267: /**
0268: * A thread to execute certain commands in (see comments to executeInSeparateThread above)
0269: */
0270: private class SeparateCmdExecutionThread extends Thread {
0271: //~ Instance fields ------------------------------------------------------------------------------------------------------
0272:
0273: private boolean stopped = false;
0274:
0275: //~ Constructors ---------------------------------------------------------------------------------------------------------
0276:
0277: public SeparateCmdExecutionThread() {
0278: ThreadInfo.addProfilerServerThread(this );
0279: setName(PROFILER_SPECIAL_EXEC_THREAD_NAME + " 6"); // NOI18N
0280: setDaemon(true);
0281: }
0282:
0283: //~ Methods --------------------------------------------------------------------------------------------------------------
0284:
0285: public void run() {
0286: synchronized (execInSeparateThreadLock) {
0287: while (true) {
0288: try {
0289: execInSeparateThreadLock.wait();
0290: } catch (InterruptedException ex) {
0291: System.err.println(THREAD_WAIT_EXCEPTION_MSG); // NOI18N
0292: }
0293:
0294: if (stopped) {
0295: return;
0296: }
0297:
0298: int opCode = execInSeparateThreadOpCode;
0299:
0300: switch (opCode) {
0301: case Command.DUMP_EXISTING_RESULTS:
0302: case Command.DUMP_EXISTING_RESULTS_LIVE:
0303:
0304: long absTimeStamp = ProfilerRuntimeCPU
0305: .getAbsTimeStampInCollectedFormat();
0306: boolean res = false;
0307:
0308: if ((ProfilerRuntime.eventBuffer != null)
0309: && !ProfilerRuntime.sendingBuffer) {
0310: synchronized (ProfilerRuntime.eventBuffer) {
0311: res = ProfilerInterface.serialClientOperationsLock
0312: .beginTrans(true, true);
0313:
0314: if (res) {
0315: try {
0316: ProfilerInterface
0317: .dumpExistingResults(opCode == Command.DUMP_EXISTING_RESULTS_LIVE);
0318: } finally {
0319: ProfilerInterface.serialClientOperationsLock
0320: .endTrans();
0321: }
0322: }
0323: }
0324: }
0325:
0326: DumpResultsResponse resp = new DumpResultsResponse(
0327: res, absTimeStamp);
0328: sendComplexResponseToClient(resp);
0329:
0330: break;
0331: case Command.RESET_PROFILER_COLLECTORS:
0332: requestClientResetResults();
0333: sendSimpleResponseToClient(true, null);
0334:
0335: break;
0336: }
0337: }
0338: }
0339: }
0340:
0341: public void terminate() {
0342: stopped = true;
0343: }
0344: }
0345:
0346: //~ Static fields/initializers -----------------------------------------------------------------------------------------------
0347:
0348: // -----
0349: // I18N String constants
0350: // !!! Warning - do not use ResourceBundle.getBundle here, won't work in context of direct/dynamic attach !!!
0351: // Default EN messages initialized here, will be replaced by localized messages in initLocalizedResources()
0352: private static ResourceBundle messages;
0353: private static String ENTER_TO_SHUTDOWN_MSG = "Press ENTER to shut down the target JVM..."; // NOI18N
0354: private static String MAIN_CLASS_NOT_PUBLIC_MSG = "Main class {0} is not public.\nProfiler can not start it"; // NOI18N
0355: private static String INCORRECT_MAIN_MODIFIERS_MSG = "Method {0}.main(String args[]) has incorrect modifiers"; // NOI18N
0356: private static String UNEXPECTED_EXCEPTION_MSG = "Target application threw an unexpected exception: {0}"; // NOI18N
0357: private static String ELAPSED_TIME_MSG = "Main application thread elapsed time: {0} ms."; // NOI18N
0358: private static String REMOTE_CONNECTION_MSG = "Profiler Agent: Established remote connection with the tool"; // NOI18N
0359: private static String LOCAL_CONNECTION_MSG = "Profiler Agent: Established local connection with the tool"; // NOI18N
0360: private static String WAITING_ON_PORT_MSG = "Profiler Agent: Waiting for connection on port {0} (Protocol version: {1})"; // NOI18N
0361: private static String WAITING_ON_PORT_TIMEOUT_MSG = "Profiler Agent: Waiting for connection on port {0}, timeout {1} seconds (Protocol version: {2})"; // NOI18N
0362: private static String CONNECTION_EXCEPTION_MSG = "Profiler Agent Error: Exception when trying to establish connection with client:\n{0}"; // NOI18N
0363: private static String CONNECTION_TIMEOUT_MSG = "Profiler Agent Error: Timed out trying to establish connection with client"; // NOI18N
0364: private static String AGENT_ERROR_MSG = "Profiler Agent Error: {0}"; // NOI18N
0365: private static String CONNECTION_INTERRUPTED_MSG = "Profiler Agent Error: Connection with client interrupted"; // NOI18N
0366: private static String COMMAND_EXCEPTION_MSG = "Profiler Agent Error: Exception when handling command from client:\n{0}"; // NOI18N
0367: private static String RESPONSE_EXCEPTION_MSG = "Profiler Agent Error: Exception when trying to send response or command to client:\n{0}"; // NOI18N
0368: private static String CONNECTION_CLOSED_MSG = "Profiler Agent: Connection with agent closed"; // NOI18N
0369: private static String INCORRECT_AGENT_ID_MSG = "Profiler Agent Warning: Wrong agentId specified: {0}"; // NOI18N
0370: private static String THREAD_EXCEPTION_MSG = "Profiler Agent Error: Exception in executeInSeparateThread()"; // NOI18N
0371: private static String THREAD_WAIT_EXCEPTION_MSG = "Profiler Agent Error: Exception in wait in SeparateCmdExecutionThread"; // NOI18N
0372: // -----
0373: public static final int ATTACH_DYNAMIC = 0;
0374: public static final int ATTACH_DIRECT = 1;
0375: private static volatile boolean profilerInterfaceInitialized;
0376: private static volatile boolean connectionOpen;
0377: private static volatile boolean connectionFailed;
0378: private static volatile boolean detachCommandReceived;
0379: private static ProfilerServer profilerServer;
0380: private static ProfilingSessionStatus status;
0381: private static volatile boolean startTargetApp;
0382: private static volatile boolean targetAppMainThreadComplete;
0383: private static volatile Exception startupException;
0384: private static Object targetAppRunningLock;
0385: private static Thread mainThread;
0386:
0387: // Management of execution of some commands in a separate thread
0388: private static SeparateCmdExecutionThread separateCmdExecutionThread;
0389: private static ShutdownWaitThread shutdownWaitThread;
0390: static Object execInSeparateThreadLock;
0391: static int execInSeparateThreadOpCode;
0392: private static boolean preemptExit = true;
0393: private static boolean shutdownOK = false;
0394: private static final Object shutdownLock = new Object();
0395: private static final Object resultsNotifiedLock = new Object();
0396:
0397: // @GuardedBy resultsNotifiedLock
0398: private static boolean resultsNotified = false;
0399: private static boolean resourcesInitialized = false;
0400:
0401: // This data is needed to avoid passing parameters to doActivate() which may cause problems in attach by pid mode on Windows.
0402: private static String _fullJFluidPath;
0403: private static int _portNo;
0404: private static int _activateCode;
0405: private static int _timeOut = 0;
0406: private static Response lastResponse;
0407: private static Object responseLock = new Object();
0408:
0409: //~ Instance fields ----------------------------------------------------------------------------------------------------------
0410:
0411: private ObjectInputStream socketIn;
0412: private ObjectOutputStream socketOut;
0413: private ServerSocket serverSocket;
0414: private Socket clientSocket;
0415: private WireIO wireIO;
0416: private boolean dynamic;
0417: private int agentId = -1;
0418:
0419: //---------------------------------------------------------------------------------------
0420: // Communication management
0421: //---------------------------------------------------------------------------------------
0422: private int serverPort;
0423: private int serverTimeout = 0; // no timeout by default
0424:
0425: //~ Constructors -------------------------------------------------------------------------------------------------------------
0426:
0427: private ProfilerServer(int port, boolean dynamic, int timeout) {
0428: super (PROFILER_SERVER_THREAD_NAME);
0429: setPriority(Thread.MAX_PRIORITY);
0430: serverPort = port;
0431: ThreadInfo.addProfilerServerThread(this );
0432: this .dynamic = dynamic;
0433:
0434: if (!dynamic) {
0435: // for dynamic attach, the server should never timeout
0436: serverTimeout = timeout;
0437: }
0438:
0439: setDaemon(true);
0440: }
0441:
0442: //~ Methods ------------------------------------------------------------------------------------------------------------------
0443:
0444: public static synchronized Response getLastResponse() {
0445: Response res;
0446:
0447: synchronized (responseLock) {
0448: if (lastResponse == null) {
0449: // I had to introduce the check below, since for some applications, seemingly the GUI ones that open a FileChooser dialog,
0450: // we can somehow get an InterruptedException below. This is likely a bug in JDK - maybe AWT just browses and calls
0451: // Thread.interrupt() that causes this exception, on waiting threads, and can mistake our thread for its own or something.
0452: boolean gotInterrupted = false;
0453:
0454: do {
0455: try {
0456: responseLock.wait();
0457: gotInterrupted = false;
0458: } catch (InterruptedException ex) {
0459: //System.err.println("*** JFluid warning: InterruptedException in ProfilerServer.getLastResponse()");
0460: gotInterrupted = true;
0461: }
0462: } while (gotInterrupted);
0463:
0464: if (lastResponse == null) {
0465: System.out
0466: .println("Profiler Agent Error: lastResponse == null - internal error?"); // NOI18N
0467: }
0468: }
0469:
0470: res = lastResponse;
0471: lastResponse = null;
0472: }
0473:
0474: return res;
0475: }
0476:
0477: public static Thread getMainThread() {
0478: return mainThread;
0479: }
0480:
0481: public static ProfilingSessionStatus getProfilingSessionStatus() {
0482: return status;
0483: }
0484:
0485: public static boolean isTargetAppMainThreadComplete() {
0486: return targetAppMainThreadComplete;
0487: }
0488:
0489: public static void activate(String fullJFluidPath, int portNo,
0490: final int activateCode) {
0491: activate(fullJFluidPath, portNo, activateCode, 0);
0492: }
0493:
0494: /**
0495: * Entrypoint in the usage scenario when the client attaches to the running target app using an OS signal,
0496: * or the "attach on startup" method. On JDK 1.5,
0497: * called from ProfilerActivate15.premain().
0498: * activateCode == 0 : "attach on the fly", activateCode == 1 : "attach on startup"
0499: *
0500: * @param fullJFluidPath Full path to the agent libs
0501: * @param portNo Port number to use
0502: * @param activateCode one of ATTACH_DIRECT or ATTACH_DYNAMIC, determines whether the server is started in dynamic
0503: * attach mode ( JDK 1.6) or Direct attach
0504: * @param timeOut Time out in seconds for server socket, or 0 for no timeout
0505: * @see #ATTACH_DIRECT
0506: * @see #ATTACH_DYNAMIC
0507: */
0508: public static void activate(String fullJFluidPath, int portNo,
0509: final int activateCode, int timeOut) {
0510: try {
0511: _fullJFluidPath = fullJFluidPath;
0512: _portNo = portNo;
0513: _timeOut = timeOut;
0514: _activateCode = activateCode;
0515:
0516: initLocalizedResources();
0517:
0518: if (activateCode == ATTACH_DYNAMIC) {
0519: // Creation of the new thread is (hopefully) a temporary workaround to avoid the problem with stack
0520: // overflow or something else when we attach on Windows "by pid", i.e. using the CreateRemoteThread() call.
0521: new AttachDynamicThread(activateCode).start();
0522: } else {
0523: doActivate(activateCode);
0524: }
0525: } catch (Throwable ex) {
0526: System.err
0527: .println("Profiler initialization failed due to:"); //NOI18N
0528: ex.printStackTrace();
0529: }
0530: }
0531:
0532: /**
0533: * Entrypoint in the usage scenario where the client starts and stops the target application.
0534: * Start the communication thread and then the target application.
0535: * args[0] is the full path to the directory where JFluid native libraries are contained.
0536: * args[1] is the communication port number.
0537: * args[2] (optional) if it is a number, it is a timeout for the profiler server (in seconds) to wait until the
0538: * client connects
0539: * args[2 or 3] is the target app main class name; args[3 or 4..n] are its arguments.
0540: */
0541: public static void main(String[] args) {
0542: mainThread = Thread.currentThread();
0543:
0544: // Fix for Issue 69454 - cannot find path to Profiler libraries (http://www.netbeans.org/issues/show_bug.cgi?id=69454)
0545: // _fullJFluidPath is needed for lazy initializing localized messages, but it was originally set only by the activate() method
0546: // Now it has to be set also here for the I18N to work
0547: try {
0548: _fullJFluidPath = new File(args[0]).getParentFile()
0549: .getParentFile().getParentFile().getAbsolutePath();
0550: } catch (Exception ex) {
0551: throw new RuntimeException(
0552: "ProfilerServer: Unable to initialize ResourceBundle for ProfilerServer, cannot resolve library directory\n" // NOI18N
0553: + ex.getMessage());
0554: }
0555:
0556: initLocalizedResources();
0557: initInternals();
0558:
0559: // Get the port number
0560: int portNo = 0;
0561:
0562: try {
0563: portNo = Integer.parseInt(args[1]);
0564: } catch (NumberFormatException e) {
0565: internalError("illegal port number specified: " + args[1]); // NOI18N
0566: }
0567:
0568: int idx = 2;
0569:
0570: // Get the optional timeout number
0571: int timeout = 0;
0572:
0573: try {
0574: timeout = Integer.parseInt(args[2]);
0575: idx = 3;
0576: } catch (NumberFormatException e) {
0577: // timeout not specified (it is optional)
0578: }
0579:
0580: // Move the target app arguments into special array
0581: int len = args.length - (idx + 1);
0582: String[] targetAppArgs = new String[len];
0583: System.arraycopy(args, idx + 1, targetAppArgs, 0, len);
0584:
0585: // Start the communication thread and wait for it to establish connection with client
0586: profilerServer = new ProfilerServer(portNo, true, timeout);
0587: profilerServer.start();
0588:
0589: while (!(connectionOpen || connectionFailed)) {
0590: delay(50);
0591: }
0592:
0593: if (connectionFailed) {
0594: // prevent the console from dying without the user being able to see the error
0595: // pressEnterToShutDown();
0596: // no cleanup in this case, as there is no connection established
0597: preemptExit = false;
0598: System.exit(-1);
0599: }
0600:
0601: ProfilerInterface.setProfilerServer(profilerServer);
0602:
0603: initSupportingFunctionality(false, profilerServer
0604: .isRemoteProfiling());
0605:
0606: // Accept, or wait for, the client command to start the target app, and then start it.
0607: while (!startTargetApp) {
0608: delay(100);
0609: }
0610:
0611: runTargetApp(args[idx], targetAppArgs);
0612: targetAppMainThreadComplete = true;
0613:
0614: // If we haven't actually managed to start the app, notify the waiting communication thread.
0615: if (startupException != null) {
0616: synchronized (targetAppRunningLock) {
0617: targetAppRunningLock.notify();
0618: }
0619: }
0620:
0621: // Wait for some time in case the target app started some threads and then exited the main thread, while the
0622: // offspring threads have not yet fully initialized
0623: delay(300);
0624:
0625: // Now wait until all target app threads (excluding this, main one) terminate.
0626: while (Threads.targetAppThreadsExist()) {
0627: delay(300);
0628: }
0629:
0630: status.targetAppRunning = false;
0631: ProfilerInterface.disableProfilerHooks(); // So that e.g. System.exit() doesn't cause class loads and command sends
0632: // DEBUGGING: if it's needed to check how good is the sampling interval when sampled instrumentation is used,
0633: // decomment the one below to make the sampling thread stop here and report the debug data.
0634: // ProfilerRuntimeCPUSampledInstr.clearDataStructures();
0635:
0636: profilerServer
0637: .sendSimpleCmdToClient(Command.SHUTDOWN_INITIATED);
0638: waitForShutdownOK();
0639: forcedShutdown();
0640: }
0641:
0642: public boolean isRemoteProfiling() {
0643: String socketAddr = clientSocket.getRemoteSocketAddress()
0644: .toString();
0645: boolean res = !(((socketAddr.indexOf("127.0.0.1") != -1) || socketAddr
0646: .startsWith("localhost"))); // NOI18N
0647:
0648: if (res) {
0649: System.out.println(REMOTE_CONNECTION_MSG);
0650: } else {
0651: System.out.println(LOCAL_CONNECTION_MSG);
0652: }
0653:
0654: return res;
0655: }
0656:
0657: public ObjectOutputStream getSocketOutputStream() {
0658: return socketOut;
0659: }
0660:
0661: public static void notifyClientOnResultsAvailability() {
0662: if (!connectionOpen) {
0663: return;
0664: }
0665:
0666: if (profilerServer == null) {
0667: return; // in calibration mode
0668: }
0669:
0670: synchronized (resultsNotifiedLock) {
0671: if (resultsNotified) {
0672: return; // no need to notify again
0673: }
0674:
0675: resultsNotified = true;
0676: profilerServer
0677: .sendSimpleCmdToClient(Command.RESULTS_AVAILABLE);
0678: }
0679: }
0680:
0681: public static void requestClientResetResults() {
0682: ProfilerInterface.resetProfilerCollectors();
0683: ProfilerCalibrator.resetInternalStatsCollectors();
0684: }
0685:
0686: public static void requestClientTakeSnapshot() {
0687: if (profilerServer == null) {
0688: return; // in calibration mode
0689: }
0690:
0691: profilerServer.sendSimpleCmdToClient(Command.TAKE_SNAPSHOT);
0692: }
0693:
0694: public boolean getAndCheckLastResponse() {
0695: Response resp = getLastResponse();
0696:
0697: return resp.isOK();
0698: }
0699:
0700: public void run() {
0701: if (connectToClient()) {
0702: while (!profilerInterfaceInitialized) {
0703: delay(50);
0704: }
0705:
0706: listenToClient();
0707: } else {
0708: preemptExit = false;
0709: }
0710: }
0711:
0712: public void sendClassLoaderUnloadingCommand() {
0713: sendSimpleCmdToClient(Command.CLASS_LOADER_UNLOADING);
0714: getLastResponse();
0715: }
0716:
0717: public synchronized void sendComplexCmdToClient(Command cmd) {
0718: try {
0719: wireIO.sendComplexCommand(cmd);
0720: } catch (IOException ex) {
0721: if (!detachCommandReceived) {
0722: handleIOExceptionOnSend(ex);
0723: }
0724: }
0725: }
0726:
0727: public synchronized void sendComplexResponseToClient(Response resp) {
0728: try {
0729: wireIO.sendComplexResponse(resp);
0730: } catch (IOException ex) {
0731: if (!detachCommandReceived) {
0732: handleIOExceptionOnSend(ex);
0733: }
0734: }
0735: }
0736:
0737: // Several methods to send commands specific for modules that use wireprotocol just occasionally
0738: public boolean sendEventBufferDumpedCommand(int length,
0739: boolean waitForResponse) {
0740: EventBufferDumpedCommand cmd = new EventBufferDumpedCommand(
0741: length);
0742: sendComplexCmdToClient(cmd);
0743:
0744: if (waitForResponse) {
0745: Response resp = getLastResponse();
0746:
0747: return resp.isOK();
0748: } else {
0749: return true;
0750: }
0751: }
0752:
0753: public synchronized void sendSimpleCmdToClient(int cmdType) {
0754: try {
0755: wireIO.sendSimpleCommand(cmdType);
0756: } catch (IOException ex) {
0757: if (!detachCommandReceived) {
0758: handleIOExceptionOnSend(ex);
0759: }
0760: }
0761: }
0762:
0763: public synchronized void sendSimpleResponseToClient(boolean val,
0764: String errorMessage) {
0765: try {
0766: wireIO.sendSimpleResponse(val, errorMessage);
0767: } catch (IOException ex) {
0768: if (!detachCommandReceived) {
0769: handleIOExceptionOnSend(ex);
0770: }
0771: }
0772: }
0773:
0774: // --- I18N Support ----------------------------------------------------------
0775:
0776: // This method is used for obtaining ResourceBundle from classes that can be
0777: // used by ProfilerServer in context of direct/dynamic attach.
0778: //
0779: // If path to profiler server libraries (.jar) is known, ResourceBundle is obtained
0780: // using custom classloader (solves problem with bootstrap classloader&dynamic attach)
0781: //
0782: // Does not support branding!
0783: static ResourceBundle getProfilerServerResourceBundle() {
0784: if (messages != null) {
0785: return messages;
0786: }
0787:
0788: // 1. try to get the ResourceBundle using custom classloader
0789: if (_fullJFluidPath != null) {
0790: try {
0791: messages = getProfilerServerResourceBundle(_fullJFluidPath);
0792: } catch (Exception e) {
0793: System.err
0794: .println("Profiler Server: Problem with customized initializing localized messages...\n"
0795: + e.getMessage()); // NOI18N
0796: }
0797: }
0798:
0799: ; // cannot find jfluid-server.jar or Bundle.properties not found
0800:
0801: if (messages != null) {
0802: return messages; // ResourceBundle successfuly loaded using custom classloader
0803: }
0804:
0805: // 2. try to get the ResourceBundle in standard way
0806: try {
0807: messages = ResourceBundle
0808: .getBundle("org.netbeans.lib.profiler.server.Bundle"); // NOI18N
0809: } catch (Exception e) {
0810: System.err
0811: .println("Profiler Server: Problem with default initializing localized messages...\n"
0812: + e.getMessage()); // NOI18N
0813: }
0814:
0815: ;
0816:
0817: return messages;
0818: }
0819:
0820: static void initLocalizedResources() {
0821: if (resourcesInitialized) {
0822: return;
0823: }
0824:
0825: messages = getProfilerServerResourceBundle();
0826:
0827: if (messages != null) {
0828: ENTER_TO_SHUTDOWN_MSG = messages
0829: .getString("ProfilerServer_EnterToShutdownMsg"); // NOI18N
0830: MAIN_CLASS_NOT_PUBLIC_MSG = messages
0831: .getString("ProfilerServer_MainClassNotPublicMsg"); // NOI18N
0832: INCORRECT_MAIN_MODIFIERS_MSG = messages
0833: .getString("ProfilerServer_IncorrectMainModifiersMsg"); // NOI18N
0834: UNEXPECTED_EXCEPTION_MSG = messages
0835: .getString("ProfilerServer_UnexpectedExceptionMsg"); // NOI18N
0836: ELAPSED_TIME_MSG = messages
0837: .getString("ProfilerServer_ElapsedTimeMsg"); // NOI18N
0838: REMOTE_CONNECTION_MSG = messages
0839: .getString("ProfilerServer_RemoteConnectionMsg"); // NOI18N
0840: LOCAL_CONNECTION_MSG = messages
0841: .getString("ProfilerServer_LocalConnectionMsg"); // NOI18N
0842: WAITING_ON_PORT_MSG = messages
0843: .getString("ProfilerServer_WaitingOnPortMsg"); // NOI18N
0844: WAITING_ON_PORT_TIMEOUT_MSG = messages
0845: .getString("ProfilerServer_WaitingOnPortTimeoutMsg"); // NOI18N
0846: CONNECTION_EXCEPTION_MSG = messages
0847: .getString("ProfilerServer_ConnectionExceptionMsg"); // NOI18N
0848: CONNECTION_TIMEOUT_MSG = messages
0849: .getString("ProfilerServer_ConnectionTimeoutMsg"); // NOI18N
0850: AGENT_ERROR_MSG = messages
0851: .getString("ProfilerServer_AgentErrorMsg"); // NOI18N
0852: CONNECTION_INTERRUPTED_MSG = messages
0853: .getString("ProfilerServer_ConnectionInterruptedMsg"); // NOI18N
0854: COMMAND_EXCEPTION_MSG = messages
0855: .getString("ProfilerServer_CommandExceptionMsg"); // NOI18N
0856: RESPONSE_EXCEPTION_MSG = messages
0857: .getString("ProfilerServer_ResponseExceptionMsg"); // NOI18N
0858: CONNECTION_CLOSED_MSG = messages
0859: .getString("ProfilerServer_ConnectionClosedMsg"); // NOI18N
0860: INCORRECT_AGENT_ID_MSG = messages
0861: .getString("ProfilerServer_IncorrectAgentIdMsg"); // NOI18N
0862: THREAD_EXCEPTION_MSG = messages
0863: .getString("ProfilerServer_ThreadExceptionMsg"); // NOI18N
0864: THREAD_WAIT_EXCEPTION_MSG = messages
0865: .getString("ProfilerServer_ThreadWaitExceptionMsg"); // NOI18N
0866: resourcesInitialized = true;
0867: }
0868: }
0869:
0870: static void loadNativeLibrary(String fullJFluidPath,
0871: boolean fullPathToLibSpecified) {
0872: String libFullName = Platform.getAgentNativeLibFullName(
0873: fullJFluidPath, fullPathToLibSpecified, null, -1);
0874: System.load(libFullName);
0875: }
0876:
0877: static boolean startProfilingPointsActive() {
0878: if (status != null) {
0879: return status.startProfilingPointsActive;
0880: }
0881:
0882: return false;
0883: }
0884:
0885: private static File getInfoFile(int port) throws IOException {
0886: String dirName = Platform.getProfilerUserDir();
0887:
0888: return new File(dirName + File.separator + port); // NOI18N
0889: }
0890:
0891: private static void setShutdownOK() {
0892: synchronized (shutdownLock) {
0893: shutdownOK = true;
0894: shutdownLock.notifyAll();
0895: }
0896: }
0897:
0898: private static void cleanupOnShutdown() {
0899: ProfilerInterface.disableProfilerHooks();
0900: ProfilerRuntimeCPU.enableProfiling(false); // Bugfix for 65947: Profiler blocks a finishing profiled application
0901: // The following connectionOpen = false is done just to prevent error message from listenToClient(). When the connection
0902: // is closed either by the client or here by closeConnection(), whoever is faster, listenToClient() waiting for input in socket
0903: // will get IOException.
0904: // Be careful with this! sendResponseToClient() currently doesn't check connectionOpen value, but if it does, this should be changed.
0905:
0906: connectionOpen = false;
0907: profilerServer
0908: .sendSimpleCmdToClient(Command.SHUTDOWN_COMPLETED);
0909: profilerServer.closeConnection();
0910: }
0911:
0912: private static void delay(int ms) {
0913: try {
0914: Thread.sleep(ms);
0915: } catch (InterruptedException e) {
0916: }
0917: }
0918:
0919: /**
0920: * Note that putting the code of this into the custom thread above and thus executing in "attach and startup"
0921: * in a separate thread as well, causes the VM to crash. Probably a new thread can't be created in a call from
0922: * pre-main function.
0923: *
0924: * @param activateCode ATTACH_DYNAMIC or ATTACH_DIRECT
0925: * @see #ATTACH_DYNAMIC
0926: * @see #ATTACH_DIRECT
0927: */
0928: private static void doActivate(int activateCode) {
0929: loadNativeLibrary(_fullJFluidPath, false);
0930:
0931: ProfilerInterface.disableProfilerHooks(); // Just in case
0932: initInternals();
0933:
0934: // Start the communication thread and wait for it to establish connection with client
0935: profilerServer = new ProfilerServer(_portNo,
0936: activateCode == ATTACH_DYNAMIC, _timeOut);
0937: profilerServer.start();
0938:
0939: while (!(connectionOpen || connectionFailed)) {
0940: delay(100);
0941: }
0942:
0943: if (connectionFailed) {
0944: if (activateCode == ATTACH_DIRECT) {
0945: System.exit(-1);
0946: } else {
0947: return; // in dynamic attach we just continue with execution
0948: }
0949: }
0950:
0951: ProfilerInterface.setProfilerServer(profilerServer);
0952:
0953: initSupportingFunctionality(true, profilerServer
0954: .isRemoteProfiling());
0955:
0956: if (_activateCode == ATTACH_DIRECT) {
0957: // "Attach on startup", where we normally wait until the initiate instrumentation request arrives and instrumentation starts.
0958: // However, the user can also choose to resume the target app without any instrumentation
0959: while ((ProfilerInterface.getCurrentInstrType() == INSTR_NONE)
0960: && !status.targetAppRunning) {
0961: delay(200);
0962: }
0963:
0964: delay(100); // Wait a bit more to make sure the classLoadHook is really set
0965: }
0966:
0967: status.targetAppRunning = true;
0968: }
0969:
0970: private static void forcedShutdown() {
0971: cleanupOnShutdown();
0972: preemptExit = false;
0973: System.exit(-1);
0974: }
0975:
0976: private static void initInternals() {
0977: shutdownWaitThread = new ShutdownWaitThread();
0978: Runtime.getRuntime().addShutdownHook(shutdownWaitThread);
0979: profilerInterfaceInitialized = false;
0980: connectionOpen = false;
0981: connectionFailed = false;
0982: detachCommandReceived = false;
0983: profilerServer = null;
0984: status = null;
0985: startTargetApp = false;
0986: startupException = null;
0987: targetAppRunningLock = new Object();
0988: execInSeparateThreadLock = new Object();
0989:
0990: // Preload this class, to avoid possible strange problems that may happen in case of wire protocol errors, that in
0991: // turn may cause loading of this class, that in turn may invoke classLoadHook, etc.
0992: try {
0993: Class.forName("java.net.SocketException"); // NOI18N
0994: } catch (ClassNotFoundException ex) { /* Shouldn't happen */
0995: }
0996:
0997: // Preload this class, to avoid possible strange problems that happen during Entire App CPU profiling of tomcat,
0998: // where classLoadHook is invoked during processing GET_DEFINING_CLASSLOADER request
0999: try {
1000: Class.forName("java.util.AbstractList$Itr"); // NOI18N
1001: } catch (ClassNotFoundException ex) { /* Shouldn't happen */
1002: }
1003:
1004: ThreadInfo.clearProfilerServerThreads();
1005: }
1006:
1007: /**
1008: * Called after the connection with the tool is established, i.e. we know that we are connected, in which mode
1009: * (attached or called directly) and whether it's local or remote connection.
1010: */
1011: private static void initSupportingFunctionality(
1012: boolean inAttachedMode, boolean remoteProfiling) {
1013: status = new ProfilingSessionStatus();
1014: status.runningInAttachedMode = inAttachedMode;
1015: status.remoteProfiling = remoteProfiling;
1016: status.targetJDKVersionString = Platform.getJDKVersionString();
1017:
1018: Monitors.initialize(); // Initialize before initProfilerInterface to get monitor thread(s) recorded as system thread(s)
1019: // Also initialize before initProfilerInterface, same purpose
1020:
1021: profilerServer.initSeparateCmdExecutionThread();
1022: ThreadInfo.addProfilerServerThread(shutdownWaitThread);
1023: // Profiler interface initialization includes recording profiler's own threads (all currently running threads minus the
1024: // current thread, since it will become the target app's main thread).
1025: ProfilerInterface.initProfilerInterface(status,
1026: inAttachedMode ? profilerServer : Thread
1027: .currentThread());
1028:
1029: // This is to preload some classes that can otherwise be loaded at inappropriate time and cause class load hook firing.
1030: if (remoteProfiling) {
1031: ClassBytesLoader.preloadClasses();
1032: }
1033:
1034: profilerInterfaceInitialized = true;
1035: }
1036:
1037: private static void pressEnterToShutDown() {
1038: // Make sure any excessive previous input doesn't cause us to shut down immediately
1039: try {
1040: while (System.in.available() > 0) {
1041: System.in.read();
1042: }
1043: } catch (IOException ex) {
1044: // ignore
1045: }
1046:
1047: System.out.println(ENTER_TO_SHUTDOWN_MSG); // NOI18N
1048:
1049: try {
1050: System.in.read();
1051: } catch (IOException ex) {
1052: // ignore
1053: }
1054: }
1055:
1056: private static void runTargetApp(String mainClassName,
1057: String[] mainArgs) {
1058: Class targetMainClass = null;
1059:
1060: try {
1061: targetMainClass = ClassLoader.getSystemClassLoader()
1062: .loadClass(mainClassName);
1063: } catch (ClassNotFoundException ex) {
1064: startupException = ex;
1065: System.err.println(ex);
1066:
1067: return;
1068: }
1069:
1070: // For the reasons I don't quite understand, if the main class is not public, then somewhere (when we attempt to invoke the
1071: // main method using reflection?) we get the following: "java.lang.IllegalAccessException: Class org.netbeans.lib.profiler.server.ProfilerServer
1072: // can not access a member of class Test with modifiers "public static"". Thus we have to run the below preemptive check. Hope this is not
1073: // a problem for the majority of our users...
1074: if (!Modifier.isPublic(targetMainClass.getModifiers())) {
1075: startupException = new IllegalAccessException(MessageFormat
1076: .format(MAIN_CLASS_NOT_PUBLIC_MSG,
1077: new Object[] { targetMainClass })); // NOI18N
1078: System.err.println(startupException);
1079:
1080: return;
1081: }
1082:
1083: Method targetMainMethod = null;
1084: Class[] params = new Class[] { String[].class };
1085:
1086: try {
1087: targetMainMethod = targetMainClass.getDeclaredMethod(
1088: "main", params); // NOI18N
1089: } catch (NoSuchMethodException ex) {
1090: startupException = ex;
1091: System.err.println(ex);
1092:
1093: return;
1094: }
1095:
1096: // Check for correct method modifiers, to (hopefully) avoid IllegalAccessException and IllegalArgumentException
1097: int mod = targetMainMethod.getModifiers();
1098:
1099: if (!(Modifier.isPublic(mod) && Modifier.isStatic(mod))
1100: || Modifier.isAbstract(mod)
1101: || Modifier.isInterface(mod)) {
1102: startupException = new IllegalAccessException(MessageFormat
1103: .format(INCORRECT_MAIN_MODIFIERS_MSG,
1104: new Object[] { targetMainClass })); // NOI18N
1105: System.err.println(startupException);
1106:
1107: return;
1108: }
1109:
1110: // We hope after our checks the only exceptions that can be thrown by the target app are those that it generates for
1111: // natural reasons, and which we should not report as "failed to start the application"
1112: status.targetAppRunning = true;
1113:
1114: synchronized (targetAppRunningLock) {
1115: targetAppRunningLock.notify();
1116: }
1117:
1118: long startTime = Timers.getCurrentTimeInCounts();
1119:
1120: try {
1121: targetMainMethod.invoke(targetMainClass,
1122: new Object[] { mainArgs });
1123: } catch (IllegalAccessException e1) {
1124: startupException = e1;
1125: System.err.println(e1);
1126: } catch (IllegalArgumentException e2) {
1127: startupException = e2;
1128: System.err.println(e2);
1129: } catch (InvocationTargetException e3) {
1130: Throwable cause = e3.getCause();
1131:
1132: if (cause != null) {
1133: cause.printStackTrace(System.err);
1134: } else { // Can this ever happen?
1135: internalError("Target application threw a null exception?"); // NOI18N
1136: }
1137: } catch (Throwable ex) {
1138: ProfilerInterface.disableProfilerHooks();
1139: internalError(MessageFormat.format(
1140: UNEXPECTED_EXCEPTION_MSG, new Object[] { ex }),
1141: false); // NOI18N
1142: ex.printStackTrace(System.err);
1143: } finally {
1144: int elapsedTime = (int) (((Timers.getCurrentTimeInCounts() - startTime) * 1000) / Timers
1145: .getNoOfCountsInSecond());
1146: System.out.println(MessageFormat.format(ELAPSED_TIME_MSG,
1147: new Object[] { "" + elapsedTime })); // NOI18N
1148: }
1149: }
1150:
1151: private static void waitForShutdownOK() {
1152: synchronized (shutdownLock) {
1153: while (!shutdownOK && !Thread.interrupted()) {
1154: try {
1155: shutdownLock.wait(500);
1156: } catch (InterruptedException e) {
1157: }
1158:
1159: Thread.yield();
1160: }
1161:
1162: if (shutdownOK) {
1163: return;
1164: }
1165: }
1166:
1167: System.err
1168: .println("ProfilerServer hasn't shut down cleanly. Terminated."); // NOI18N
1169:
1170: // while (true) {
1171: // if (shutdownOK) {
1172: // return;
1173: // }
1174: // delay(100);
1175: // }
1176: }
1177:
1178: private int getAgentId() {
1179: if (agentId == -1) {
1180: String id = System.getProperty("nbprofiler.agentid"); // NOI18N
1181:
1182: if (id != null) {
1183: try {
1184: agentId = Integer.parseInt(id);
1185: } catch (NumberFormatException e) {
1186: System.err.println(MessageFormat
1187: .format(INCORRECT_AGENT_ID_MSG,
1188: new Object[] { id })); // NOI18N
1189: // ignore, the agentId will be generated randomly
1190: }
1191: }
1192:
1193: if (agentId == -1) {
1194: agentId = (int) (Math.random() * (float) Integer.MAX_VALUE);
1195: }
1196: }
1197:
1198: return agentId;
1199: }
1200:
1201: private static void setLastResponse(Response r) {
1202: synchronized (responseLock) {
1203: lastResponse = r;
1204:
1205: try {
1206: responseLock.notify();
1207: } catch (IllegalMonitorStateException ex) {
1208: internalError("IllegalMonitorState in ProfilerServer.setLastResponse()"); // NOI18N
1209: }
1210: }
1211: }
1212:
1213: private static String getLocalizedJFluidServerJar(
1214: String jfluidServerDir) {
1215: String localizedJFluidServerJar = null;
1216:
1217: // normalize provided directory to use forward slashes with slash at the end of path
1218: String baseDir = jfluidServerDir.replace('\\', '/'); // NOI18N
1219:
1220: if (!baseDir.endsWith("/")) { // NOI18N
1221: baseDir = baseDir + "/"; // NOI18N
1222: }
1223:
1224: // check if directory exists
1225: File baseDirF = new File(baseDir);
1226:
1227: if (!baseDirF.exists() || !baseDirF.isDirectory()) {
1228: return null;
1229: }
1230:
1231: // check if locale directory exists
1232: String localeDir = baseDir + "locale/"; // NOI18N
1233: File localeDirF = new File(localeDir);
1234:
1235: if (localeDirF.exists() && localeDirF.isDirectory()) {
1236: // locale directory found, try to find jar inside
1237: localizedJFluidServerJar = getLocalizedJFluidServerJarInDir(localeDir);
1238:
1239: if (localizedJFluidServerJar != null) {
1240: return localizedJFluidServerJar;
1241: }
1242: }
1243:
1244: // locale directory doesn't exist or jar not found in it, try to find jar directly in jfluid dir
1245: localizedJFluidServerJar = getLocalizedJFluidServerJarInDir(baseDir);
1246:
1247: return localizedJFluidServerJar;
1248: }
1249:
1250: private static String getLocalizedJFluidServerJarInDir(
1251: String jfluidServerLocaleDir) {
1252: LocaleIterator localeIterator = new LocaleIterator(Locale
1253: .getDefault());
1254: String jarFile;
1255: File jarFileF;
1256:
1257: while (localeIterator.hasNext()) {
1258: jarFile = jfluidServerLocaleDir + "jfluid-server"
1259: + localeIterator.next() + ".jar"; // NOI18N
1260: jarFileF = new File(jarFile);
1261:
1262: if (jarFileF.exists() && jarFileF.isFile()) {
1263: return jarFile;
1264: }
1265: }
1266:
1267: return null;
1268: }
1269:
1270: // Does not support branding!
1271: private static ResourceBundle getProfilerServerResourceBundle(
1272: String jfluidPath) {
1273: ResourceBundle bundle = null;
1274:
1275: if (jfluidPath == null) {
1276: throw new RuntimeException(
1277: "ProfilerServer: Unable to initialize ResourceBundle for ProfilerServer, " // NOI18N
1278: + "cannot find path to Profiler libraries" // NOI18N
1279: );
1280: }
1281:
1282: String jfluidServerJar = getLocalizedJFluidServerJar(jfluidPath);
1283:
1284: if (jfluidServerJar == null) {
1285: throw new RuntimeException(
1286: "ProfilerServer: Unable to initialize ResourceBundle for ProfilerServer, " // NOI18N
1287: + "cannot find localized jfluid-server.jar" // NOI18N
1288: );
1289: }
1290:
1291: try {
1292: if (!jfluidServerJar.startsWith("/")) {
1293: jfluidServerJar = "/" + jfluidServerJar; // NOI18N
1294: }
1295:
1296: String bundleJarURLPath = "jar:file:" + jfluidServerJar
1297: + "!/"; // NOI18N
1298: URLClassLoader loader = new URLClassLoader(
1299: new URL[] { new URL(bundleJarURLPath) });
1300: bundle = ResourceBundle.getBundle(
1301: "org.netbeans.lib.profiler.server.Bundle", Locale
1302: .getDefault(), loader); // NOI18N
1303: } catch (Exception e2) {
1304: throw new RuntimeException(
1305: "ProfilerServer: Unable to initialize ResourceBundle for ProfilerServer\n"
1306: + e2.getMessage() // NOI18N
1307: );
1308: }
1309:
1310: if (bundle == null) {
1311: throw new RuntimeException(
1312: "ProfilerServer: Unable to initialize ResourceBundle for ProfilerServer" // NOI18N
1313: );
1314: }
1315:
1316: return bundle;
1317: }
1318:
1319: private synchronized void closeConnection() {
1320: connectionOpen = false;
1321: status.targetAppRunning = false;
1322: removeInfoFile();
1323:
1324: try {
1325: socketOut.close();
1326: socketIn.close();
1327: clientSocket.close();
1328: serverSocket.close();
1329: } catch (IOException ex) {
1330: }
1331:
1332: if (status.runningInAttachedMode) {
1333: System.out.println(CONNECTION_CLOSED_MSG);
1334: }
1335:
1336: preemptExit = false;
1337: }
1338:
1339: private boolean connectToClient() {
1340: try {
1341: if (serverTimeout == 0) {
1342: System.out
1343: .println(MessageFormat
1344: .format(
1345: WAITING_ON_PORT_MSG,
1346: new Object[] {
1347: "" + serverPort, // NOI18N
1348: ""
1349: + CommonConstants.CURRENT_AGENT_VERSION // NOI18N
1350: }));
1351: } else {
1352: System.out
1353: .println(MessageFormat
1354: .format(
1355: WAITING_ON_PORT_TIMEOUT_MSG,
1356: new Object[] {
1357: "" + serverPort, // NOI18N
1358: "" + serverTimeout, // NOI18N
1359: ""
1360: + CommonConstants.CURRENT_AGENT_VERSION } // NOI18N
1361: )); // NOI18N
1362: }
1363:
1364: serverSocket = new ServerSocket(serverPort);
1365: serverSocket.setSoTimeout(serverTimeout * 1000); // serverTimeout is in seconds
1366: createInfoFile();
1367: clientSocket = serverSocket.accept();
1368: clientSocket.setTcpNoDelay(true); // Necessary at least on Solaris to avoid delays in e.g. readInt() etc.
1369: socketIn = new ObjectInputStream(clientSocket
1370: .getInputStream());
1371: socketOut = new ObjectOutputStream(clientSocket
1372: .getOutputStream());
1373: wireIO = new WireIO(socketOut, socketIn);
1374: connectionOpen = true;
1375:
1376: return true;
1377: } catch (SocketTimeoutException ex) {
1378: System.err.println(CONNECTION_TIMEOUT_MSG); // NOI18N
1379: connectionFailed = true;
1380: } catch (IOException ex) {
1381: System.err.println(MessageFormat.format(
1382: CONNECTION_EXCEPTION_MSG, new Object[] { ex })); // NOI18N
1383: connectionFailed = true;
1384: } finally {
1385: //removeInfoFile ();
1386: }
1387:
1388: return false;
1389: }
1390:
1391: private void createInfoFile() {
1392: BufferedOutputStream bos = null;
1393:
1394: try {
1395: File f = getInfoFile(serverPort);
1396: f.createNewFile();
1397: f.deleteOnExit();
1398:
1399: Properties props = new Properties();
1400: props.setProperty("dynamic", Boolean.toString(dynamic)); // NOI18N
1401: props.setProperty("working.dir", System
1402: .getProperty("user.dir")); // NOI18N
1403: props.setProperty("agent.id", Integer
1404: .toString(getAgentId())); // NOI18N
1405: props.setProperty("java.version", System
1406: .getProperty("java.version")); // NOI18N
1407:
1408: FileOutputStream fos = new FileOutputStream(f);
1409: bos = new BufferedOutputStream(fos);
1410:
1411: props.store(bos, ""); // NOI18N
1412:
1413: bos.close();
1414: } catch (IOException e) {
1415: System.err.println(MessageFormat.format(AGENT_ERROR_MSG,
1416: new Object[] { e.getMessage() })); // NOI18N
1417: } finally {
1418: if (bos != null) {
1419: try {
1420: bos.close();
1421: } catch (IOException e) {
1422: e.printStackTrace();
1423: }
1424: }
1425: }
1426: }
1427:
1428: /**
1429: * Some of the commands need to be executed in a separate thread, because they result in the server sending something
1430: * to the client and awaiting its response. The response, in turn, can only be picked up by the single JFluid communication
1431: * thread. So we execute these commands in a separate thread to allow the main communication thread to return immediately,
1432: * and be ready to process client's response.
1433: */
1434: private void executeInSeparateThread(int opCode) {
1435: synchronized (execInSeparateThreadLock) {
1436: execInSeparateThreadOpCode = opCode;
1437:
1438: try {
1439: execInSeparateThreadLock.notify();
1440: } catch (IllegalMonitorStateException ex) {
1441: System.err.println(THREAD_EXCEPTION_MSG); // NOI18N
1442: }
1443: }
1444: }
1445:
1446: //---------------------------------------------------------------------------------------
1447: // Command/response handling
1448: //---------------------------------------------------------------------------------------
1449: private void handleClientCommand(Command cmd) {
1450: //System.out.println(">>> Got command " + cmd);
1451: if (cmd.getType() == Command.START_TARGET_APP) {
1452: if (status.runningInAttachedMode) {
1453: // This is a special case - the user has chosen "Attach on startup" and then "resume application without instrumentation"
1454: status.targetAppRunning = true;
1455: sendSimpleResponseToClient(true, null);
1456:
1457: return;
1458: }
1459:
1460: // Start target app is handled by a separate thread, since we want to return to the client a synchronous response telling
1461: // whether or not the target app was started successfully. To get an answer to this question, we have to wait until the main
1462: // class is loaded, its main method is found, etc. Only after that the targetAppRunningLock.notify() is called. Until then
1463: // this thread remains blocked. However, if instrumentation root method == main method, class load hook is invoked immediately
1464: // when the main class is loaded. Class load hook, in turn, sends a RootClassLoaded command to the server and waits for the
1465: // response. But responses are read by the same thread that calls handleClientCommand(). So if we do the below operations in
1466: // the same thread, we deadlock - so, a separate thread is needed to allow the main listener thread to handle incoming
1467: // commands/responses immediately.
1468: class MyThread extends Thread {
1469: MyThread() {
1470: ThreadInfo.addProfilerServerThread(this );
1471: this .setName(PROFILER_SPECIAL_EXEC_THREAD_NAME
1472: + " 4"); // NOI18N
1473: }
1474:
1475: public void run() {
1476: synchronized (targetAppRunningLock) {
1477: startTargetApp = true;
1478:
1479: try {
1480: targetAppRunningLock.wait();
1481: } catch (InterruptedException ex) {
1482: internalError("START_TARGET_APP");
1483: } // NOI18N
1484: }
1485:
1486: if (startupException != null) {
1487: sendSimpleResponseToClient(false,
1488: startupException.toString());
1489: } else {
1490: sendSimpleResponseToClient(true, null);
1491: }
1492:
1493: ThreadInfo.removeProfilerServerThread(this );
1494: }
1495: }
1496: new MyThread().start();
1497:
1498: return;
1499: }
1500:
1501: switch (cmd.getType()) {
1502: case Command.GET_MONITORED_NUMBERS:
1503: sendComplexResponseToClient(Monitors.getMonitoredNumbers());
1504:
1505: break;
1506: case Command.INITIATE_INSTRUMENTATION:
1507:
1508: // Bugfix 69645: Take snapshot is not enabled after modifying profiling from CPU to memory
1509: // http://profiler.netbeans.org/issues/show_bug.cgi?id=69645
1510: synchronized (resultsNotifiedLock) {
1511: resultsNotified = false;
1512: }
1513:
1514: try {
1515: ProfilerInterface.initiateInstrumentation(
1516: (InitiateInstrumentationCommand) cmd,
1517: status.targetAppRunning);
1518: sendSimpleResponseToClient(true, null);
1519: } catch (Exception ex) {
1520: sendSimpleResponseToClient(false, ex.getMessage());
1521: }
1522:
1523: break;
1524: case Command.INSTRUMENT_METHOD_GROUP:
1525: class InstrumentMethodGroupThread extends Thread {
1526: final InstrumentMethodGroupCommand methodGroupCmd;
1527: String exceptionString;
1528:
1529: InstrumentMethodGroupThread(
1530: InstrumentMethodGroupCommand cmd) {
1531: ThreadInfo.addProfilerServerThread(this );
1532: setName(PROFILER_SPECIAL_EXEC_THREAD_NAME + " 8"); // NOI18N
1533: methodGroupCmd = cmd;
1534: }
1535:
1536: public void run() {
1537: try {
1538: ProfilerInterface
1539: .instrumentMethods(methodGroupCmd);
1540: } catch (Exception ex) {
1541: exceptionString = ex.getLocalizedMessage();
1542: }
1543:
1544: ThreadInfo.removeProfilerServerThread(this );
1545: }
1546: }
1547:
1548: InstrumentMethodGroupThread instrumentMethodGroupThread = new InstrumentMethodGroupThread(
1549: (InstrumentMethodGroupCommand) cmd);
1550: instrumentMethodGroupThread.start();
1551:
1552: while (instrumentMethodGroupThread.isAlive()) {
1553: delay(2000);
1554: sendSimpleCmdToClient(Command.STILL_ALIVE);
1555: }
1556:
1557: if (instrumentMethodGroupThread.exceptionString != null) {
1558: sendSimpleResponseToClient(false,
1559: instrumentMethodGroupThread.exceptionString);
1560: } else {
1561: sendSimpleResponseToClient(true, null);
1562: }
1563:
1564: break;
1565: case Command.CHECK_CONNECTION:
1566: sendSimpleResponseToClient(true, null);
1567:
1568: break;
1569: case Command.SET_CHANGEABLE_INSTR_PARAMS:
1570:
1571: SetChangeableInstrParamsCommand scipCmd = (SetChangeableInstrParamsCommand) cmd;
1572: ProfilerRuntimeCPU.setNProfiledThreadsLimit(scipCmd
1573: .getNProfiledThreadsLimit());
1574: ProfilerRuntimeCPUSampledInstr.setSamplingInterval(scipCmd
1575: .getSamplingInterval());
1576: ProfilerRuntimeMemory.setSamplingInterval((short) scipCmd
1577: .getObjAllocStackSamplingInterval());
1578: ProfilerRuntimeMemory.setSamplingDepth(scipCmd
1579: .getObjAllocStackSamplingDepth());
1580: ProfilerRuntimeObjLiveness.setRunGCOnGetResults(scipCmd
1581: .getRunGCOnGetResultsInMemoryProfiling());
1582: Classes.setWaitTrackingEnabled(scipCmd
1583: .getWaitTrackingEnabled());
1584: Classes.setSleepTrackingEnabled(scipCmd
1585: .getSleepTrackingEnabled());
1586: sendSimpleResponseToClient(true, null);
1587:
1588: break;
1589: case Command.SET_UNCHANGEABLE_INSTR_PARAMS:
1590:
1591: SetUnchangeableInstrParamsCommand sucipCmd = (SetUnchangeableInstrParamsCommand) cmd;
1592: ProfilerRuntimeCPU.setTimerTypes(sucipCmd
1593: .getAbsoluteTimerOn(), sucipCmd
1594: .getThreadCPUTimerOn());
1595: status.instrScheme = sucipCmd.getInstrScheme();
1596: ProfilerRuntimeCPUCodeRegion.setCPUResBufSize(sucipCmd
1597: .getCodeRegionCPUResBufSize());
1598: sendSimpleResponseToClient(true, null);
1599:
1600: break;
1601: case Command.CPU_RESULTS_EXIST:
1602: sendSimpleResponseToClient(ProfilerInterface
1603: .cpuResultsExist(), null);
1604:
1605: break;
1606: case Command.DUMP_EXISTING_RESULTS:
1607: case Command.DUMP_EXISTING_RESULTS_LIVE:
1608: // We have to execute the dump in a separate thread to make this call (handleClientCommand()) return immediately.
1609: // Otherwise, it would not allow the server to receive a response from the client, that the client sends when it
1610: // processes the dumped results. Generally, all commands that may call ProfilerRuntime.dumpEventBuffer() should be
1611: // executed in a separate thread.
1612: executeInSeparateThread(cmd.getType());
1613:
1614: break;
1615: case Command.GET_CODE_REGION_CPU_RESULTS:
1616: sendComplexResponseToClient(ProfilerInterface
1617: .getCodeRegionCPUResults());
1618:
1619: break;
1620: case Command.GET_OBJECT_ALLOCATION_RESULTS:
1621: sendComplexResponseToClient(ProfilerInterface
1622: .getObjectAllocationResults());
1623:
1624: break;
1625: case Command.GET_METHOD_NAMES_FOR_JMETHOD_IDS:
1626:
1627: GetMethodNamesForJMethodIdsCommand gmnCmd = (GetMethodNamesForJMethodIdsCommand) cmd;
1628: sendComplexResponseToClient(ProfilerInterface
1629: .getMethodNamesForJMethodIds(gmnCmd.getMethodIds()));
1630:
1631: break;
1632: case Command.RESET_PROFILER_COLLECTORS:
1633:
1634: synchronized (resultsNotifiedLock) {
1635: resultsNotified = false;
1636: }
1637:
1638: // Since the resetProfilerCollectors() eventually invokes the dump results method, which in turn sends a command to the client
1639: // and awaits response, we have to execute it in a separate thread. See comments in DUMP_EXISTING_RESULTS above.
1640: executeInSeparateThread(cmd.getType());
1641:
1642: break;
1643: case Command.DEACTIVATE_INJECTED_CODE:
1644: ProfilerInterface.deactivateInjectedCode();
1645: sendSimpleResponseToClient(true, null);
1646:
1647: break;
1648: case Command.GET_THREAD_LIVENESS_STATUS:
1649: sendComplexResponseToClient(ProfilerInterface
1650: .getCurrentThreadLivenessStatus());
1651:
1652: break;
1653: case Command.SUSPEND_TARGET_APP:
1654: ProfilerInterface.suspendTargetApp();
1655: sendSimpleResponseToClient(true, null);
1656:
1657: break;
1658: case Command.RESUME_TARGET_APP:
1659: ProfilerInterface.resumeTargetApp();
1660: sendSimpleResponseToClient(true, null);
1661:
1662: break;
1663: case Command.TERMINATE_TARGET_JVM:
1664:
1665: if (ProfilerInterface.getCurrentInstrType() != INSTR_NONE) {
1666: ProfilerInterface.deactivateInjectedCode();
1667: }
1668:
1669: sendSimpleResponseToClient(true, null);
1670: closeConnection();
1671: preemptExit = false;
1672: System.exit(-1);
1673:
1674: break;
1675: case Command.SHUTDOWN_OK:
1676: setShutdownOK();
1677:
1678: break;
1679: case Command.INSTRUMENT_REFLECTION:
1680: ProfilerInterface.setInstrumentReflection(true);
1681: sendSimpleResponseToClient(true, null);
1682:
1683: break;
1684: case Command.DEINSTRUMENT_REFLECTION:
1685: ProfilerInterface.setInstrumentReflection(false);
1686: sendSimpleResponseToClient(true, null);
1687:
1688: break;
1689: case Command.RUN_GC:
1690: GC.runGC();
1691: sendSimpleResponseToClient(true, null);
1692:
1693: break;
1694: case Command.GET_DEFINING_CLASS_LOADER: {
1695: GetDefiningClassLoaderCommand gdclCmd = (GetDefiningClassLoaderCommand) cmd;
1696: int loaderId = ClassLoaderManager
1697: .getDefiningLoaderForClass(gdclCmd.getClassName(),
1698: gdclCmd.getClassLoaderId());
1699: DefiningLoaderResponse resp = new DefiningLoaderResponse(
1700: loaderId);
1701: sendComplexResponseToClient(resp);
1702:
1703: break;
1704: }
1705: case Command.GET_VM_PROPERTIES: {
1706: status.jvmArguments = Threads.getJVMArguments();
1707: status.javaCommand = Threads.getJavaCommand();
1708:
1709: VMPropertiesResponse resp = new VMPropertiesResponse(System
1710: .getProperty("java.version"), // NOI18N
1711: System.getProperty("java.class.path"), // NOI18N
1712: System.getProperty("java.ext.dirs"), // NOI18N
1713: System.getProperty("sun.boot.class.path"), // NOI18N
1714: System.getProperty("user.dir"), // NOI18N
1715: status.jvmArguments, status.javaCommand, System
1716: .getProperty("os.name"), // NOI18N
1717: Runtime.getRuntime().maxMemory(), System
1718: .currentTimeMillis(), Timers
1719: .getCurrentTimeInCounts(), getAgentId() // NOI18N
1720: ); // NOI18N
1721: sendComplexResponseToClient(resp);
1722:
1723: break;
1724: }
1725: case Command.GET_STORED_CALIBRATION_DATA: { // called in the beginning of remote CPU profiling
1726:
1727: int ret = CalibrationDataFileIO
1728: .readSavedCalibrationData(status);
1729:
1730: if (ret == 0) {
1731: CalibrationDataResponse resp = new CalibrationDataResponse(
1732: status.methodEntryExitCallTime,
1733: status.methodEntryExitInnerTime,
1734: status.methodEntryExitOuterTime,
1735: status.timerCountsInSecond);
1736: profilerServer.sendComplexResponseToClient(resp);
1737: } else {
1738: sendSimpleResponseToClient(false, CalibrationDataFileIO
1739: .getErrorMessage());
1740: }
1741:
1742: break;
1743: }
1744: case Command.RUN_CALIBRATION_AND_GET_DATA: {
1745: ProfilerCalibrator.init(status);
1746: ProfilerCalibrator.measureBCIOverhead(false);
1747:
1748: CalibrationDataResponse resp = new CalibrationDataResponse(
1749: status.methodEntryExitCallTime,
1750: status.methodEntryExitInnerTime,
1751: status.methodEntryExitOuterTime,
1752: status.timerCountsInSecond);
1753: profilerServer.sendComplexResponseToClient(resp);
1754:
1755: break;
1756: }
1757: case Command.GET_INTERNAL_STATS:
1758: ProfilerCalibrator.init(status);
1759: sendComplexResponseToClient(ProfilerCalibrator
1760: .getInternalStats());
1761:
1762: break;
1763: case Command.DETACH:
1764: // Just in case, normally should be deactivated and cleaned up by client
1765: ProfilerInterface.deactivateInjectedCode();
1766: ProfilerInterface.disableProfilerHooks();
1767: ProfilerInterface.clearProfilerDataStructures();
1768:
1769: stopSeparateCmdExecutionThread();
1770: Monitors.shutdown();
1771: ThreadInfo.clearProfilerServerThreads();
1772: detachCommandReceived = true;
1773: sendSimpleResponseToClient(true, null);
1774:
1775: break;
1776: case Command.TAKE_HEAP_DUMP:
1777:
1778: TakeHeapDumpCommand dumpCmd = (TakeHeapDumpCommand) cmd;
1779: String error = HeapDump.takeHeapDump(Platform
1780: .getJDKVersionNumber() == Platform.JDK_15, dumpCmd
1781: .getOutputFile());
1782:
1783: sendSimpleResponseToClient(error == null, error);
1784:
1785: break;
1786: }
1787: }
1788:
1789: private void handleIOExceptionOnSend(IOException ex) {
1790: System.err.println(MessageFormat.format(RESPONSE_EXCEPTION_MSG,
1791: new Object[] { ex })); // NOI18N
1792: ex.printStackTrace(System.err);
1793: closeConnection();
1794: }
1795:
1796: private void initSeparateCmdExecutionThread() {
1797: separateCmdExecutionThread = new SeparateCmdExecutionThread();
1798: separateCmdExecutionThread.start();
1799: }
1800:
1801: private static void internalError(String message) {
1802: internalError(message, true);
1803: }
1804:
1805: private static void internalError(String message, boolean exit) {
1806: System.err.println("Profiler Engine Error: " + message); // NOI18N
1807:
1808: if (exit) {
1809: preemptExit = false;
1810: System.exit(-1);
1811: }
1812: }
1813:
1814: private void listenToClient() {
1815: while (connectionOpen && !detachCommandReceived) {
1816: try {
1817: Object o = wireIO.receiveCommandOrResponse();
1818:
1819: if (o == null) {
1820: System.err.println(CONNECTION_INTERRUPTED_MSG); // NOI18N
1821:
1822: break; // end of connection
1823: }
1824:
1825: //System.out.println(">>> Profiler Engine: received command or response " + o);
1826: if (o instanceof Command) {
1827: handleClientCommand((Command) o);
1828: } else {
1829: setLastResponse((Response) o);
1830: }
1831: } catch (IOException ex) {
1832: if (connectionOpen && !detachCommandReceived) { // It is not an asynchronous connection shutdown
1833: System.err
1834: .println(MessageFormat.format(
1835: COMMAND_EXCEPTION_MSG,
1836: new Object[] { ex })); // NOI18N
1837: }
1838:
1839: break;
1840: }
1841: }
1842:
1843: closeConnection();
1844: }
1845:
1846: private void removeInfoFile() {
1847: try {
1848: getInfoFile(serverPort).delete();
1849: } catch (IOException e) {
1850: System.err.println(MessageFormat.format(AGENT_ERROR_MSG,
1851: new Object[] { e.getMessage() })); // NOI18N
1852: }
1853: }
1854:
1855: private void stopSeparateCmdExecutionThread() {
1856: separateCmdExecutionThread.terminate();
1857:
1858: synchronized (execInSeparateThreadLock) {
1859: try {
1860: execInSeparateThreadLock.notify();
1861: } catch (IllegalMonitorStateException ex) {
1862: }
1863: }
1864: }
1865: }
|