346 lines · 12.0 KB · Java
RawDownload
/* * Copyright (c) 1998, 2008, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.tools.example.debug.gui; import java.util.*;import java.util.List;  // Must import explicitly due to conflict with javax.awt.List import javax.swing.*;import javax.swing.tree.*;import java.awt.*;import java.awt.event.*; import com.sun.jdi.*;import com.sun.tools.example.debug.event.*;import com.sun.tools.example.debug.bdi.*; //### Bug: If the name of a thread is changed via Thread.setName(), the//### thread tree view does not reflect this.  The name of the thread at//### the time it is created is used throughout its lifetime. public class ThreadTreeTool extends JPanel {     private static final long serialVersionUID = 4168599992853038878L;     private Environment env;     private ExecutionManager runtime;    private SourceManager sourceManager;    private ClassManager classManager;     private JTree tree;    private DefaultTreeModel treeModel;    private ThreadTreeNode root;    private SearchPath sourcePath;     private CommandInterpreter interpreter;     private static String HEADING = "THREADS";     public ThreadTreeTool(Environment env) {         super(new BorderLayout());         this.env = env;        this.runtime = env.getExecutionManager();        this.sourceManager = env.getSourceManager();         this.interpreter = new CommandInterpreter(env);         root = createThreadTree(HEADING);        treeModel = new DefaultTreeModel(root);         // Create a tree that allows one selection at a time.         tree = new JTree(treeModel);        tree.setSelectionModel(new SingleLeafTreeSelectionModel());         MouseListener ml = new MouseAdapter() {            @Override            public void mouseClicked(MouseEvent e) {                int selRow = tree.getRowForLocation(e.getX(), e.getY());                TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());                if(selRow != -1) {                    if(e.getClickCount() == 1) {                        ThreadTreeNode node =                            (ThreadTreeNode)selPath.getLastPathComponent();                        // If user clicks on leaf, select it, and issue 'thread' command.                        if (node.isLeaf()) {                            tree.setSelectionPath(selPath);                            interpreter.executeCommand("thread " +                                                       node.getThreadId() +                                                       "  (\"" +                                                       node.getName() + "\")");                        }                    }                }            }        };         tree.addMouseListener(ml);         JScrollPane treeView = new JScrollPane(tree);        add(treeView);         // Create listener.        ThreadTreeToolListener listener = new ThreadTreeToolListener();        runtime.addJDIListener(listener);        runtime.addSessionListener(listener);         //### remove listeners on exit!    }     HashMap<ThreadReference, List<String>> threadTable = new HashMap<ThreadReference, List<String>>();     private List<String> threadPath(ThreadReference thread) {        // May exit abnormally if VM disconnects.        List<String> l = new ArrayList<String>();        l.add(0, thread.name());        ThreadGroupReference group = thread.threadGroup();        while (group != null) {            l.add(0, group.name());            group = group.parent();        }        return l;    }     private class ThreadTreeToolListener extends JDIAdapter                              implements JDIListener, SessionListener {         // SessionListener         @Override        public void sessionStart(EventObject e) {            try {                for (ThreadReference thread : runtime.allThreads()) {                    root.addThread(thread);                }            } catch (VMDisconnectedException ee) {                // VM went away unexpectedly.            } catch (NoSessionException ee) {                // Ignore.  Should not happen.            }        }         @Override        public void sessionInterrupt(EventObject e) {}        @Override        public void sessionContinue(EventObject e) {}          // JDIListener         @Override        public void threadStart(ThreadStartEventSet e) {            root.addThread(e.getThread());        }         @Override        public void threadDeath(ThreadDeathEventSet e) {            root.removeThread(e.getThread());        }         @Override        public void vmDisconnect(VMDisconnectEventSet e) {            // Clear the contents of this view.            root = createThreadTree(HEADING);            treeModel = new DefaultTreeModel(root);            tree.setModel(treeModel);            threadTable = new HashMap<ThreadReference, List<String>>();        }     }     ThreadTreeNode createThreadTree(String label) {        return new ThreadTreeNode(label, null);    }     class ThreadTreeNode extends DefaultMutableTreeNode {         String name;        ThreadReference thread; // null if thread group        long uid;        String description;         ThreadTreeNode(String name, ThreadReference thread) {            if (name == null) {                name = "<unnamed>";            }            this.name = name;            this.thread = thread;            if (thread == null) {                this.uid = -1;                this.description = name;            } else {                this.uid = thread.uniqueID();                this.description = name + " (t@" + Long.toHexString(uid) + ")";            }        }         @Override        public String toString() {            return description;        }         public String getName() {            return name;        }         public ThreadReference getThread() {            return thread;        }         public String getThreadId() {            return "t@" + Long.toHexString(uid);        }         private boolean isThreadGroup() {            return (thread == null);        }         @Override        public boolean isLeaf() {            return !isThreadGroup();        }         public void addThread(ThreadReference thread) {            // This can fail if the VM disconnects.            // It is important to do all necessary JDI calls            // before modifying the tree, so we don't abort            // midway through!            if (threadTable.get(thread) == null) {                // Add thread only if not already present.                try {                    List<String> path = threadPath(thread);                    // May not get here due to exception.                    // If we get here, we are committed.                    // We must not leave the tree partially updated.                    try {                        threadTable.put(thread, path);                        addThread(path, thread);                    } catch (Throwable tt) {                        //### Assertion failure.                        throw new RuntimeException("ThreadTree corrupted");                    }                } catch (VMDisconnectedException ee) {                    // Ignore.  Thread will not be added.                }            }        }         private void addThread(List<String> threadPath, ThreadReference thread) {            int size = threadPath.size();            if (size == 0) {                return;            } else if (size == 1) {                String name = threadPath.get(0);                insertNode(name, thread);            } else {                String head = threadPath.get(0);                List<String> tail = threadPath.subList(1, size);                ThreadTreeNode child = insertNode(head, null);                child.addThread(tail, thread);            }        }         private ThreadTreeNode insertNode(String name, ThreadReference thread) {            for (int i = 0; i < getChildCount(); i++) {                ThreadTreeNode child = (ThreadTreeNode)getChildAt(i);                int cmp = name.compareTo(child.getName());                if (cmp == 0 && thread == null) {                    // A like-named interior node already exists.                    return child;                } else if (cmp < 0) {                    // Insert new node before the child.                    ThreadTreeNode newChild = new ThreadTreeNode(name, thread);                    treeModel.insertNodeInto(newChild, this, i);                    return newChild;                }            }            // Insert new node after last child.            ThreadTreeNode newChild = new ThreadTreeNode(name, thread);            treeModel.insertNodeInto(newChild, this, getChildCount());            return newChild;        }         public void removeThread(ThreadReference thread) {            List<String> threadPath = threadTable.get(thread);            // Only remove thread if we recorded it in table.            // Original add may have failed due to VM disconnect.            if (threadPath != null) {                removeThread(threadPath, thread);            }        }         private void removeThread(List<String> threadPath, ThreadReference thread) {            int size = threadPath.size();            if (size == 0) {                return;            } else if (size == 1) {                String name = threadPath.get(0);                ThreadTreeNode child = findLeafNode(thread, name);                treeModel.removeNodeFromParent(child);            } else {                String head = threadPath.get(0);                List<String> tail = threadPath.subList(1, size);                ThreadTreeNode child = findInternalNode(head);                child.removeThread(tail, thread);                if (child.isThreadGroup() && child.getChildCount() < 1) {                    // Prune non-leaf nodes with no children.                    treeModel.removeNodeFromParent(child);                }            }        }         private ThreadTreeNode findLeafNode(ThreadReference thread, String name) {            for (int i = 0; i < getChildCount(); i++) {                ThreadTreeNode child = (ThreadTreeNode)getChildAt(i);                if (child.getThread() == thread) {                    if (!name.equals(child.getName())) {                        //### Assertion failure.                        throw new RuntimeException("name mismatch");                    }                    return child;                }            }            //### Assertion failure.            throw new RuntimeException("not found");        }         private ThreadTreeNode findInternalNode(String name) {            for (int i = 0; i < getChildCount(); i++) {                ThreadTreeNode child = (ThreadTreeNode)getChildAt(i);                if (name.equals(child.getName())) {                    return child;                }            }            //### Assertion failure.            throw new RuntimeException("not found");        }     } }