001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: * The Original Software is NetBeans. The Initial Developer of the Original
026: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
027: * Microsystems, Inc. All Rights Reserved.
028: *
029: * If you wish your version of this file to be governed by only the CDDL
030: * or only the GPL Version 2, indicate your decision by adding
031: * "[Contributor] elects to include this software in this distribution
032: * under the [CDDL or GPL Version 2] license." If you do not indicate a
033: * single choice of license, a recipient has the option to distribute
034: * your version of this file under either the CDDL, the GPL Version 2 or
035: * to extend the choice of license to its licensees as provided above.
036: * However, if you add GPL Version 2 code and therefore, elected the GPL
037: * Version 2 license, then the option applies only if the new code is
038: * made subject to such option by the copyright holder.
039: */
040:
041: package org.netbeans.lib.profiler;
042:
043: import org.netbeans.lib.profiler.client.AppStatusHandler;
044: import org.netbeans.lib.profiler.client.ClientUtils;
045: import org.netbeans.lib.profiler.client.ProfilingPointsProcessor;
046: import org.netbeans.lib.profiler.global.CalibrationDataFileIO;
047: import org.netbeans.lib.profiler.global.CommonConstants;
048: import org.netbeans.lib.profiler.global.Platform;
049: import org.netbeans.lib.profiler.global.ProfilingSessionStatus;
050: import org.netbeans.lib.profiler.results.EventBufferProcessor;
051: import org.netbeans.lib.profiler.results.cpu.CPUCCTContainer;
052: import org.netbeans.lib.profiler.utils.MiscUtils;
053: import org.netbeans.lib.profiler.wireprotocol.AsyncMessageCommand;
054: import org.netbeans.lib.profiler.wireprotocol.Command;
055: import org.netbeans.lib.profiler.wireprotocol.InternalStatsResponse;
056: import java.io.File;
057: import java.io.IOException;
058: import java.text.MessageFormat;
059: import java.text.NumberFormat;
060: import java.util.ArrayList;
061: import java.util.ResourceBundle;
062: import java.util.Vector;
063:
064: /**
065: * Functionality for high-level control of the Target Application (TA) execution, plus some utility methods
066: * that seemed to fit best here.
067: *
068: * @author Tomas Hurka
069: * @author Misha Dmitriev
070: * @author Ian Formanek
071: */
072: public class TargetAppRunner implements CommonConstants {
073: //~ Static fields/initializers -----------------------------------------------------------------------------------------------
074:
075: // -----
076: // I18N String constants
077: private static final ResourceBundle messages = ResourceBundle
078: .getBundle("org.netbeans.lib.profiler.Bundle"); // NOI18N
079: private static final String CLASSPATH_SETTINGS_IGNORED_MSG = messages
080: .getString("TargetAppRunner_ClasspathSettingsIgnoredMsg"); // NOI18N
081: private static final String ERROR_STARTING_JVM_MSG = messages
082: .getString("TargetAppRunner_ErrorStartingJvmMsg"); // NOI18N
083: private static final String CALIBRATION_SUMMARY_SHORT_MSG = messages
084: .getString("TargetAppRunner_CalibrationSummaryShortMsg"); // NOI18N
085: private static final String CALIBRATION_SUMMARY_DETAILS_MSG = messages
086: .getString("TargetAppRunner_CalibrationSummaryDetailsMsg"); // NOI18N
087: private static final String FAILED_ESTABLISH_CONN_MSG = messages
088: .getString("TargetAppRunner_FailedEstablishConnMsg"); // NOI18N
089: private static final String UNEXPECTED_PROBLEM_STARTING_APP_MSG = messages
090: .getString("TargetAppRunner_UnexpectedProblemStartingAppMsg"); // NOI18N
091: private static final String JVM_TERMINATED_NOTRESPOND_STRING = messages
092: .getString("TargetAppRunner_JvmTerminatedNotRespondString"); // NOI18N
093: private static final String INTERNAL_PROBLEM_STRING = messages
094: .getString("TargetAppRunner_InternalProblemString"); // NOI18N
095: private static final String FAILED_START_APP_CAUSE_MSG = messages
096: .getString("TargetAppRunner_FailedStartAppCauseMsg"); // NOI18N
097: private static final String CALIBRATION_RESULTS_MSG = messages
098: .getString("TargetAppRunner_CalibrationResultsMsg"); // NOI18N
099: private static final String CALIBRATION_ERROR_MSG = messages
100: .getString("TargetAppRunner_CalibrationErrorMsg"); // NOI18N
101: private static final String INTERNAL_STATISTICS_ONLY_MSG = messages
102: .getString("TargetAppRunner_InternalStatisticsOnlyMsg"); // NOI18N
103: private static final String INSTR_METHODS_COUNT_MSG = messages
104: .getString("TargetAppRunner_InstrMethodsCountMsg"); // NOI18N
105: private static final String CLASSLOAD_FIRSTINV_COUNT_MSG = messages
106: .getString("TargetAppRunner_ClassLoadFirstInvCountMsg"); // NOI18N
107: private static final String NON_EMPTY_IMG_COUNT_MSG = messages
108: .getString("TargetAppRunner_NonEmptyImgCountMsg"); // NOI18N
109: private static final String EMPTY_IMG_COUNT_MSG = messages
110: .getString("TargetAppRunner_EmptyImgCountMsg"); // NOI18N
111: private static final String SINGLE_IMG_COUNT_MSG = messages
112: .getString("TargetAppRunner_SingleImgCountMsg"); // NOI18N
113: private static final String AVG_METHOD_TIME_MSG = messages
114: .getString("TargetAppRunner_AvgMethodTimeMsg"); // NOI18N
115: private static final String MIN_METHOD_TIME_MSG = messages
116: .getString("TargetAppRunner_MinMethodTimeMsg"); // NOI18N
117: private static final String MAX_METHOD_TIME_MSG = messages
118: .getString("TargetAppRunner_MaxMethodTimeMsg"); // NOI18N
119: private static final String TOTAL_RUN_TIME_MSG = messages
120: .getString("TargetAppRunner_TotalRunTimeMsg"); // NOI18N
121: private static final String INJ_INSTR_TIME_MSG = messages
122: .getString("TargetAppRunner_InjInstrTimeMsg"); // NOI18N
123: private static final String TOTAL_INSTR_HOTSWAP_TIME_MSG = messages
124: .getString("TargetAppRunner_TotalInstrHotSwapTimeMsg"); // NOI18N
125: private static final String BYTECODE_COMM_TIME_MSG = messages
126: .getString("TargetAppRunner_ByteCodeCommTimeMsg"); // NOI18N
127: private static final String CLIENT_BYTECODE_TIME_MSG = messages
128: .getString("TargetAppRunner_ClientByteCodeTimeMsg"); // NOI18N
129: private static final String CLIENT_DISK_PROCESS_MSG = messages
130: .getString("TargetAppRunner_ClientDiskProcessTimeMsg"); // NOI18N
131: private static final String CLIENT_RESULTS_PROCESS_MSG = messages
132: .getString("TargetAppRunner_ClientResultsProcessTimeMsg"); // NOI18N
133: private static final String PERFORMING_CALIBRATION_MSG = messages
134: .getString("TargetAppRunner_PerformingCalibrationMsg"); // NOI18N
135: // -----
136: private static final boolean DEBUG = System
137: .getProperty("org.netbeans.lib.profiler.TargetAppRunner") != null; // NOI18N
138: private static TargetAppRunner defaultTAR; // Ok only while we don't have multiple profiling sessions
139: private static final int EVENT_STARTED = 0;
140: private static final int EVENT_STOPPED = 1;
141: private static final int EVENT_SUSPENDED = 2;
142: private static final int EVENT_RESUMED = 3;
143: private static final int EVENT_ATTACHED = 4;
144: private static final int EVENT_TERMINATED = 5;
145: private static final int EVENT_DETACHED = 6;
146:
147: //~ Instance fields ----------------------------------------------------------------------------------------------------------
148:
149: // Required for dialog shown during calibration
150: private AppStatusHandler.AsyncDialog waitDialog;
151: private AppStatusHandler appStatusHandler;
152: private Process runningAppProcess;
153: private ProfilerClient profilerClient;
154: private ProfilerEngineSettings settings;
155: private ProfilingPointsProcessor profilingPointProcessor;
156: private ProfilingSessionStatus status;
157: private Vector listeners = new Vector();
158: private boolean targetAppIsSuspended;
159:
160: //~ Constructors -------------------------------------------------------------------------------------------------------------
161:
162: public TargetAppRunner(ProfilerEngineSettings settings,
163: AppStatusHandler ash, ProfilingPointsProcessor ppp) {
164: this .settings = settings;
165: status = new ProfilingSessionStatus();
166: appStatusHandler = ash;
167: profilingPointProcessor = ppp;
168:
169: profilerClient = new ProfilerClient(settings, status,
170: appStatusHandler,
171: new AppStatusHandler.ServerCommandHandler() {
172: public void handleServerCommand(Command cmd) {
173: if (cmd != null) {
174: if (cmd.getType() == Command.MESSAGE) {
175: AsyncMessageCommand msg = (AsyncMessageCommand) cmd;
176:
177: if (msg.isPositive()) {
178: appStatusHandler
179: .displayNotification(msg
180: .getMessage());
181: } else {
182: appStatusHandler.displayError(msg
183: .getMessage());
184:
185: //profilerClient.setCurrentInstrType(INSTR_NONE);
186: // It looks like it often makes more sense to ignore the problem and get at least some info...
187: }
188: } else if (cmd.getType() == Command.RESULTS_AVAILABLE) {
189: appStatusHandler.resultsAvailable();
190: } else if (cmd.getType() == Command.TAKE_SNAPSHOT) {
191: appStatusHandler.takeSnapshot();
192: }
193: }
194: }
195: });
196: defaultTAR = this ;
197: }
198:
199: //~ Methods ------------------------------------------------------------------------------------------------------------------
200:
201: public static TargetAppRunner getDefault() {
202: return defaultTAR;
203: }
204:
205: public AppStatusHandler getAppStatusHandler() {
206: return appStatusHandler;
207: }
208:
209: public String getInternalStats()
210: throws ClientUtils.TargetAppOrVMTerminated {
211: InternalStatsResponse stats = (status.savedInternalStats != null) ? status.savedInternalStats
212: : profilerClient.getInternalStats();
213:
214: return getInternalStatsText(stats);
215: }
216:
217: public ProfilerClient getProfilerClient() {
218: return profilerClient;
219: }
220:
221: public ProfilerEngineSettings getProfilerEngineSettings() {
222: return settings;
223: }
224:
225: public ProfilingPointsProcessor getProfilingPointsProcessor() {
226: return profilingPointProcessor;
227: }
228:
229: public ProfilingSessionStatus getProfilingSessionStatus() {
230: return status;
231: }
232:
233: public Process getRunningAppProcess() {
234: return runningAppProcess;
235: }
236:
237: public void addProfilingEventListener(
238: ProfilingEventListener profilingEventListener) {
239: listeners.add(profilingEventListener);
240: }
241:
242: /**
243: * Attaches to a running application. It is expected that prepareForAttach method is called before this one
244: * to prepare the target app environment for attaching.
245: *
246: * @param tvmDir The working directory for the target application
247: * @throws IOException in case sending signal to target app failed
248: */
249: public boolean attachToTargetJVM(File tvmDir) throws IOException {
250: if (connectToStartedVMAndStartTA(1, false)) {
251: status.runningInAttachedMode = true;
252: notifyListeners(EVENT_ATTACHED);
253:
254: return true;
255: } else {
256: return false;
257: }
258: }
259:
260: public boolean attachToTargetVM() {
261: if (connectToStartedVMAndStartTA(1, false)) {
262: status.runningInAttachedMode = true;
263: notifyListeners(EVENT_ATTACHED);
264:
265: return true;
266: } else {
267: return false;
268: }
269: }
270:
271: /**
272: * Attach to the started and waiting target JVM using the "attach on startup" method
273: */
274: public boolean attachToTargetVMOnStartup() {
275: if (connectToStartedVMAndStartTA(2, false)) {
276: status.runningInAttachedMode = true;
277: notifyListeners(EVENT_ATTACHED);
278:
279: return true;
280: } else {
281: return false;
282: }
283: }
284:
285: /**
286: * This call runs the target JVM instance just to calibrate (that is, measure the time it takes to execute)
287: * the instrumentation code that we then inject into target app code. The results are saved to be reused
288: * in subsequent runs.
289: */
290: public boolean calibrateInstrumentationCode() {
291: status.targetJDKVersionString = settings
292: .getTargetJDKVersionString();
293:
294: waitDialog = appStatusHandler.getAsyncDialogInstance(
295: PERFORMING_CALIBRATION_MSG, false, false);
296: waitDialog.display();
297:
298: boolean res = false;
299:
300: try {
301: if (!runJVMToCalibrateInstrumentation()) {
302: return false;
303: }
304:
305: res = CalibrationDataFileIO.saveCalibrationData(status);
306:
307: return true;
308: } finally {
309: if (waitDialog != null) {
310: waitDialog.close();
311: }
312:
313: if (res) {
314: StringBuffer s = new StringBuffer();
315: s.append(CALIBRATION_SUMMARY_DETAILS_MSG);
316: appendCalibrationData(s);
317: appStatusHandler
318: .displayNotificationWithDetailsAndWaitForConfirm(
319: CALIBRATION_SUMMARY_SHORT_MSG, s
320: .toString());
321: } else {
322: appStatusHandler
323: .displayErrorAndWaitForConfirm(CalibrationDataFileIO
324: .getErrorMessage());
325: }
326: }
327: }
328:
329: /**
330: * Connects to the target JVM started using startTargetVM(), and starts the target application.
331: * Error reporting happens in the same way as in runTargetApp().
332: */
333: public boolean connectToStartedVMAndStartTA() {
334: return connectToStartedVMAndStartTA(false);
335: }
336:
337: public void detachFromTargetJVM() {
338: if (targetAppIsSuspended) {
339: try {
340: profilerClient.resumeTargetAppThreads();
341: } catch (ClientUtils.TargetAppOrVMTerminated ex) {
342: }
343:
344: targetAppIsSuspended = false;
345: }
346:
347: try {
348: profilerClient.detachFromTargetJVM();
349: notifyListeners(EVENT_DETACHED);
350: } catch (ClientUtils.TargetAppOrVMTerminated ex) { /* No need to say anything if it's already terminated */
351: }
352:
353: targetAppIsSuspended = false;
354: }
355:
356: public boolean hasSupportedJDKForHeapDump() {
357: // not supported for JDK other than 1.6 & 1.7 & 1.5.0_12 and up
358: String jdkVersion = getProfilerEngineSettings()
359: .getTargetJDKVersionString();
360:
361: if (CommonConstants.JDK_16_STRING.equals(jdkVersion)
362: || CommonConstants.JDK_17_STRING.equals(jdkVersion)) {
363: return true;
364: }
365:
366: if (CommonConstants.JDK_15_STRING.equals(jdkVersion)) {
367: String fullJDKString = getProfilingSessionStatus().fullTargetJDKVersionString;
368: int minorVersion = Platform
369: .getJDKMinorNumber(fullJDKString);
370:
371: if (minorVersion >= 12) {
372: return true;
373: }
374: }
375:
376: return false;
377: }
378:
379: /**
380: * Initiates profiling session
381: * @param attachMode 0 = no attach; 1 = direct; 2 = dynamic
382: * @param calibrationOnlyRun
383: * @return Returns TRUE if the connection to the profiler agent has been successfuly established
384: */
385: public boolean initiateSession(int attachMode,
386: boolean calibrationOnlyRun) {
387: if (targetJVMIsAlive()) {
388: return true;
389: }
390:
391: return profilerClient.establishConnectionWithServer(attachMode,
392: calibrationOnlyRun);
393: }
394:
395: /**
396: * @return true if the calibration data was read succesfully, false otherwise
397: */
398: public boolean readSavedCalibrationData() {
399: status.targetJDKVersionString = settings
400: .getTargetJDKVersionString();
401:
402: int res = CalibrationDataFileIO
403: .readSavedCalibrationData(status);
404:
405: if (res < 0) { // Fatal error with reading saved file data - report the details
406: appStatusHandler
407: .displayErrorAndWaitForConfirm(CalibrationDataFileIO
408: .getErrorMessage());
409: }
410:
411: return (res == 0);
412: }
413:
414: public void removeProfilingEventListener(
415: ProfilingEventListener profilingEventListener) {
416: listeners.remove(profilingEventListener);
417: }
418:
419: public void resetTimers()
420: throws ClientUtils.TargetAppOrVMTerminated {
421: profilerClient.resetProfilerCollectors();
422: }
423:
424: public void resumeTargetAppIfSuspended()
425: throws ClientUtils.TargetAppOrVMTerminated {
426: if (targetAppIsSuspended) {
427: profilerClient.resumeTargetAppThreads();
428: targetAppIsSuspended = false;
429: notifyListeners(EVENT_RESUMED);
430: }
431: }
432:
433: public void runGC() throws ClientUtils.TargetAppOrVMTerminated {
434: profilerClient.runGC();
435: }
436:
437: /**
438: * Starts the the target JVM, that then waits for the tool to establish the socket connection and start the
439: * TA itself). This function returns boolean indicating success or failure, however the actual problem is
440: * reported inside it using methods of the AppStatusHandler passed to this TargetAppRunner.
441: */
442: public boolean startTargetVM() {
443: return startTargetVM(settings.getJVMArgs(), settings
444: .getMainClassName(), settings.getMainArgs(), settings
445: .getWorkingDir(), settings.getSeparateConsole());
446: }
447:
448: public void suspendTargetAppIfRunning()
449: throws ClientUtils.TargetAppOrVMTerminated {
450: if (!targetAppIsSuspended) {
451: profilerClient.suspendTargetAppThreads();
452: targetAppIsSuspended = true;
453: notifyListeners(EVENT_SUSPENDED);
454: }
455: }
456:
457: public boolean targetAppIsRunning() {
458: return status.targetAppRunning;
459: }
460:
461: public boolean targetAppSuspended() {
462: return targetAppIsSuspended;
463: }
464:
465: public boolean targetJVMIsAlive() {
466: return profilerClient.targetJVMIsAlive();
467: }
468:
469: public void terminateTargetJVM() {
470: if (targetAppIsSuspended) {
471: try {
472: profilerClient.resumeTargetAppThreads();
473: } catch (ClientUtils.TargetAppOrVMTerminated ex) {
474: }
475:
476: targetAppIsSuspended = false;
477: }
478:
479: try {
480: profilerClient.terminateTargetJVM();
481: notifyListeners(EVENT_TERMINATED);
482: } catch (ClientUtils.TargetAppOrVMTerminated ex) {
483: /* Probably no need to say anything if it's already terminated */
484: }
485:
486: targetAppIsSuspended = false;
487: }
488:
489: //---------------------------- Statistics printing routines ----------------------------------
490:
491: /**
492: * Note that this displayed statistics is really current, i.e. it may be newer than CPU results currently displayed
493: */
494: private String getInternalStatsText(InternalStatsResponse r) {
495: NumberFormat nf = NumberFormat.getInstance();
496: nf.setMaximumFractionDigits(2);
497:
498: StringBuffer s = new StringBuffer(1000);
499:
500: s.append(INTERNAL_STATISTICS_ONLY_MSG);
501:
502: double wholeGraphGrossTimeAbs = CPUCCTContainer
503: .getWholeGraphGrossTimeAbsForDisplayedThread();
504: double timeInInjectedCode = CPUCCTContainer
505: .getTimeInInjectedCodeForDisplayedThread();
506: double totalRunTime = wholeGraphGrossTimeAbs
507: + r.totalHotswappingTime + r.clientInstrTime
508: + r.clientDataProcTime;
509:
510: s.append(MessageFormat.format(INSTR_METHODS_COUNT_MSG,
511: new Object[] { "" + r.nTotalInstrMethods })); // NOI18N
512: s.append("\n"); // NOI18N
513: s.append(MessageFormat.format(CLASSLOAD_FIRSTINV_COUNT_MSG,
514: new Object[] { "" + r.nClassLoads,
515: "" + r.nFirstMethodInvocations })); // NOI18N
516: s.append("\n"); // NOI18N
517: s.append(MessageFormat.format(NON_EMPTY_IMG_COUNT_MSG,
518: new Object[] { ""
519: + r.nNonEmptyInstrMethodGroupResponses })); // NOI18N
520: s.append("\n"); // NOI18N
521: s.append(MessageFormat
522: .format(EMPTY_IMG_COUNT_MSG, new Object[] { ""
523: + r.nEmptyInstrMethodGroupResponses })); // NOI18N
524: s.append("\n"); // NOI18N
525: s.append(MessageFormat.format(SINGLE_IMG_COUNT_MSG,
526: new Object[] { ""
527: + r.nSingleMethodInstrMethodGroupResponses })); // NOI18N
528: s.append("\n"); // NOI18N
529:
530: if (r.nNonEmptyInstrMethodGroupResponses > 0) {
531: s.append(MessageFormat
532: .format(AVG_METHOD_TIME_MSG, new Object[] { nf
533: .format(r.averageHotswappingTime) }));
534: s.append("\n"); // NOI18N
535: s.append(MessageFormat.format(MIN_METHOD_TIME_MSG,
536: new Object[] { nf.format(r.minHotswappingTime) }));
537: s.append("\n"); // NOI18N
538: s.append(MessageFormat.format(MAX_METHOD_TIME_MSG,
539: new Object[] { nf.format(r.maxHotswappingTime) }));
540: s.append("\n"); // NOI18N
541: }
542:
543: s.append("\n"); // NOI18N
544:
545: s.append(MessageFormat.format(TOTAL_RUN_TIME_MSG,
546: new Object[] { nf.format(totalRunTime) }));
547: s.append("\n"); // NOI18N
548:
549: if (totalRunTime == 0.0) {
550: totalRunTime = 1.0; // Just to avoid funny percentage figures in this case
551: }
552:
553: s.append(MessageFormat.format(INJ_INSTR_TIME_MSG, new Object[] {
554: nf.format(timeInInjectedCode),
555: nf.format(timeInInjectedCode / totalRunTime * 100) }));
556: s.append("\n"); // NOI18N
557: s.append(MessageFormat.format(TOTAL_INSTR_HOTSWAP_TIME_MSG,
558: new Object[] {
559: nf.format(r.totalHotswappingTime),
560: nf.format(r.totalHotswappingTime / totalRunTime
561: * 100) }));
562: s.append("\n"); // NOI18N
563: s.append(MessageFormat.format(BYTECODE_COMM_TIME_MSG,
564: new Object[] {
565: nf.format(r.clientInstrTime),
566: nf.format(r.clientInstrTime / totalRunTime
567: * 100) }));
568: s.append("\n"); // NOI18N
569: s.append(MessageFormat.format(CLIENT_BYTECODE_TIME_MSG,
570: new Object[] { ""
571: + profilerClient.getInstrProcessingTime() })); // NOI18N
572: s.append("\n"); // NOI18N
573: s.append(MessageFormat.format(CLIENT_DISK_PROCESS_MSG,
574: new Object[] {
575: nf.format(r.clientDataProcTime),
576: nf.format(r.clientDataProcTime / totalRunTime
577: * 100) }));
578: s.append("\n"); // NOI18N
579: // no idea what is this supposed to do; put it back if someone has a clue
580: // EventBufferProcessor eb = profilerClient.getCPUCallGraphBuilder();
581: // if (eb == null) {
582: // eb = profilerClient.getMemoryCallGraphBuilder();
583: // }
584: // long dataProcessingTime = (eb != null) ? EventBufferProcessor.getDataProcessingTime() : 0;
585:
586: long dataProcessingTime = EventBufferProcessor
587: .getDataProcessingTime();
588: s.append(MessageFormat.format(CLIENT_RESULTS_PROCESS_MSG,
589: new Object[] { "" + dataProcessingTime })); // NOI18N
590: s.append("\n"); // NOI18N
591: s.append("\n"); // NOI18N
592:
593: appendCalibrationData(s);
594: s.append("\n"); // NOI18N
595:
596: return s.toString();
597: }
598:
599: private void appendCalibrationData(StringBuffer s) {
600: NumberFormat nf = NumberFormat.getInstance();
601: nf.setMaximumFractionDigits(4);
602:
603: long cntsInSec = status.timerCountsInSecond[0];
604: double m0 = (((double) status.methodEntryExitCallTime[0]) * 1000000)
605: / cntsInSec; // Expressed in microseconds
606: double m1 = (((double) status.methodEntryExitCallTime[1]) * 1000000)
607: / cntsInSec; // Ditto
608: double m2 = (((double) status.methodEntryExitCallTime[2]) * 1000000)
609: / cntsInSec; // Ditto
610: double m4 = (((double) status.methodEntryExitCallTime[4]) * 1000000)
611: / cntsInSec; // Ditto
612:
613: s.append(MessageFormat.format(CALIBRATION_RESULTS_MSG,
614: new Object[] { nf.format(m0), nf.format(m1),
615: nf.format(m2), nf.format(m4) }));
616: }
617:
618: private boolean connectToStartedVMAndStartTA(
619: boolean calibrationOnlyRun) {
620: if (!connectToStartedVMAndStartTA(0, calibrationOnlyRun)) {
621: return false;
622: }
623:
624: status.runningInAttachedMode = false;
625: notifyListeners(EVENT_STARTED);
626:
627: return true;
628: }
629:
630: //--------------------------------- Private implementation ------------------------------------
631:
632: /**
633: * attachMode can have the following values:
634: * 0 - application started from under the tool; 1 - attach to the running application; 2 - attach on startup.
635: * calibrationOnlyRun == true means that we run the target JVM just to obtain instrumentation calibration data.
636: */
637: private boolean connectToStartedVMAndStartTA(int attachMode,
638: boolean calibrationOnlyRun) {
639: // if (!profilerClient.establishConnectionWithServer(attachMode != 0, calibrationOnlyRun)) {
640: // appStatusHandler.displayError(FAILED_ESTABLISH_CONN_MSG);
641: // return false;
642: // }
643: if (calibrationOnlyRun) {
644: return true;
645: }
646:
647: boolean sendExplicitStartCommand = (attachMode != 1);
648:
649: try {
650: return profilerClient
651: .startTargetApp(sendExplicitStartCommand);
652: } catch (ClientUtils.TargetAppOrVMTerminated e1) {
653: String message = UNEXPECTED_PROBLEM_STARTING_APP_MSG;
654:
655: if (e1.isVMTerminated()) {
656: message += JVM_TERMINATED_NOTRESPOND_STRING;
657: } else {
658: message += INTERNAL_PROBLEM_STRING;
659: }
660:
661: appStatusHandler.displayError(message);
662:
663: return false;
664: } catch (ClientUtils.TargetAppFailedToStart e2) {
665: appStatusHandler.displayError(MessageFormat.format(
666: FAILED_START_APP_CAUSE_MSG, new Object[] { e2
667: .getOrigCause() }));
668:
669: return false;
670: }
671: }
672:
673: private void notifyListeners(int event) {
674: Vector targets = null;
675:
676: synchronized (this ) {
677: if (listeners != null) {
678: targets = (java.util.Vector) listeners.clone();
679: }
680: }
681:
682: if (targets != null) {
683: for (int i = 0; i < targets.size(); i++) {
684: ProfilingEventListener target = (ProfilingEventListener) targets
685: .elementAt(i);
686:
687: switch (event) {
688: case EVENT_STARTED:
689: target.targetAppStarted();
690:
691: break;
692: case EVENT_STOPPED:
693: target.targetAppStopped();
694:
695: break;
696: case EVENT_SUSPENDED:
697: target.targetAppSuspended();
698:
699: break;
700: case EVENT_RESUMED:
701: target.targetAppResumed();
702:
703: break;
704: case EVENT_TERMINATED:
705: target.targetVMTerminated();
706:
707: break;
708: case EVENT_ATTACHED:
709: target.attachedToTarget();
710:
711: break;
712: case EVENT_DETACHED:
713: target.detachedFromTarget();
714:
715: break;
716: }
717: }
718: }
719: }
720:
721: //-------------------------- Calibration-related routines ----------------------------------
722: private boolean runJVMToCalibrateInstrumentation() {
723: boolean result = startTargetVM(new String[] {},
724: CALIBRATION_PSEUDO_CLASS_NAME, new String[] {}, ".",
725: settings.getSeparateConsole() // NOI18N
726: );
727:
728: if (!result) {
729: return false;
730: }
731:
732: result = initiateSession(0, true);
733:
734: // result = connectToStartedVMAndStartTA(true);
735: // if (!result) {
736: // return false;
737: // }
738: while (targetJVMIsAlive()) {
739: try {
740: Thread.sleep(100);
741: } catch (Exception ex) {
742: // ignore
743: }
744: }
745:
746: if (status.timerCountsInSecond[0] == 0) { // Data not received?
747: appStatusHandler
748: .displayErrorAndWaitForConfirm(CALIBRATION_ERROR_MSG);
749:
750: return false;
751: }
752:
753: return true;
754: }
755:
756: private boolean startTargetVM(String[] jvmArgs,
757: String mainClassName, String[] mainArgs, String workingDir,
758: boolean separateConsole) {
759: boolean isWindows = Platform.isWindows();
760: status.savedInternalStats = null;
761:
762: File dir = new File(workingDir);
763:
764: String classPathArg = settings.getMainClassPath(); // Note that this returns user.dir if not set
765:
766: // Create the classpath containing the JFluid server-side classes, that is passed to the target VM
767: String libPath = settings.getJFluidRootDirName();
768: String jdkVer = settings.getTargetJDKVersionString();
769:
770: if (jdkVer.equals(JDK_16_STRING)
771: || jdkVer.equals(JDK_17_STRING)) {
772: // for now the 1.6 and 1.7 profiling uses the same agent as 1.5
773: jdkVer = JDK_15_STRING;
774: }
775:
776: jdkVer = jdkVer.substring(3); // Convert e.g. "jdk15" into just "15"
777:
778: String jFluidCP = libPath
779: + File.separator
780: + "jfluid-server.jar" // NOI18N
781: + File.pathSeparator + libPath + File.separator
782: + "jfluid-server-" // NOI18N
783: + jdkVer + ".jar"; // NOI18N
784:
785: String[] newJVMArgs = new String[jvmArgs.length];
786: int idx = 0;
787:
788: for (int i = 0; i < jvmArgs.length; i++) {
789: if ((jvmArgs[i].equals("-classpath") || jvmArgs[i]
790: .equals("-cp"))
791: && ((i + 1) < jvmArgs.length)) { // NOI18N
792: // The user shouldn't set the classpath here, so let's ignore it.
793: appStatusHandler
794: .displayWarning(CLASSPATH_SETTINGS_IGNORED_MSG);
795: i++;
796:
797: //classPathArg += File.pathSeparator;
798: //classPathArg += jvmArgs[++i];
799: } else {
800: newJVMArgs[idx++] = jvmArgs[i];
801: }
802: }
803:
804: ArrayList commands = new ArrayList(10);
805:
806: if (separateConsole) {
807: if (isWindows) {
808: commands.add("cmd.exe"); // NOI18N
809: commands.add("/K"); // NOI18N
810: commands.add("start"); // NOI18N
811: commands.add("\"Profiled Application Console\""); // NOI18N
812:
813: if (settings.getTargetWindowRemains()) {
814: commands.add("cmd"); // This is used to prevent the window from closing if, say, // NOI18N
815: commands.add("/K"); // the target JVM crashes. // NOI18N
816: }
817: } else { // Solaris
818: commands.add("xterm"); // NOI18N
819: commands.add("-sb"); // NOI18N
820: commands.add("-sl"); // NOI18N
821: commands.add("1000"); // NOI18N
822: commands.add("-e"); // NOI18N
823: }
824: }
825:
826: commands.add(settings.getTargetJVMExeFile());
827:
828: if (settings.getTargetJDKVersionString().equals(
829: Platform.JDK_15_STRING)
830: || settings.getTargetJDKVersionString().equals(
831: Platform.JDK_16_STRING)
832: || settings.getTargetJDKVersionString().equals(
833: Platform.JDK_17_STRING)) {
834: String jfNativeLibFullName = Platform
835: .getAgentNativeLibFullName(settings
836: .getJFluidRootDirName(), false, settings
837: .getTargetJDKVersionString(), settings
838: .getSystemArchitecture());
839: commands.add("-agentpath:" + jfNativeLibFullName); // NOI18N
840: }
841:
842: commands.add("-Xbootclasspath/a:" + jFluidCP); // NOI18N
843:
844: if ((classPathArg != null) && !classPathArg.equals("")) { // NOI18N
845: commands.add("-classpath"); // NOI18N
846: commands.add(classPathArg);
847: }
848:
849: if (!isWindows && settings.getTargetWindowRemains()) {
850: commands.add("-XX:+ShowMessageBoxOnError"); // NOI18N
851: }
852:
853: for (int i = 0; i < newJVMArgs.length; i++) {
854: commands.add(newJVMArgs[i]);
855: }
856:
857: // debugging property for agent side - wire I/O
858: if (System
859: .getProperty("org.netbeans.lib.profiler.wireprotocol.WireIO.agent") != null) { // NOI18N
860: commands
861: .add("-Dorg.netbeans.lib.profiler.wireprotocol.WireIO=true"); // NOI18N
862: }
863:
864: // debugging property for agent side - Class loader hook
865: if (System
866: .getProperty("org.netbeans.lib.profiler.server.ProfilerInterface.classLoadHook") != null) { // NOI18N
867: commands
868: .add("-Dorg.netbeans.lib.profiler.server.ProfilerInterface.classLoadHook=true"); // NOI18N
869: }
870:
871: commands.add("org.netbeans.lib.profiler.server.ProfilerServer"); // NOI18N
872:
873: // Really needed by ProfilerServer only in JDK 1.4.2, to call System.load() with this param - TODO: check this
874: commands.add(Platform.getJFluidNativeLibDirName(settings
875: .getJFluidRootDirName(), settings
876: .getTargetJDKVersionString(), settings
877: .getSystemArchitecture()));
878: commands.add(Integer.toString(settings.getPortNo()));
879:
880: // 10 seconds is the default timeout, can be set via the profiler.agent.connect.timeout property
881: String timeOut = System.getProperty(
882: "profiler.agent.connect.timeout", "10"); // NOI18N
883: commands.add(timeOut);
884:
885: if (mainClassName != null) {
886: commands.add(mainClassName);
887: }
888:
889: for (int i = 0; i < mainArgs.length; i++) {
890: commands.add(mainArgs[i]);
891: }
892:
893: String[] cmdArray = new String[commands.size()];
894: commands.toArray(cmdArray);
895:
896: MiscUtils.printInfoMessage("Starting target application..."); // NOI18N
897: MiscUtils.printVerboseInfoMessage(cmdArray);
898:
899: if (DEBUG) {
900: System.err
901: .println("TargetAppRunner.DEBUG: Starting VM with "
902: + cmdArray.length + " commands."); // NOI18N
903:
904: for (int i = 0; i < cmdArray.length; i++) {
905: System.err.println("TargetAppRunner.DEBUG: cmd[" + i
906: + "] = >" + cmdArray[i] + "<"); // NOI18N
907: }
908: }
909:
910: try {
911: runningAppProcess = Runtime.getRuntime().exec(cmdArray,
912: null, dir);
913: } catch (IOException ex) {
914: String s = ""; // NOI18N
915:
916: for (int i = 0; i < cmdArray.length; i++) {
917: s = s + cmdArray[i] + "\n"; // NOI18N
918: }
919:
920: appStatusHandler.displayError(MessageFormat.format(
921: ERROR_STARTING_JVM_MSG, new Object[] { s, ex }));
922:
923: return false;
924: }
925:
926: return true;
927: }
928: }
|