Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4342974

JPDA example to attach to a running VM hits NullPointerException

XMLWordPrintable

    • ladybird
    • x86
    • windows_nt
    • Not verified



        Name: rlT66838 Date: 06/02/2000


        java version "1.3.0rc3"
        Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.0rc3-Z)
        Java HotSpot(TM) Client VM (build 1.3.0rc3-Z, mixed mode)

        I encountered a problem using the JPDA example to attach to a running VM.

        I downloaded jpda1_0-win.zip.

        I run my test program, which opens a window and waits for a close event, from
        one console window:

        D:> java -classpath d:\jd\work -Djava.compiler=NONE -Xnoagent -Xdebug -
        Xrunjdwp:transport=dt_shmem,server=y,suspend=n Simple
        Java HotSpot(TM) Client VM warning: Setting of property "java.compiler" is
        ignored
        Listening for transport dt_shmem at address: javadebug

        In another console window, I compiled the example provided (package tty) in the
        jpda1_0-examples.zip file and ran TTY:

        C:\jpda\examples\debuggers>d:\jdk1.3\bin\java -classpath .;d:\jd\jpda.jar
        tty.TTY -attach javadebug
        Internal exception: java.lang.NullPointerException
                at tty.VMConnection.open(VMConnection.java:138)
                at tty.Env.init(Env.java:68)
                at tty.TTY.main(TTY.java:923)

        The code in VMConnection looks like this:

        132 synchronized VirtualMachine open() {
        133 if (connector instanceof LaunchingConnector) {
        134 vm = launchTarget();
        135 } else if (connector instanceof AttachingConnector) {
        136 vm = attachTarget();
        137 // Allow debugger operations like 'cont'
        138 ThreadInfo.setCurrentThread(ThreadInfo.getThread(1).thread);
          . . .

        It works when I try to attach using the "jdb" executable provided with jpda1_0-
        win.zip:

        C:\jpda\bin>jdb -attach javadebug
        Initializing jdb...
        Signal dispatcher[1] threads
        Group system:
        1. (java.lang.Thread)0x3 Signal dispatcher running
        2. (java.lang.ref.Reference$ReferenceHandler)0x4 Reference Handler cond. waitin
        3. (java.lang.ref.Finalizer$FinalizerThread)0x5 Finalizer cond. waitin
        Group main:
        4. (java.awt.EventDispatchThread)0x7 AWT-EventQueue-0 cond. waiting
        5. (sun.awt.PostEventQueue)0x8 PostEventQueue-0 cond. waiting
        6. (java.lang.Thread)0x9 AWT-Windows running
        7. (ProcessRunWindow$1)0xa input stream running
        8. (ProcessRunWindow$2)0xb error stream running
        9. (java.lang.Thread)0xc Thread-0 cond. waiting
        Signal dispatcher[1]


        Source for test class:

        import java.awt.*;
        /*
         * Simple generated by BuildWin
         * Generated from the file D:\jd\work\Simple.bwn
         * Fri Apr 14 12:16:41 EDT 2000
         */
        /**
         *
         */
        class Simple extends Frame {

        boolean exitOnClose=false;
        Label label1=new Label(); //
        /**
         * Test driver for the class Simple
         */

        public static void main(String[] args) {
        Simple tSimple = new Simple();
        tSimple.exitOnClose=true;
        tSimple.pack();
        // you can tSimple.setSize(width,height); here
        // you can tSimple.setLocation(x,y); here
        tSimple.show();
        }
        /**
         * Handles only the window close event
         */

        public boolean handleEvent(Event event) {
        if (event.id == Event.WINDOW_DESTROY && exitOnClose) System.exit(0);
        if (event.id == Event.WINDOW_DESTROY) dispose();
        return super.handleEvent(event);
        }

        /**
         * Constructor of a Simple GUI.
         */
        Simple() {
        setTitle("Simple");
        setForeground(Color.black);
        setBackground(Color.white);
        setFont(new Font("Courier",0,12));
        GridBagConstraints c = new GridBagConstraints();
        BorderLayout borderlayout=new BorderLayout();
        setLayout(borderlayout);
        label1.setText("Waiting for a close event");
        add(label1);
        MenuBar mb = new MenuBar();
        setMenuBar(mb);
        }
        }
        (Review ID: 105650)
        ======================================================================

        Name: tb29552 Date: 09/22/2000


        java version "1.3.0"
        Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.0-C)
        Java HotSpot(TM) Client VM (build 1.3.0-C, mixed mode)

        The jdb delivered with Java 2 SDK, Standard Edition, v1.3 will fail
        with a NullPointerException if you attempt to attach to a running VM
        and the original (main) thread is no longer running. This problem
        occurs when trying to attach to an Apache Jakarta-Tomcat server
        because the initial thread runs for only a short time -- just long
        enough to startup another thread.

        Below is a short program (TestThreads.java) to demonstrate the problem.
        It has a main thread that runs for a short time. The main thread
        creates a new thread that continues to run for 30 seconds after the
        main thread completes. To reproduce the problem:

                1. Save the sample code to a file named TestThreads.java.

                2. Compile for debugging.

                        javac -g TestThreads.java

                3. Execute the test program, starting the VM with the
                   appropriate options.

                        java -Xdebug -
        Xrunjdwp:transport=dt_shmem,address=jdbdebug,server=y,suspend=n TestThreads

                4. In another window on the same system, attempt to connect to the
                   running VM. You have to connect after the main thread exits
                   (which happens pretty much instantly) and before the new thread
                   exits (which takes about 30 seconds).

                        jdb -attach jdbdebug

                5. Note that jdb fails with the following stack trace:

                        Internal exception: java.lang.NullPointerException
                 at com.sun.tools.example.debug.tty.VMConnection.open
        (VMConnection.java:138)
        at com.sun.tools.example.debug.tty.Env.init(Env.java:68)
                 at com.sun.tools.example.debug.tty.TTY.main(TTY.java:923)

        The reason appears to be that the jdb startup code wants to set the
        thread whose id=1 (main) to be the current thread. The value of 1 is
        hardcoded. See "Orig VMConnection.open() method" and
        "ThreadInfo.getThread() method" below. However, in this instance, no
        thread with id=1 exists and getThread() returns null. The
        VMConnection.open() method does not check for a null return value from
        ThreadInfo.getThread().

        I fixed the problem by changing the startup code to simply set the
        thread with the lowest id to be the current thread. I assume that
        it's not really important which thread is initially designated as the
        current thread as long as the debugger gets control to give the user
        the opportunity to interact with the debugger. Please let me know if
        this is not correct.

        The fix is shown in "Modified VMConnection.open() method" and "New
        ThreadInfo.setDefaultThread() method" below. These are from
        com.sun.tools.example.debug.tty.VMConnection.java and
        com.sun.tools.example.debug.tty.ThreadInfo.java in
        demo/jpda/examples.jar respectively. The resulting class files
        (ThreadInfo.class, VMConnection.class, and VMConnection$1.class)
        replace the ones in tools.jar.

        Let me know if you need more info.

        ===== TestThreads.java =====
        public class TestThreads {

            public TestThreads() {

                System.err.println("Creating new thread");
                MyThread myThread = new MyThread();
                System.err.println("New thread created");

                myThread.start();

                return;
            }

            public static void main(String args[] ) {

                System.err.println("Main thread running");
                TestThreads t = new TestThreads();
                System.err.println("Main thread exiting");

                return;

            } // end method main

            class MyThread extends Thread {

                public void run() {

                    int cnt = 0;

                    System.err.println("\tNew thread running");
                    try {
                        for (int i = 0; i < 15; ++i) {
                            Thread.sleep(2000);
                            System.err.println("\tNew thread working");
                        }
                    } catch (InterruptedException e) {
                        System.err.println("\tInterruptedException");
                    }
                    System.err.println("\tNew thread exiting");

                    return;
                }

                public void start() {
                    System.err.println("\tStarting new thread");
                    super.start();
                    System.err.println("\tNew thread started");
                }

            }

        } // end class TestThreads
        ===== end TestThreads.java =====

        ===== Orig VMConnection.open() method ====
            synchronized VirtualMachine open() {
                if (connector instanceof LaunchingConnector) {
                    vm = launchTarget();
                } else if (connector instanceof AttachingConnector) {
                    vm = attachTarget();
                    // Allow debugger operations like 'cont'
                    ThreadInfo.setCurrentThread(ThreadInfo.getThread(1).thread);
                } else if (connector instanceof ListeningConnector) {
                    vm = listenTarget();
                    // Allow debugger operations like 'cont'
                    ThreadInfo.setCurrentThread(ThreadInfo.getThread(1).thread);
                } else {
                    throw new InternalError("Invalid connect type");
                }
                vm.setDebugTraceMode(traceFlags);
                setEventRequests(vm);
                resolveEventRequests();
        // Env.out.println("Connected to " + target);
                return vm;
            }
        ===== end Orig VMConnection.open() method ====

        ===== Modified VMConnection.open() method =====
            synchronized VirtualMachine open() {

                if (connector instanceof LaunchingConnector) {
                    vm = launchTarget();
                } else {
                    if (connector instanceof AttachingConnector) {
                        vm = attachTarget();
                    } else if (connector instanceof ListeningConnector) {
                        vm = listenTarget();
                    } else {
                        throw new InternalError("Invalid connect type");
                    }
                    // Allow debugger operations like 'cont'
                    ThreadInfo.setDefaultThread();
                }
                vm.setDebugTraceMode(traceFlags);
                setEventRequests(vm);
                resolveEventRequests();
        // Env.out.println("Connected to " + target);
                return vm;
            }
        ===== end Modified VMConnection.open() method =====

        ===== ThreadInfo.getThread() method =====
            static ThreadInfo getThread(long id) {
                ThreadInfo retInfo = null;

                synchronized (threads) {
                    Iterator iter = threads().iterator();
                    while (iter.hasNext()) {
                        ThreadInfo ti = (ThreadInfo)iter.next();
                        if (ti.thread.uniqueID() == id) {
                           retInfo = ti;
                           break;
                        }
                    }
                }
                return retInfo;
            }
        ===== end ThreadInfo.getThread() method =====

        ===== New ThreadInfo.setDefaultThread() method =====
            static void setDefaultThread() {

                long threadId = -1;
                ThreadInfo threadInfo = null;

                synchronized (threads) {
                    Iterator iter = threads().iterator();
                    while (iter.hasNext()) {
                        ThreadInfo ti = (ThreadInfo)iter.next();
                        if ((-1 == threadId) || (ti.thread.uniqueID() < threadId)) {
                            threadId = ti.thread.uniqueID();
                            threadInfo = ti;
                        }
                    }
                }

                // No need to throw exception here. A VMNotConnectedException will
                // be thrown later when we actually try to use currentThread.
                // cmb 21-Sep-2000
                if (null != threadInfo) {
                    ThreadInfo.setCurrentThread(threadInfo.thread);
                }

                return;
            }
        ===== end New ThreadInfo.setDefaultThread() method =====
        (Review ID: 109895)
        ======================================================================

              tbell Tim Bell
              rlewis Roger Lewis (Inactive)
              Votes:
              0 Vote for this issue
              Watchers:
              0 Start watching this issue

                Created:
                Updated:
                Resolved:
                Imported:
                Indexed: