Skip to main content

Slowness Detection in Netbeans RCP Applications

Posted by fvo on November 22, 2010 at 4:40 PM PST

NetBeans Platform lab at Devoxx 2010

Last week at Devoxx 2010, I had the pleasure to be part of the 3 hour NetBeans Platform lab by Geertjan Wielenga. During the "Advanced Random Stuff" part I showed how the slowness detection feature of the Netbeans IDE can be used in Netbeans RCP applications. This post describes how it works.

What means slowness detection?

Please see following links.

http://wiki.netbeans.org/FitnessViaPostMortem
http://wiki.netbeans.org/Fitness
http://wiki.netbeans.org/Performance

How works slowness detection in the Netbeans RCP / IDE?

The Netbeans RCP uses it's own EventQueue implementation, which has a separate thread observing the EventDispatchThread. If dispatching of an event tooks to long, the observer thread starts self sampling using JMX. After the event has been dispatched, the sampling result is written to a (java) Logger with name "org.netbeans.ui". To process these slowness data in the way you need, just implement a java.util.logging.Handler, register it with name "org.netbeans.ui" and grap the right LogRecords.

Implementation of a simple slowness detector

  1. Create a module project
  2. Create a class and extend java.util.logging.Handler (e.g. called SlownessCatcher). Implement the publish method and catch the slowness log records. Extract the self sampling snapshot data (.npss)
  3. package be.devoxx.netbeans.slowness;

    import java.util.logging.Handler;
    import java.util.logging.Level;
    import java.util.logging.LogRecord;
    import javax.swing.ImageIcon;
    import org.openide.awt.NotificationDisplayer;
    import org.openide.util.ImageUtilities;

    /**
    * Catch slownes log records, notify user and process it if needed.
    *
    * @author Florian Vogler
    */
    final class SlownessCatcher extends Handler {

        private static interface Singleton {

            SlownessCatcher INSTANCE = new SlownessCatcher();
        }

        public static SlownessCatcher getInstance() {
            return Singleton.INSTANCE;
        }

        private boolean accept(LogRecord record) {
            return record.getLevel().equals(Level.CONFIG) && record.getMessage().startsWith("Slowness detected");
        }

        @Override
        public void publish(LogRecord record) {
            if (accept(record)) {
                // Extract log record parameters
                Object[] params = record.getParameters();
                byte[] nps = (byte[]) params[0];
                long time = (Long) params[1];
                String slownessType = params.length > 2 ? params[2].toString() : "Unknown";
                assert nps != null : "nps param should be not null";
                assert nps.length > 0 : "nps param should not be empty";


                /* Use notification api to inform user */
               
                // ActionListener to store slowness data if requested
                SaveSlownessData data = new SaveSlownessData(slownessType, time, nps);

                // Icon belongs to netbeans codebase!
                ImageIcon icon = ImageUtilities.loadImageIcon("be/devoxx/netbeans/slowness/vilik.png", true);
                NotificationDisplayer.getDefault().notify("Slowness detected", icon, "Slowness detected for " + time + "ms", data);
            }
        }

        @Override
        public void flush() {
        }

        @Override
        public void close() throws SecurityException {
        }
    }
       
  4. Example ActionListener saving slowness data to user dir.
  5. package be.devoxx.netbeans.slowness;

    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    import javax.swing.ImageIcon;
    import org.openide.awt.NotificationDisplayer;
    import org.openide.util.Exceptions;
    import org.openide.util.ImageUtilities;

    /**
    * Stores slowness data to ${netbeans.user}/var/logs/slowness
    *
    * @author Florian Vogler
    */
    final class SaveSlownessData implements ActionListener {

        private final String slownessType;
        private final long time;
        private final byte[] snapshot;

        SaveSlownessData(String slownessType, long time, byte[] snapshot) {
            this.slownessType = slownessType;
            this.time = time;
            this.snapshot = snapshot;
        }

        @Override
        public void actionPerformed(ActionEvent e) {

            try {
                File file = createFile();
                OutputStream out = new FileOutputStream(file);
                try {
                    for (byte b : snapshot) {
                        out.write(b);
                    }
                } finally {
                    out.close();
                }

                // Icon belongs to netbeans codebase
                ImageIcon icon = ImageUtilities.loadImageIcon("be/devoxx/netbeans/slowness/vilik.png", true);
                NotificationDisplayer.getDefault().notify(
                        "Slowness data saved", icon, "Slowness data saved to " + file.getAbsolutePath(), null);
            } catch (IOException ex) {
                Exceptions.printStackTrace(ex);
            }
        }

        private File createFile() throws IOException {
            File userDir = new File(System.getProperty("netbeans.user") + "/var/log/slowness");
            if (!userDir.isDirectory()) {
                userDir.mkdirs();
            }
            File file = new File(userDir, "SlownessData-" + slownessType + ".npss");

            int i = 1;
            while (!file.createNewFile()) {
                file = new File(userDir, "SlownessData-" + slownessType + " (" + time + ") " + "-" + i++ + ".npss");
            }

            return file;
        }
    }
       
  6. Create a module installer and register the logger. (Wizard available)
  7. package be.devoxx.netbeans.slowness;

    import java.util.logging.Level;
    import java.util.logging.Logger;
    import org.openide.modules.ModuleInstall;

    /**
    * Manages a module's lifecycle. Remember that an installer is optional and
    * often not needed at all.
    *
    * @author Florian Vogler
    */
    public class Installer extends ModuleInstall {

        private static final String LOGGER_NAME = "org.netbeans.ui";

        @Override
        public void restored() {
            Logger logger = Logger.getLogger(LOGGER_NAME);
            logger.setUseParentHandlers(false);
            logger.setLevel(Level.FINEST);
            logger.addHandler(SlownessCatcher.getInstance());
        }

        @Override
        public boolean closing() {
            Logger logger = Logger.getLogger(LOGGER_NAME);
            logger.removeHandler(SlownessCatcher.getInstance());
            return true;
        }
    }
       

 

Related Topics >>

Comments

Slowness Detection in

Very informative post. Really a lot of good points. Glad I found your site. Will note it and return for more info.
...
Place your <a href="http://www.freeadsau.com">Free Ads, Free Classified, Free Ad, Classified Free Ads, Ads for Free, Free Online Advertising, Australia, AU</a>, anyone can place community or business ads on http://www.freeadsau.com