diff --git a/core.ui/nbproject/project.xml b/core.ui/nbproject/project.xml --- a/core.ui/nbproject/project.xml +++ b/core.ui/nbproject/project.xml @@ -77,6 +77,14 @@ + org.netbeans.modules.sampler + + + + 1.0 + + + org.netbeans.spi.quicksearch @@ -180,11 +188,6 @@ org.netbeans.libs.junit4 - - org.netbeans.modules.nbjunit - - - diff --git a/core.ui/src/org/netbeans/core/ui/sampler/Bundle.properties b/core.ui/src/org/netbeans/core/ui/sampler/Bundle.properties --- a/core.ui/src/org/netbeans/core/ui/sampler/Bundle.properties +++ b/core.ui/src/org/netbeans/core/ui/sampler/Bundle.properties @@ -46,5 +46,3 @@ SelfSamplerAction_ActionNameStop=Stop Application Profiling and Take a Snapshot SelfSamplerAction_ActionDescription=Will take a snapshot based on the thread dump SelfSamplerAction_NotSupported=Self Sampler is not available. -SelfSamplerAction_SavedFile=Snapshot was saved to {0} -Save_Progress=Saving snapshot diff --git a/core.ui/src/org/netbeans/core/ui/sampler/SelfSamplerAction.java b/core.ui/src/org/netbeans/core/ui/sampler/SelfSamplerAction.java --- a/core.ui/src/org/netbeans/core/ui/sampler/SelfSamplerAction.java +++ b/core.ui/src/org/netbeans/core/ui/sampler/SelfSamplerAction.java @@ -42,38 +42,21 @@ package org.netbeans.core.ui.sampler; import java.awt.AWTEvent; -import java.awt.EventQueue; import java.awt.Toolkit; import java.awt.event.AWTEventListener; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.lang.management.RuntimeMXBean; -import java.lang.management.ThreadMXBean; -import java.text.MessageFormat; -import java.util.List; import java.util.concurrent.atomic.AtomicReference; -import java.util.logging.Level; -import java.util.logging.Logger; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.SwingWorker; -import org.netbeans.api.progress.ProgressHandle; -import org.netbeans.api.progress.ProgressHandleFactory; +import org.netbeans.modules.sampler.Sampler; import org.openide.DialogDisplayer; import org.openide.NotifyDescriptor; import org.openide.awt.ActionID; import org.openide.awt.ActionReference; import org.openide.awt.ActionReferences; import org.openide.awt.ActionRegistration; -import org.openide.cookies.OpenCookie; -import org.openide.filesystems.FileObject; -import org.openide.filesystems.FileUtil; -import org.openide.loaders.DataObject; -import org.openide.util.Exceptions; import org.openide.util.NbBundle; /** @@ -93,20 +76,7 @@ private static final String ACTION_NAME_STOP = NbBundle.getMessage(SelfSamplerAction.class, "SelfSamplerAction_ActionNameStop"); // private static final String ACTION_DESCR = NbBundle.getMessage(SelfSamplerAction.class, "SelfSamplerAction_ActionDescription"); private static final String NOT_SUPPORTED = NbBundle.getMessage(SelfSamplerAction.class, "SelfSamplerAction_NotSupported"); - private static final String SAVE_MSG = NbBundle.getMessage(SelfSamplerAction.class, "SelfSamplerAction_SavedFile"); - private static final String DEBUG_ARG = "-Xdebug"; // NOI18N - private static final Logger LOGGER = Logger.getLogger(SelfSamplerAction.class.getName()); private final AtomicReference RUNNING = new AtomicReference(); - private static Boolean debugMode; - private static String lastReason; - private static Class defaultDataObject; - static { - try { - defaultDataObject = Class.forName("org.openide.loaders.DefaultDataObject"); // NOI18N - } catch (ClassNotFoundException ex) { - Exceptions.printStackTrace(ex); - } - } //~ Constructors ------------------------------------------------------------------------------------------------------------- public SelfSamplerAction() { @@ -125,13 +95,13 @@ */ @Override public void actionPerformed(final ActionEvent e) { - if (SamplesOutputStream.isSupported()) { - Sampler c; - if (RUNNING.compareAndSet(null, c = new InternalSampler("Self Sampler"))) { // NOI18N + Sampler c = Sampler.getGenericSampler("Self Sampler"); // NOI18N + if (c != null) { + if (RUNNING.compareAndSet(null, c)) { putValue(Action.NAME, ACTION_NAME_STOP); putValue(Action.SHORT_DESCRIPTION, ACTION_NAME_STOP); putValue ("iconBase", "org/netbeans/core/ui/sampler/selfSamplerRunning.png"); // NOI18N - c.run(); + c.start(); } else if ((c = RUNNING.getAndSet(null)) != null) { final Sampler controller = c; @@ -140,7 +110,7 @@ @Override protected Object doInBackground() throws Exception { - controller.actionPerformed(new ActionEvent(this, 0, "show")); // NOI18N + controller.stop(); return null; } @@ -156,6 +126,7 @@ } } else { NotifyDescriptor d = new NotifyDescriptor.Message(NOT_SUPPORTED, NotifyDescriptor.INFORMATION_MESSAGE); + DialogDisplayer.getDefault().notify(d); } } @@ -168,144 +139,7 @@ } } - @Override - public Object getValue(String key) { - Object o = super.getValue(key); - if (o == null && key.startsWith("logger-") && SamplesOutputStream.isSupported() && isRunMode()) { // NOI18N - return new InternalSampler(key); - } - return o; - } - final boolean isProfileMe(Sampler c) { return c == RUNNING.get(); } - - private static synchronized boolean isDebugged() { - if (debugMode == null) { - debugMode = Boolean.FALSE; - - // check if we are debugged - RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean(); - List args = runtime.getInputArguments(); - if (args.contains(DEBUG_ARG)) { - debugMode = Boolean.TRUE; - } - } - return debugMode.booleanValue(); - } - - private static boolean isRunMode() { - boolean runMode = true; - String reason = null; - - if (isDebugged()) { - reason = "running in debug mode"; // NOI18N - runMode = false; - } - if (runMode) { - // check if netbeans is profiled - try { - Class.forName("org.netbeans.lib.profiler.server.ProfilerServer", false, ClassLoader.getSystemClassLoader()); // NO18N - reason = "running under profiler"; // NOI18N - runMode = false; - } catch (ClassNotFoundException ex) { - } - } - if (!runMode && !reason.equals(lastReason)) { - LOGGER.log(Level.INFO, "Slowness detector disabled - {0}", reason); // NOI18N - } - lastReason = reason; - return runMode; - } - - private static final class InternalSampler extends Sampler { - private ProgressHandle progress; - - InternalSampler(String thread) { - super(thread); - } - - @Override - protected void printStackTrace(Throwable ex) { - Exceptions.printStackTrace(ex); - } - - @Override - protected void saveSnapshot(byte[] arr) throws IOException { - // save snapshot - File outFile = File.createTempFile("selfsampler", SamplesOutputStream.FILE_EXT); // NOI18N - outFile = FileUtil.normalizeFile(outFile); - writeToFile(outFile, arr); - - File gestures = new File(new File(new File( - new File(System.getProperty("netbeans.user")), // NOI18N - "var"), "log"), "uigestures"); // NOI18N - - SelfSampleVFS fs; - if (gestures.exists()) { - fs = new SelfSampleVFS( - new String[] { "selfsampler.npss", "selfsampler.log" }, - new File[] { outFile, gestures } - ); - } else { - fs = new SelfSampleVFS( - new String[] { "selfsampler.npss" }, - new File[] { outFile } - ); - } - - // open snapshot - FileObject fo = fs.findResource("selfsampler.npss"); - DataObject dobj = DataObject.find(fo); - // ugly test for DefaultDataObject - if (defaultDataObject.isAssignableFrom(dobj.getClass())) { - String msg = MessageFormat.format(SelfSamplerAction.SAVE_MSG, outFile.getAbsolutePath()); - DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(msg)); - } else { - dobj.getCookie(OpenCookie.class).open(); - } - } - - private void writeToFile(File file, byte[] arr) { - try { - FileOutputStream fstream = new FileOutputStream(file); - fstream.write(arr); - fstream.close(); - } catch (IOException ex) { - Exceptions.printStackTrace(ex); - } - } - - @Override - protected ThreadMXBean getThreadMXBean() { - return ManagementFactory.getThreadMXBean(); - } - - protected void openProgress(final int steps) { - if (EventQueue.isDispatchThread()) { - // log warnining - return; - } - progress = ProgressHandleFactory.createHandle(NbBundle.getMessage(SelfSamplerAction.class, "Save_Progress")); - progress.start(steps); - } - - protected void closeProgress() { - if (EventQueue.isDispatchThread()) { - return; - } - progress.finish(); - progress = null; - } - - protected void progress(int i) { - if (EventQueue.isDispatchThread()) { - return; - } - if (progress != null) { - progress.progress(i); - } - } - } } diff --git a/editor.completion/nbproject/project.xml b/editor.completion/nbproject/project.xml --- a/editor.completion/nbproject/project.xml +++ b/editor.completion/nbproject/project.xml @@ -104,6 +104,14 @@ + org.netbeans.modules.sampler + + + + 1.0 + + + org.openide.awt diff --git a/editor.completion/src/org/netbeans/modules/editor/completion/CompletionImplProfile.java b/editor.completion/src/org/netbeans/modules/editor/completion/CompletionImplProfile.java --- a/editor.completion/src/org/netbeans/modules/editor/completion/CompletionImplProfile.java +++ b/editor.completion/src/org/netbeans/modules/editor/completion/CompletionImplProfile.java @@ -43,15 +43,11 @@ */ package org.netbeans.modules.editor.completion; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.util.logging.Level; import java.util.logging.Logger; -import javax.swing.Action; -import org.openide.filesystems.FileObject; -import org.openide.filesystems.FileUtil; +import org.netbeans.modules.sampler.Sampler; import org.openide.util.Exceptions; import org.openide.util.RequestProcessor; @@ -59,27 +55,18 @@ private static final Logger LOG = Logger.getLogger(CompletionImplProfile.class.getName()); static final RequestProcessor RP = new RequestProcessor("Completion Slowness"); // NOI18N - private final Object profiler; + private final Sampler profiler; private boolean profiling; private final long time; CompletionImplProfile(long when) { time = when; - Object p = null; - FileObject fo = FileUtil.getConfigFile("Actions/Profile/org-netbeans-modules-profiler-actions-SelfSamplerAction.instance"); // NOI18N - if (fo != null) { - Action a = (Action) fo.getAttribute("delegate"); // NOI18N - if (a != null) { - p = a.getValue("logger-completion"); // NOI18N - } - } - this.profiler = p; + this.profiler = Sampler.getSampler("logger-completion"); // NOI18N this.profiling = true; - if (profiler instanceof Runnable) { - Runnable r = (Runnable) profiler; - r.run(); + if (profiler != null) { + profiler.start(); LOG.log(Level.FINE, "Profiling started {0} at {1}", new Object[] { profiler, time }); } } @@ -106,12 +93,11 @@ private void stopImpl(long now) throws Exception { long delta = now - time; LOG.log(Level.FINE, "Profiling stopped at {0}", now); - ActionListener ss = (ActionListener) profiler; int report = Integer.getInteger("org.netbeans.modules.editor.completion.slowness.report", 2000); // NOI18N if (delta < report) { - LOG.log(Level.FINE, "Cancel profiling of {0}. Profiling {1}. Time {2} ms.", new Object[] { ss, profiling, delta }); - if (ss != null) { - ss.actionPerformed(new ActionEvent(this, 0, "cancel")); + LOG.log(Level.FINE, "Cancel profiling of {0}. Profiling {1}. Time {2} ms.", new Object[] { profiler, profiling, delta }); + if (profiler != null) { + profiler.cancel(); } return; } @@ -119,8 +105,8 @@ LOG.log(Level.FINE, "Obtaining snapshot for {0} ms.", delta); ByteArrayOutputStream out = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(out); - if (ss != null) { - ss.actionPerformed(new ActionEvent(dos, 0, "write")); // NOI18N + if (profiler != null) { + profiler.stopAndWriteTo(dos); } dos.close(); if (dos.size() > 0) { diff --git a/ide.kit/nbproject/project.xml b/ide.kit/nbproject/project.xml --- a/ide.kit/nbproject/project.xml +++ b/ide.kit/nbproject/project.xml @@ -230,10 +230,6 @@ - org.netbeans.core.ui - - - org.netbeans.libs.junit4 @@ -264,6 +260,11 @@ + org.netbeans.modules.sampler + + + + org.openide.modules diff --git a/ide.kit/test/qa-functional/src/org/netbeans/test/ide/BlacklistedClassesHandlerSingleton.java b/ide.kit/test/qa-functional/src/org/netbeans/test/ide/BlacklistedClassesHandlerSingleton.java --- a/ide.kit/test/qa-functional/src/org/netbeans/test/ide/BlacklistedClassesHandlerSingleton.java +++ b/ide.kit/test/qa-functional/src/org/netbeans/test/ide/BlacklistedClassesHandlerSingleton.java @@ -77,7 +77,7 @@ import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.logging.Logger; -import org.netbeans.core.ui.sampler.SamplesOutputStream; +import org.netbeans.modules.sampler.CustomSamplesStream; import org.openide.util.Exceptions; /** @@ -114,7 +114,7 @@ private String newWhitelistFileName; private String previousWhitelistFileName = null; private File whitelistStorageDir = null; - private SamplesOutputStream samples; + private CustomSamplesStream samples; private ByteArrayOutputStream stream; private ThreadMXBean threadBean; private long start; @@ -176,7 +176,7 @@ start = System.currentTimeMillis(); stream = new ByteArrayOutputStream(); try { - samples = new SamplesOutputStream(stream, null, 5000); + samples = new CustomSamplesStream(stream, 5000); } catch (IOException ex) { throw new RuntimeException(ex); } diff --git a/jumpto/nbproject/project.xml b/jumpto/nbproject/project.xml --- a/jumpto/nbproject/project.xml +++ b/jumpto/nbproject/project.xml @@ -104,6 +104,14 @@ + org.netbeans.modules.sampler + + + + 1.0 + + + org.netbeans.spi.quicksearch diff --git a/jumpto/src/org/netbeans/modules/jumpto/type/GoToTypeAction.java b/jumpto/src/org/netbeans/modules/jumpto/type/GoToTypeAction.java --- a/jumpto/src/org/netbeans/modules/jumpto/type/GoToTypeAction.java +++ b/jumpto/src/org/netbeans/modules/jumpto/type/GoToTypeAction.java @@ -71,7 +71,6 @@ import java.util.logging.Logger; import java.util.regex.Pattern; import javax.swing.AbstractAction; -import javax.swing.Action; import javax.swing.DefaultListModel; import javax.swing.JButton; import javax.swing.ListCellRenderer; @@ -87,6 +86,7 @@ import org.netbeans.api.project.ui.OpenProjects; import org.netbeans.modules.jumpto.EntitiesListCellRenderer; import org.netbeans.modules.jumpto.file.LazyListModel; +import org.netbeans.modules.sampler.Sampler; import org.openide.DialogDescriptor; import org.openide.DialogDisplayer; import org.openide.ErrorManager; @@ -774,15 +774,7 @@ return null; } - FileObject fo = FileUtil.getConfigFile("Actions/Profile/org-netbeans-modules-profiler-actions-SelfSamplerAction.instance"); //NOI18N - if (fo == null) { - return null; - } - Action a = (Action)fo.getAttribute("delegate"); // NOI18N - if (a == null) { - return null; - } - Object profiler = a.getValue("logger-jumpto"); //NOI18N + Sampler profiler = Sampler.getSampler("logger-jumpto"); //NOI18N if (profiler == null) { return null; } @@ -791,10 +783,10 @@ private class Profile implements Runnable { private final long time; - private volatile Object profiler; + private volatile Sampler profiler; private volatile boolean profiling; - public Profile(Object profiler) { + public Profile(Sampler profiler) { time = System.currentTimeMillis(); this.profiler = profiler; } @@ -807,16 +799,13 @@ @Override public synchronized void run() { profiling = true; - if (profiler instanceof Runnable) { - Runnable r = (Runnable)profiler; - r.run(); - } + profiler.start(); } private synchronized void stop() throws Exception { long delta = System.currentTimeMillis() - time; - ActionListener ss = (ActionListener)profiler; + Sampler ss = profiler; profiler = null; if (!profiling) { return; @@ -824,7 +813,7 @@ try { ByteArrayOutputStream out = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(out); - ss.actionPerformed(new ActionEvent(dos, 0, "write")); // NOI18N + ss.stopAndWriteTo(dos); dos.close(); if (dos.size() > 0) { Object[] params = new Object[]{out.toByteArray(), delta, "GoToType" }; //NOI18N diff --git a/nbbuild/cluster.properties b/nbbuild/cluster.properties --- a/nbbuild/cluster.properties +++ b/nbbuild/cluster.properties @@ -209,6 +209,7 @@ print,\ progress.ui,\ queries,\ + sampler,\ sendopts,\ settings,\ spi.actions,\ diff --git a/o.n.core/nbproject/project.xml b/o.n.core/nbproject/project.xml --- a/o.n.core/nbproject/project.xml +++ b/o.n.core/nbproject/project.xml @@ -76,6 +76,14 @@ + org.netbeans.modules.sampler + + + + 1.0 + + + org.netbeans.swing.plaf diff --git a/o.n.core/src/org/netbeans/core/TimableEventQueue.java b/o.n.core/src/org/netbeans/core/TimableEventQueue.java --- a/o.n.core/src/org/netbeans/core/TimableEventQueue.java +++ b/o.n.core/src/org/netbeans/core/TimableEventQueue.java @@ -50,18 +50,14 @@ import java.awt.KeyboardFocusManager; import java.awt.Toolkit; import java.awt.Window; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.util.logging.Level; import java.util.logging.Logger; -import javax.swing.Action; import javax.swing.JFrame; import javax.swing.JRootPane; import javax.swing.SwingUtilities; -import org.openide.filesystems.FileObject; -import org.openide.filesystems.FileUtil; +import org.netbeans.modules.sampler.Sampler; import org.openide.util.Exceptions; import org.openide.util.Lookup; import org.openide.util.Mutex; @@ -97,7 +93,7 @@ private final RequestProcessor.Task WAIT_CURSOR_CHECKER; private volatile long ignoreTill; private volatile long start; - private volatile ActionListener stoppable; + private volatile Sampler stoppable; private volatile boolean isWaitCursor; static volatile Thread eq; private final Frame mainWindow; @@ -197,9 +193,9 @@ } else { LOG.log(Level.FINEST, "done, timer stopped, took {0}", time); } - ActionListener ss = stoppable; + Sampler ss = stoppable; if (ss != null) { - ss.actionPerformed(new ActionEvent(this, 0, "cancel")); // NOI18N + ss.cancel(); stoppable = null; } return; @@ -225,10 +221,10 @@ LOG.log(Level.WARNING, "Still previous controller {0}", stoppable); return; } - Runnable selfSampler = (Runnable)createSelfSampler(); + Sampler selfSampler = createSelfSampler(); if (selfSampler != null) { - selfSampler.run(); - stoppable = (ActionListener)selfSampler; + selfSampler.start(); + stoppable = selfSampler; } isWaitCursor |= isWaitCursor(); if (!isWaitCursor) { @@ -236,7 +232,7 @@ } } - private static void report(final ActionListener ss, final long time) { + private static void report(final Sampler ss, final long time) { if (ss == null) { return; } @@ -246,7 +242,7 @@ try { ByteArrayOutputStream out = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(out); - ss.actionPerformed(new ActionEvent(dos, 0, "write")); // NOI18N + ss.stopAndWriteTo(dos); dos.close(); if (dos.size() > 0) { Object[] params = new Object[]{out.toByteArray(), time}; @@ -262,16 +258,8 @@ RP.post(new R()); } - private static Object createSelfSampler() { - FileObject fo = FileUtil.getConfigFile("Actions/Profile/org-netbeans-modules-profiler-actions-SelfSamplerAction.instance"); - if (fo == null) { - return null; - } - Action a = (Action)fo.getAttribute("delegate"); // NOI18N - if (a == null) { - return null; - } - return a.getValue("logger-awt"); // NOI18N + private static Sampler createSelfSampler() { + return Sampler.getSampler("logger-awt"); // NOI18N } private static boolean isWaitCursor() { diff --git a/parsing.api/nbproject/project.xml b/parsing.api/nbproject/project.xml --- a/parsing.api/nbproject/project.xml +++ b/parsing.api/nbproject/project.xml @@ -141,6 +141,14 @@ + org.netbeans.modules.sampler + + + + 1.0 + + + org.netbeans.spi.tasklist diff --git a/parsing.api/src/org/netbeans/modules/parsing/impl/SelfProfile.java b/parsing.api/src/org/netbeans/modules/parsing/impl/SelfProfile.java --- a/parsing.api/src/org/netbeans/modules/parsing/impl/SelfProfile.java +++ b/parsing.api/src/org/netbeans/modules/parsing/impl/SelfProfile.java @@ -41,15 +41,11 @@ */ package org.netbeans.modules.parsing.impl; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.util.logging.Level; import java.util.logging.Logger; -import javax.swing.Action; -import org.openide.filesystems.FileObject; -import org.openide.filesystems.FileUtil; +import org.netbeans.modules.sampler.Sampler; import org.openide.util.Exceptions; /** @@ -58,28 +54,18 @@ */ final class SelfProfile { private static final Logger LOG = Logger.getLogger(SelfProfile.class.getName()); - - private final Object profiler; + + private final Sampler profiler; private final long time; private volatile boolean profiling; SelfProfile (long when) { time = when; - Object p = null; - final FileObject fo = FileUtil.getConfigFile("Actions/Profile/org-netbeans-modules-profiler-actions-SelfSamplerAction.instance"); // NOI18N - if (fo != null) { - final Action a = (Action) fo.getAttribute("delegate"); // NOI18N - if (a != null) { - p = a.getValue("logger-taskcancel"); // NOI18N - } - } - this.profiler = p; + this.profiler = Sampler.getSampler("logger-taskcancel"); // NOI18N; this.profiling = true; - LOG.finest("STARTED"); //NOI18N - if (profiler instanceof Runnable) { - final Runnable r = (Runnable) profiler; - r.run(); + if (profiler != null) { + profiler.start(); LOG.log(Level.FINE, "Profiling started {0} at {1}", new Object[] { profiler, time }); //NOI18N } } @@ -101,23 +87,20 @@ final long now = System.currentTimeMillis(); long delta = now - time; LOG.log(Level.FINE, "Profiling stopped at {0}", now); - ActionListener ss = (ActionListener) profiler; int report = Integer.getInteger("org.netbeans.modules.parsing.api.taskcancel.slowness.report", 1000); // NOI18N if (delta < report) { - LOG.finest("CANCEL"); //NOI18N - if (ss != null) { - ss.actionPerformed(new ActionEvent(this, 0, "cancel")); - LOG.log(Level.FINE, "Cancel profiling of {0}. Profiling {1}. Time {2} ms.", new Object[] { ss, profiling, delta }); + LOG.log(Level.FINE, "Cancel profiling of {0}. Profiling {1}. Time {2} ms.", new Object[] { profiler, profiling, delta }); + if (profiler != null) { + profiler.cancel(); } return; } try { + LOG.log(Level.FINE, "Obtaining snapshot for {0} ms.", delta); ByteArrayOutputStream out = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(out); - LOG.finest("LOGGED"); //NOI18N - if (ss != null) { - ss.actionPerformed(new ActionEvent(dos, 0, "write")); // NOI18N - LOG.log(Level.FINE, "Obtaining snapshot for {0} ms.", delta); //NOI18N + if (profiler != null) { + profiler.stopAndWriteTo(dos); } dos.close(); if (dos.size() > 0) { diff --git a/sampler/apichanges.xml b/sampler/apichanges.xml new file mode 100644 --- /dev/null +++ b/sampler/apichanges.xml @@ -0,0 +1,80 @@ + + + + + + + + Sampler API + + + + + +Change History for the Sampler API + + + + +

Introduction

+

What do the Dates Mean?

+

The supplied dates indicate when the API change was made, on the HG +default branch. From this you can generally tell whether the change should be +present in a given build or not; for trunk builds, simply whether it +was made before or after the change; for builds on a stabilization +branch, whether the branch was made before or after the given date. In +some cases corresponding API changes have been made both in the trunk +and in an in-progress stabilization branch, if they were needed for a +bug fix; this ought to be marked in this list.

+
+ +
+

@FOOTER@

+ +
+
diff --git a/sampler/arch.xml b/sampler/arch.xml new file mode 100644 --- /dev/null +++ b/sampler/arch.xml @@ -0,0 +1,1068 @@ + + + +]> + + + + &api-questions; + + + + +

+ + allows anyone to self-sample NetBeans Platform based application. +

+ +
+ + + + + +

+ There will be tests to verify that the API works + as advertised. +

+
+ + + + + +

+ Currently NetBeans has sampling code part of core.ui module. There is + semi-API used by other modules like core, jumpto, editor.completion, + but it would be desirable to have real API in separate module. + So let's target this for inclusion in NetBeans 7.2 releases. +

+
+ + + + + +

+ + There needs to be a simple API for someone who wants to create simple user + action which starts and stops self-sampling and presents user with sampled + results. +

+ The correct way to achieve this is to call + Sampler.getGenericSampler(), + followed by + Sampler.start() and + Sampler.stop(). + + + + Second major use-case of the Sampling API is sampling of the slow operation. + +

+ The correct way to achieve this is to call + Sampler.getSampler(), + followed by + Sampler.start() and + Sampler.stopAndWriteTo(DataOutputStream dos). +
+

+
+ + + + + +

+ It allows NetBeans Platform developer to self-sample their application, + which should provide them with a valuable information about CPU + performance problems. +

+
+ + + + + + + + + + + + +

+ Yes, it is. +

+
+ + +

+ This module replaces the previous semi-API + offered by core.ui module. Modules are now adviced + to depend just on the API exported by this module. +

+
+ + + + +

+ No standard is implemented by this module. +

+
+ + + + + +

+ XXX no answer for compat-version +

+
+ + + + + +

+ XXX no answer for dep-jre +

+
+ + + + + +

+ XXX no answer for dep-jrejdk +

+
+ + + + + + + + + + + + +

+ None. +

+
+ + + + + +

+ There is no platform dependency. +

+
+ + + + +

+ Nothing. +

+
+ + + + + +

+ XXX no answer for deploy-jar +

+
+ + + + + +

+ Yes, it can be deployed as NBM. +

+
+ + + + + +

+ XXX no answer for deploy-packages +

+
+ + + + + +

+ XXX no answer for deploy-shared +

+
+ + + + + +

+ XXX no answer for exec-ant-tasks +

+
+ + + + + +

+ XXX no answer for exec-classloader +

+
+ + + + + +

+ XXX no answer for exec-component +

+
+ + + + + +

+ XXX no answer for exec-introspection +

+
+ + + + + +

+ XXX no answer for exec-privateaccess +

+
+ + + + + +

+ XXX no answer for exec-process +

+
+ + + + + +

+ XXX no answer for exec-property +

+
+ + + + + +

+ XXX no answer for exec-reflection +

+
+ + + + + +

+ XXX no answer for exec-threading +

+
+ + + + + +

+ XXX no answer for format-clipboard +

+
+ + + + + +

+ XXX no answer for format-dnd +

+
+ + + + + +

+ XXX no answer for format-types +

+
+ + + + + +

+ XXX no answer for format-types +

+
+ + + + + +

+ XXX no answer for lookup-register +

+
+ + + + + +

+ XXX no answer for lookup-remove +

+
+ + + + + +

+ XXX no answer for perf-exit +

+
+ + + + + +

+ XXX no answer for perf-huge_dialogs +

+
+ + + + + +

+ XXX no answer for perf-limit +

+
+ + + + + +

+ XXX no answer for perf-mem +

+
+ + + + + +

+ XXX no answer for perf-menus +

+
+ + + + + +

+ XXX no answer for perf-progress +

+
+ + + + + +

+ XXX no answer for perf-scale +

+
+ + + + + +

+ XXX no answer for perf-spi +

+
+ + + + + +

+ XXX no answer for perf-startup +

+
+ + + + + +

+ XXX no answer for perf-wakeup +

+
+ + + + + +

+ XXX no answer for resources-file +

+
+ + + + + +

+ XXX no answer for resources-layer +

+
+ + + + + +

+ XXX no answer for resources-mask +

+
+ + + + + +

+ XXX no answer for resources-read +

+
+ + + + + +

+ XXX no answer for security-grant +

+
+ + + + + +

+ XXX no answer for security-policy +

+
+ + + + + + +

+ XXX no answer for resources-preferences +

+
+ +
diff --git a/sampler/build.xml b/sampler/build.xml new file mode 100644 --- /dev/null +++ b/sampler/build.xml @@ -0,0 +1,5 @@ + + + Builds, tests, and runs the project org.netbeans.modules.sampler + + diff --git a/sampler/manifest.mf b/sampler/manifest.mf new file mode 100644 --- /dev/null +++ b/sampler/manifest.mf @@ -0,0 +1,5 @@ +Manifest-Version: 1.0 +OpenIDE-Module: org.netbeans.modules.sampler +OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/sampler/Bundle.properties +OpenIDE-Module-Specification-Version: 1.0 + diff --git a/sampler/nbproject/project.properties b/sampler/nbproject/project.properties new file mode 100644 --- /dev/null +++ b/sampler/nbproject/project.properties @@ -0,0 +1,4 @@ +is.autoload=true +javac.source=1.6 +javac.compilerargs=-Xlint -Xlint:-serial +javadoc.arch=${basedir}/arch.xml diff --git a/sampler/nbproject/project.xml b/sampler/nbproject/project.xml new file mode 100644 --- /dev/null +++ b/sampler/nbproject/project.xml @@ -0,0 +1,109 @@ + + + org.netbeans.modules.apisupport.project + + + org.netbeans.modules.sampler + + + org.netbeans.api.annotations.common + + + + 1 + 1.13 + + + + org.netbeans.api.progress + + + + 1 + 1.27 + + + + org.openide.awt + + + + 7.40 + + + + org.openide.dialogs + + + + 7.24 + + + + org.openide.filesystems + + + + 7.55 + + + + org.openide.loaders + + + + 7.33 + + + + org.openide.nodes + + + + 7.26 + + + + org.openide.util + + + + 8.20 + + + + org.openide.util.lookup + + + + 8.12 + + + + + + unit + + org.netbeans.insane + + + + org.netbeans.libs.junit4 + + + + org.netbeans.modules.nbjunit + + + + org.openide.dialogs + + + + + + org.netbeans.modules.sampler + + + + diff --git a/sampler/src/org/netbeans/modules/sampler/Bundle.properties b/sampler/src/org/netbeans/modules/sampler/Bundle.properties new file mode 100644 --- /dev/null +++ b/sampler/src/org/netbeans/modules/sampler/Bundle.properties @@ -0,0 +1,3 @@ +OpenIDE-Module-Name=Sampler +SelfSamplerAction_SavedFile=Snapshot was saved to {0} +Save_Progress=Saving snapshot diff --git a/sampler/src/org/netbeans/modules/sampler/CLISampler.java b/sampler/src/org/netbeans/modules/sampler/CLISampler.java new file mode 100644 --- /dev/null +++ b/sampler/src/org/netbeans/modules/sampler/CLISampler.java @@ -0,0 +1,161 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ +package org.netbeans.modules.sampler; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadMXBean; +import javax.management.MBeanServerConnection; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXServiceURL; + +/** + * Support for sampling from command line + * + * @author Tomas Hurka, Jaroslav Tulach + * + */ +class CLISampler extends Sampler { + + private final ThreadMXBean threadMXBean; + private final File output; + + public static void main(String... args) throws Exception { + if (args.length != 2) { + System.out.println("Usage: "); + System.out.println(); + System.out.println("First of all start your application with following parameters:"); + System.out.println(" -Dcom.sun.management.jmxremote.authenticate=false"); + System.out.println(" -Dcom.sun.management.jmxremote.ssl=false"); + System.out.println(" -Dcom.sun.management.jmxremote.port="); + System.out.println("Then you can start this sampler with correct port and file to write snapshot to."); + System.exit(1); + } + if (!SamplesOutputStream.isSupported()) { + System.err.println("Sampling is not supported by JVM"); + System.exit(2); + } + + String u = args[0]; + try { + u = "service:jmx:rmi:///jndi/rmi://localhost:" + Integer.parseInt(args[0]) + "/jmxrmi"; + } catch (NumberFormatException ex) { + // OK, use args[0] + } + + System.err.println("Connecting to " + u); + JMXServiceURL url = new JMXServiceURL(u); + JMXConnector jmxc = null; + Exception ex = null; + for (int i = 0; i < 100; i++) { + try { + jmxc = JMXConnectorFactory.connect(url, null); + break; + } catch (IOException e) { + ex = e; + System.err.println("Connection failed. Will retry in 300ms."); + Thread.sleep(300); + } + } + if (jmxc == null) { + ex.printStackTrace(); + System.err.println("Cannot connect to " + u); + System.exit(3); + } + MBeanServerConnection server = jmxc.getMBeanServerConnection(); + + final ThreadMXBean threadMXBean = ManagementFactory.newPlatformMXBeanProxy( + server, ManagementFactory.THREAD_MXBEAN_NAME, ThreadMXBean.class); + final File output = new File(args[1]); + + + CLISampler s = new CLISampler(threadMXBean, output); + s.start(); + System.out.println("Press enter to generate sample into " + output); + System.in.read(); + s.stop(); + System.out.println(); + System.out.println("Sample written to " + output); + System.exit(0); + } + + private CLISampler(ThreadMXBean threadBean, File out) { + super("CLISampler"); + threadMXBean = threadBean; + output = out; + } + + @Override + protected ThreadMXBean getThreadMXBean() { + return threadMXBean; + } + + @Override + protected void saveSnapshot(byte[] arr) throws IOException { + FileOutputStream os = new FileOutputStream(output); + os.write(arr); + os.close(); + } + + @Override + protected void printStackTrace(Throwable ex) { + ex.printStackTrace(); + System.exit(2); + } + + @Override + protected void openProgress(int steps) { + } + + @Override + protected void closeProgress() { + } + + @Override + protected void progress(int i) { + System.out.print("#"); + System.out.flush(); + } +} diff --git a/sampler/src/org/netbeans/modules/sampler/InternalSampler.java b/sampler/src/org/netbeans/modules/sampler/InternalSampler.java new file mode 100644 --- /dev/null +++ b/sampler/src/org/netbeans/modules/sampler/InternalSampler.java @@ -0,0 +1,214 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ +package org.netbeans.modules.sampler; + +import java.awt.EventQueue; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; +import java.lang.management.ThreadMXBean; +import java.text.MessageFormat; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.netbeans.api.actions.Openable; +import org.netbeans.api.progress.ProgressHandle; +import org.netbeans.api.progress.ProgressHandleFactory; +import org.openide.DialogDisplayer; +import org.openide.NotifyDescriptor; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; +import org.openide.loaders.DataObject; +import org.openide.util.Exceptions; +import org.openide.util.NbBundle; + +/** + * + * @author Tomas Hurka + */ +final class InternalSampler extends Sampler { + private static final String SAVE_MSG = NbBundle.getMessage(InternalSampler.class, "SelfSamplerAction_SavedFile"); // NOI18N + private static Class defaultDataObject; + private static final String DEBUG_ARG = "-Xdebug"; // NOI18N + private static final Logger LOGGER = Logger.getLogger(InternalSampler.class.getName()); + private static Boolean debugMode; + private static String lastReason; + + private ProgressHandle progress; + static { + try { + defaultDataObject = Class.forName("org.openide.loaders.DefaultDataObject"); // NOI18N + } catch (ClassNotFoundException ex) { + Exceptions.printStackTrace(ex); + } + } + + static InternalSampler getInternalSampler(String key) { + if (SamplesOutputStream.isSupported() && isRunMode()) { + return new InternalSampler(key); + } + return null; + } + + private static synchronized boolean isDebugged() { + if (debugMode == null) { + debugMode = Boolean.FALSE; + + // check if we are debugged + RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean(); + List args = runtime.getInputArguments(); + if (args.contains(DEBUG_ARG)) { + debugMode = Boolean.TRUE; + } + } + return debugMode.booleanValue(); + } + + private static boolean isRunMode() { + boolean runMode = true; + String reason = null; + + if (isDebugged()) { + reason = "running in debug mode"; // NOI18N + runMode = false; + } + if (runMode) { + // check if netbeans is profiled + try { + Class.forName("org.netbeans.lib.profiler.server.ProfilerServer", false, ClassLoader.getSystemClassLoader()); // NO18N + reason = "running under profiler"; // NOI18N + runMode = false; + } catch (ClassNotFoundException ex) { + } + } + if (!runMode && !reason.equals(lastReason)) { + LOGGER.log(Level.INFO, "Slowness detector disabled - {0}", reason); // NOI18N + } + lastReason = reason; + return runMode; + } + + InternalSampler(String thread) { + super(thread); + } + + @Override + protected void printStackTrace(Throwable ex) { + Exceptions.printStackTrace(ex); + } + + @Override + protected void saveSnapshot(byte[] arr) throws IOException { // save snapshot + File outFile = File.createTempFile("selfsampler", SamplesOutputStream.FILE_EXT); // NOI18N + String userDir = System.getProperty("netbeans.user"); + File gestures = null; + SelfSampleVFS fs; + + outFile = FileUtil.normalizeFile(outFile); + writeToFile(outFile, arr); + if (userDir != null) { + gestures = new File(new File(new File(new File(userDir), "var"), "log"), "uigestures"); // NOI18N + } + if (gestures != null && gestures.exists()) { + fs = new SelfSampleVFS(new String[]{"selfsampler.npss", "selfsampler.log"}, new File[]{outFile, gestures}); + } else { + fs = new SelfSampleVFS(new String[]{"selfsampler.npss"}, new File[]{outFile}); + } + // open snapshot + FileObject fo = fs.findResource("selfsampler.npss"); + DataObject dobj = DataObject.find(fo); + // ugly test for DefaultDataObject + if (defaultDataObject.isAssignableFrom(dobj.getClass())) { + String msg = MessageFormat.format(SAVE_MSG, outFile.getAbsolutePath()); + DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(msg)); + } else { + dobj.getLookup().lookup(Openable.class).open(); + } + } + + private void writeToFile(File file, byte[] arr) { + try { + FileOutputStream fstream = new FileOutputStream(file); + fstream.write(arr); + fstream.close(); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } + } + + @Override + ThreadMXBean getThreadMXBean() { + return ManagementFactory.getThreadMXBean(); + } + + @Override + void openProgress(final int steps) { + if (EventQueue.isDispatchThread()) { + // log warnining + return; + } + progress = ProgressHandleFactory.createHandle(NbBundle.getMessage(InternalSampler.class, "Save_Progress")); + progress.start(steps); + } + + @Override + void closeProgress() { + if (EventQueue.isDispatchThread()) { + return; + } + progress.finish(); + progress = null; + } + + @Override + void progress(int i) { + if (EventQueue.isDispatchThread()) { + return; + } + if (progress != null) { + progress.progress(i); + } + } + +} diff --git a/core.ui/src/org/netbeans/core/ui/sampler/Sampler.java b/sampler/src/org/netbeans/modules/sampler/Sampler.java rename from core.ui/src/org/netbeans/core/ui/sampler/Sampler.java rename to sampler/src/org/netbeans/modules/sampler/Sampler.java --- a/core.ui/src/org/netbeans/core/ui/sampler/Sampler.java +++ b/sampler/src/org/netbeans/modules/sampler/Sampler.java @@ -39,32 +39,32 @@ * * Portions Copyrighted 2008 Sun Microsystems, Inc. */ -package org.netbeans.core.ui.sampler; +package org.netbeans.modules.sampler; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; -import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; -import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.util.Timer; import java.util.TimerTask; import java.util.logging.Level; import java.util.logging.Logger; -import javax.management.MBeanServerConnection; -import javax.management.remote.JMXConnector; -import javax.management.remote.JMXConnectorFactory; -import javax.management.remote.JMXServiceURL; +import javax.swing.SwingUtilities; +import org.netbeans.api.annotations.common.CheckForNull; +import org.netbeans.api.annotations.common.NonNull; /** - * + * Sampler class provides API for self-sampling of NetBeans + * platform application. The self-sampling should be used for + * diagnostic purposes and should help NetBeans platform developers + * with solving CPU related performance problems. Sampled data are + * stored in NPSS file, which can be opened by NetBeans Profiler or + * Java VisualVM for later analysis of sampled data. + * * @author Jaroslav Bachorik, Tomas Hurka, Jaroslav Tulach */ -abstract class Sampler implements Runnable, ActionListener { +public abstract class Sampler { private static final int SAMPLER_RATE = 10; private static final double MAX_AVERAGE = SAMPLER_RATE * 3; private static final double MAX_STDDEVIATION = SAMPLER_RATE * 4; @@ -87,6 +87,35 @@ private volatile boolean stopped; private volatile boolean running; + /** + * Factory method for creating Sampler suitable for automatic reporting + * of the slow operation (i.e. AWT blocked for some time). This method can + * return null if the sampled application is in nonstandard mode + * which will produce unrealistic data - for example application is + * running under debugger or profiler. + * @param name which identifies the sampler thread + * @return instance of the {@link Sampler} or null if application + * is in nonstandard mode. + */ + public static @CheckForNull Sampler getSampler(@NonNull String name) { + return InternalSampler.getInternalSampler(name); + } + + /** + * Factory method for creating Sampler suitable for manual or user invoked + * scanning. This method can return null if it is running on + * some exotic variant of JDK, where sampling is not supported. + * @param name which identifies the sampler thread + * @return instance of the {@link Sampler} or null if sampling + * is not supported. + */ + public static @CheckForNull Sampler getGenericSampler(@NonNull String name) { + if (SamplesOutputStream.isSupported()) { + return new InternalSampler(name); + } + return null; + } + Sampler(String n) { name = n; } @@ -94,25 +123,25 @@ /** Returns the bean to use for sampling. * @return instance of the bean to take thread dumps from */ - protected abstract ThreadMXBean getThreadMXBean(); + abstract ThreadMXBean getThreadMXBean(); /** Allows subclasses to handle created snapshot * @param arr the content of the snapshot * @throws IOException thrown in case of I/O error */ - protected abstract void saveSnapshot(byte[] arr) throws IOException; + abstract void saveSnapshot(byte[] arr) throws IOException; /** How to report an exception. * * @param ex exception */ - protected abstract void printStackTrace(Throwable ex); + abstract void printStackTrace(Throwable ex); /** Methods for displaying progress. */ - protected abstract void openProgress(int steps); - protected abstract void closeProgress(); - protected abstract void progress(int i); + abstract void openProgress(int steps); + abstract void closeProgress(); + abstract void progress(int i); private void updateStats(long timestamp) { if (laststamp != 0) { @@ -129,9 +158,13 @@ laststamp = timestamp; } - @Override - public synchronized void run() { - assert !running; + /** + * Start self-sampling. This method starts timer identified by name + * for actual sampling and returns immediately. + */ + public synchronized void start() { + if (running) throw new IllegalStateException("sampling is already running"); // NOI18N + if (stopped) throw new IllegalStateException("it is not possible to restart sampling"); // NOI18N running = true; final ThreadMXBean threadBean = getThreadMXBean(); out = new ByteArrayOutputStream(64 * 1024); @@ -165,21 +198,51 @@ }, SAMPLER_RATE, SAMPLER_RATE); } - @Override - public synchronized void actionPerformed(ActionEvent e) { + /** + * Cancels the self-sampling. All sampled data are discarded. + */ + public void cancel() { + stopSampling(true, null); + } + + /** + * Stop the self-sampling started by {@link #start()} method and writes the data to + * {@link DataOutputStream}. If the internal sampling logic detects that + * the data are distorted for example due to heavy system I/O, collected + * samples are discarded and nothing is written to {@link DataOutputStream}. + *
+ * This method can take a long time and should not be invoked from EDT. + * @param dos {@link DataOutputStream} where sampled data is written to. + */ + public void stopAndWriteTo(@NonNull DataOutputStream dos) { + stopSampling(false, dos); + } + + /** + * Stop the self-sampling, save and open gathered data. If there is no + * sampled data, this method does nothing and returns immediately. + *
+ * This method can take a long time and should not be invoked from EDT. + */ + public void stop() { + stopSampling(false, null); + } + + private synchronized void stopSampling(boolean cancel, DataOutputStream dos) { try { - assert running; - assert !stopped; + if (!running) throw new IllegalStateException("sampling was not started"); // NOI18N + if (stopped) throw new IllegalStateException("sampling is not running"); // NOI18N stopped = true; timer.cancel(); - if ("cancel".equals(e.getActionCommand()) || samples < 1) { // NOi18N + if (cancel || samples < 1) { return; } + if (SwingUtilities.isEventDispatchThread()) throw new IllegalStateException("sampling cannot be stopped from EDT"); double average = sum / samples; double std_deviation = Math.sqrt(devSquaresSum / samples); - boolean writeCommand = "write".equals(e.getActionCommand()); // NOI18N + boolean writeCommand = dos != null; if (writeCommand) { - Object[] params = new Object[]{startTime, "Samples", samples, "Average", average, "Minimum", min, "Maximum", max, "Std. deviation", std_deviation}; + Object[] params = new Object[]{startTime, "Samples", samples, "Average", average, "Minimum", min, "Maximum", max, "Std. deviation", std_deviation}; // NOI18N Logger.getLogger("org.netbeans.ui.performance").log(Level.CONFIG, "Snapshot statistics", params); // NOI18N if (average > MAX_AVERAGE || std_deviation > MAX_STDDEVIATION || samples < MIN_SAMPLES) { // do not take snapshot if the sampling was not regular enough @@ -189,7 +252,6 @@ samplesStream.close(); samplesStream = null; if (writeCommand) { - DataOutputStream dos = (DataOutputStream) e.getSource(); dos.write(out.toByteArray()); dos.close(); return; @@ -202,101 +264,5 @@ out = null; samplesStream = null; } - } - - // - // Support for sampling from command line - // - - public static void main(String... args) throws Exception { - if (args.length != 2) { - System.out.println("Usage: "); - System.out.println(); - System.out.println("First of all start your application with following parameters:"); - System.out.println(" -Dcom.sun.management.jmxremote.authenticate=false"); - System.out.println(" -Dcom.sun.management.jmxremote.ssl=false"); - System.out.println(" -Dcom.sun.management.jmxremote.port="); - System.out.println("Then you can start this sampler with correct port and file to write snapshot to."); - System.exit(1); - } - - String u = args[0]; - try { - u = "service:jmx:rmi:///jndi/rmi://localhost:" + Integer.parseInt(args[0]) + "/jmxrmi"; - } catch (NumberFormatException ex) { - // OK, use args[0] - } - - System.err.println("Connecting to " + u); - JMXServiceURL url = new JMXServiceURL(u); - JMXConnector jmxc = null; - Exception ex = null; - for (int i = 0; i < 100; i++) { - try { - jmxc = JMXConnectorFactory.connect(url, null); - break; - } catch (IOException e) { - ex = e; - System.err.println("Connection failed. Will retry in 300ms."); - Thread.sleep(300); - } - } - if (jmxc == null) { - ex.printStackTrace(); - System.err.println("Cannot connect to " + u); - System.exit(3); - } - MBeanServerConnection server = jmxc.getMBeanServerConnection(); - - final ThreadMXBean threadMXBean = ManagementFactory.newPlatformMXBeanProxy( - server,ManagementFactory.THREAD_MXBEAN_NAME,ThreadMXBean.class - ); - final File output = new File(args[1]); - class CLISampler extends Sampler { - CLISampler() { - super(""); - } - - @Override - protected ThreadMXBean getThreadMXBean() { - return threadMXBean; - } - - @Override - protected void saveSnapshot(byte[] arr) throws IOException { - FileOutputStream os = new FileOutputStream(output); - os.write(arr); - os.close(); - } - - @Override - protected void printStackTrace(Throwable ex) { - ex.printStackTrace(); - System.exit(2); - } - - @Override - protected void openProgress(int steps) { - } - - @Override - protected void closeProgress() { - } - - @Override - protected void progress(int i) { - System.out.print("#"); - System.out.flush(); - } - } - - CLISampler s = new CLISampler(); - s.run(); - System.out.println("Press enter to generate sample into " + output); - System.in.read(); - s.actionPerformed(new ActionEvent(s, 0, "")); - System.out.println(); - System.out.println("Sample written to " + output); - System.exit(0); - } + } } diff --git a/core.ui/src/org/netbeans/core/ui/sampler/SamplesOutputStream.java b/sampler/src/org/netbeans/modules/sampler/SamplesOutputStream.java rename from core.ui/src/org/netbeans/core/ui/sampler/SamplesOutputStream.java rename to sampler/src/org/netbeans/modules/sampler/SamplesOutputStream.java --- a/core.ui/src/org/netbeans/core/ui/sampler/SamplesOutputStream.java +++ b/sampler/src/org/netbeans/modules/sampler/SamplesOutputStream.java @@ -40,7 +40,7 @@ * Portions Copyrighted 2010 Sun Microsystems, Inc. */ -package org.netbeans.core.ui.sampler; +package org.netbeans.modules.sampler; import java.io.IOException; import java.io.ObjectOutputStream; @@ -64,14 +64,14 @@ * * @author Tomas Hurka */ -public class SamplesOutputStream { +class SamplesOutputStream { private static final String[][] methods = new String[][]{ {"sun.management.ThreadInfoCompositeData", "toCompositeData"}, // NOI18N Sun JVM {"com.ibm.lang.management.ManagementUtils", "toThreadInfoCompositeData"} // NOI18N IBM J9 }; static final String ID = "NPSS"; // NetBeans Profiler samples stream - static final String FILE_EXT = ".npss"; // NOI18N + public static final String FILE_EXT = ".npss"; // NOI18N static final int RESET_THRESHOLD = 5000; static final int STEPS = 1000; static byte version = 2; @@ -101,11 +101,11 @@ int maxSamples; int offset; - static boolean isSupported() { + public static boolean isSupported() { return toCompositeDataMethod != null; } - public SamplesOutputStream(OutputStream os, Sampler progress, int max) throws IOException { + SamplesOutputStream(OutputStream os, Sampler progress, int max) throws IOException { maxSamples = max; this.progress = progress; outStream = os; @@ -116,7 +116,7 @@ samples = new ArrayList(1024); } - public void writeSample(ThreadInfo[] infos, long time, long selfThreadId) throws IOException { + void writeSample(ThreadInfo[] infos, long time, long selfThreadId) throws IOException { List sameT = new ArrayList(); List newT = new ArrayList(); List tids = new ArrayList(); @@ -208,7 +208,7 @@ } } - public void close() throws IOException { + void close() throws IOException { steCache = null; GZIPOutputStream stream = new GZIPOutputStream(outStream, 64 * 1024); ObjectOutputStream out = new ObjectOutputStream(stream); diff --git a/core.ui/src/org/netbeans/core/ui/sampler/SelfSampleVFS.java b/sampler/src/org/netbeans/modules/sampler/SelfSampleVFS.java rename from core.ui/src/org/netbeans/core/ui/sampler/SelfSampleVFS.java rename to sampler/src/org/netbeans/modules/sampler/SelfSampleVFS.java --- a/core.ui/src/org/netbeans/core/ui/sampler/SelfSampleVFS.java +++ b/sampler/src/org/netbeans/modules/sampler/SelfSampleVFS.java @@ -40,7 +40,7 @@ * Portions Copyrighted 2010 Sun Microsystems, Inc. */ -package org.netbeans.core.ui.sampler; +package org.netbeans.modules.sampler; import java.io.File; import java.io.FileInputStream; diff --git a/sampler/test/unit/src/org/netbeans/modules/sampler/CustomSamplesStream.java b/sampler/test/unit/src/org/netbeans/modules/sampler/CustomSamplesStream.java new file mode 100644 --- /dev/null +++ b/sampler/test/unit/src/org/netbeans/modules/sampler/CustomSamplesStream.java @@ -0,0 +1,68 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ +package org.netbeans.modules.sampler; + +import java.io.IOException; +import java.io.OutputStream; +import java.lang.management.ThreadInfo; + +/** + * + * @author Tomas Hurka + */ +public class CustomSamplesStream extends SamplesOutputStream { + + public CustomSamplesStream(OutputStream os, int max) throws IOException { + super(os, null, max); + } + + @Override + public void writeSample(ThreadInfo[] infos, long time, long selfThreadId) throws IOException { + super.writeSample(infos, time, selfThreadId); + } + + @Override + public void close() throws IOException { + super.close(); + } + +} diff --git a/sampler/test/unit/src/org/netbeans/modules/sampler/SamplerTest.java b/sampler/test/unit/src/org/netbeans/modules/sampler/SamplerTest.java new file mode 100644 --- /dev/null +++ b/sampler/test/unit/src/org/netbeans/modules/sampler/SamplerTest.java @@ -0,0 +1,189 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ +package org.netbeans.modules.sampler; + +import java.awt.Dialog; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import org.junit.*; +import static org.junit.Assert.*; +import org.netbeans.junit.MockServices; +import org.netbeans.junit.NbTestCase; +import org.openide.DialogDescriptor; +import org.openide.DialogDisplayer; +import org.openide.NotifyDescriptor; +import org.openide.util.Exceptions; + +/** + * + * @author Tomas Hurka + */ +public class SamplerTest { + + @BeforeClass + public static void setUpClass() throws Exception { + //register DialogDisplayer which "pushes" Yes option in the document save dialog + MockServices.setServices(DD.class); + } + + @AfterClass + public static void tearDownClass() throws Exception { + } + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + /** + * Test of getSampler method, of class Sampler. + */ + @Test + public void testGetSampler() { + System.out.println("getSampler"); + String name = "test"; + Sampler result = Sampler.getSampler(name); + assertNotNull(result); + } + + /** + * Test of getGenericSampler method, of class Sampler. + */ + @Test + public void testGetGenericSampler() { + System.out.println("getGenericSampler"); + String name = "gentest"; + Sampler result = Sampler.getGenericSampler(name); + assertNotNull(result); + } + + /** + * Test of cancel method, of class Sampler. + */ + @Test + public void testCancel() { + System.out.println("cancel"); + Sampler instance = Sampler.getGenericSampler("cancel"); + instance.start(); + instance.cancel(); + } + + /** + * Test of stopAndWriteTo method, of class Sampler. + */ + @Test + public void testStopAndWriteTo() throws IOException { + System.out.println("stopAndWriteTo"); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(out); + Sampler instance = Sampler.getSampler("cancel"); + instance.start(); + instance.stopAndWriteTo(dos); + dos.close(); + // there should no data in out, since stopAndWriteTo is + // invoked immediately after start + assertTrue(out.size() == 0); + } + /** + * Test of stopAndWriteTo method, of class Sampler. + */ + @Test + public void testStopAndWriteTo1() throws IOException { + System.out.println("stopAndWriteTo1"); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(out); + Sampler instance = Sampler.getSampler("cancel"); + instance.start(); + longRunningMethod(); + instance.stopAndWriteTo(dos); + dos.close(); + // make sure we have some sampling data + assertTrue(out.size() > 0); + } + + private void longRunningMethod() { + for (int i=0; i<100;i++) { + try { + Thread.sleep(30); + } catch (InterruptedException ex) { + Exceptions.printStackTrace(ex); + } + } + } + + /** + * Test of stop method, of class Sampler. + */ + @Test + public void testStop() { + System.out.println("stop"); + Sampler instance = Sampler.getGenericSampler("stop"); + DD.hasData = false; + instance.start(); + longRunningMethod(); + instance.stop(); + assert(DD.hasData); + } + + /** Our own dialog displayer. + */ + public static final class DD extends DialogDisplayer { + static boolean hasData; + + @Override + public Dialog createDialog(DialogDescriptor descriptor) { + throw new IllegalStateException ("Not implemented"); + } + + @Override + public Object notify(NotifyDescriptor descriptor) { + hasData = true; + return null; + } + + } // end of DD + +} diff --git a/core.ui/test/unit/src/org/netbeans/core/ui/sampler/SelfSampleVFSTest.java b/sampler/test/unit/src/org/netbeans/modules/sampler/SelfSampleVFSTest.java rename from core.ui/test/unit/src/org/netbeans/core/ui/sampler/SelfSampleVFSTest.java rename to sampler/test/unit/src/org/netbeans/modules/sampler/SelfSampleVFSTest.java --- a/core.ui/test/unit/src/org/netbeans/core/ui/sampler/SelfSampleVFSTest.java +++ b/sampler/test/unit/src/org/netbeans/modules/sampler/SelfSampleVFSTest.java @@ -40,7 +40,7 @@ * Portions Copyrighted 2010 Sun Microsystems, Inc. */ -package org.netbeans.core.ui.sampler; +package org.netbeans.modules.sampler; import java.io.File; import java.io.FileOutputStream;