tomcat-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Britton, Chris" <Chris_Brit...@bmc.com>
Subject Re: Debugging Tomcat Crashes on start
Date Fri, 22 Sep 2000 00:07:50 GMT
There is also a related problem that is specific to the jdb delivered with
Java 2 v1.3.
Attached is a bug report that I submitted to Sun.

Hope this helps.

-- 
Chris Britton
cbritton@bmc.com

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 =====

Mime
View raw message