344 lines · 12.1 KB · Java
RawDownload
/* * Copyright (c) 2001, 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.trace; import com.sun.jdi.*;import com.sun.jdi.request.*;import com.sun.jdi.event.*; import java.util.*;import java.io.PrintWriter; /** * This class processes incoming JDI events and displays them * * @author Robert Field */public class EventThread extends Thread {     private final VirtualMachine vm;   // Running VM    private final String[] excludes;   // Packages to exclude    private final PrintWriter writer;  // Where output goes     static String nextBaseIndent = ""; // Starting indent for next thread     private boolean connected = true;  // Connected to VM    private boolean vmDied = true;     // VMDeath occurred     // Maps ThreadReference to ThreadTrace instances    private Map<ThreadReference, ThreadTrace> traceMap =       new HashMap<>();     EventThread(VirtualMachine vm, String[] excludes, PrintWriter writer) {        super("event-handler");        this.vm = vm;        this.excludes = excludes;        this.writer = writer;    }     /**     * Run the event handling thread.     * As long as we are connected, get event sets off     * the queue and dispatch the events within them.     */    @Override    public void run() {        EventQueue queue = vm.eventQueue();        while (connected) {            try {                EventSet eventSet = queue.remove();                EventIterator it = eventSet.eventIterator();                while (it.hasNext()) {                    handleEvent(it.nextEvent());                }                eventSet.resume();            } catch (InterruptedException exc) {                // Ignore            } catch (VMDisconnectedException discExc) {                handleDisconnectedException();                break;            }        }    }     /**     * Create the desired event requests, and enable     * them so that we will get events.     * @param excludes     Class patterns for which we don't want events     * @param watchFields  Do we want to watch assignments to fields     */    void setEventRequests(boolean watchFields) {        EventRequestManager mgr = vm.eventRequestManager();         // want all exceptions        ExceptionRequest excReq = mgr.createExceptionRequest(null,                                                             true, true);        // suspend so we can step        excReq.setSuspendPolicy(EventRequest.SUSPEND_ALL);        excReq.enable();         MethodEntryRequest menr = mgr.createMethodEntryRequest();        for (int i=0; i<excludes.length; ++i) {            menr.addClassExclusionFilter(excludes[i]);        }        menr.setSuspendPolicy(EventRequest.SUSPEND_NONE);        menr.enable();         MethodExitRequest mexr = mgr.createMethodExitRequest();        for (int i=0; i<excludes.length; ++i) {            mexr.addClassExclusionFilter(excludes[i]);        }        mexr.setSuspendPolicy(EventRequest.SUSPEND_NONE);        mexr.enable();         ThreadDeathRequest tdr = mgr.createThreadDeathRequest();        // Make sure we sync on thread death        tdr.setSuspendPolicy(EventRequest.SUSPEND_ALL);        tdr.enable();         if (watchFields) {            ClassPrepareRequest cpr = mgr.createClassPrepareRequest();            for (int i=0; i<excludes.length; ++i) {                cpr.addClassExclusionFilter(excludes[i]);            }            cpr.setSuspendPolicy(EventRequest.SUSPEND_ALL);            cpr.enable();        }    }     /**     * This class keeps context on events in one thread.     * In this implementation, context is the indentation prefix.     */    class ThreadTrace {        final ThreadReference thread;        final String baseIndent;        static final String threadDelta = "                     ";        StringBuffer indent;         ThreadTrace(ThreadReference thread) {            this.thread = thread;            this.baseIndent = nextBaseIndent;            indent = new StringBuffer(baseIndent);            nextBaseIndent += threadDelta;            println("====== " + thread.name() + " ======");        }         private void println(String str) {            writer.print(indent);            writer.println(str);        }         void methodEntryEvent(MethodEntryEvent event)  {            println(event.method().name() + "  --  "                    + event.method().declaringType().name());            indent.append("| ");        }         void methodExitEvent(MethodExitEvent event)  {            indent.setLength(indent.length()-2);        }         void fieldWatchEvent(ModificationWatchpointEvent event)  {            Field field = event.field();            Value value = event.valueToBe();            println("    " + field.name() + " = " + value);        }         void exceptionEvent(ExceptionEvent event) {            println("Exception: " + event.exception() +                    " catch: " + event.catchLocation());             // Step to the catch            EventRequestManager mgr = vm.eventRequestManager();            StepRequest req = mgr.createStepRequest(thread,                                                    StepRequest.STEP_MIN,                                                    StepRequest.STEP_INTO);            req.addCountFilter(1);  // next step only            req.setSuspendPolicy(EventRequest.SUSPEND_ALL);            req.enable();        }         // Step to exception catch        void stepEvent(StepEvent event)  {            // Adjust call depth            int cnt = 0;            indent = new StringBuffer(baseIndent);            try {                cnt = thread.frameCount();            } catch (IncompatibleThreadStateException exc) {            }            while (cnt-- > 0) {                indent.append("| ");            }             EventRequestManager mgr = vm.eventRequestManager();            mgr.deleteEventRequest(event.request());        }         void threadDeathEvent(ThreadDeathEvent event)  {            indent = new StringBuffer(baseIndent);            println("====== " + thread.name() + " end ======");        }    }     /**     * Returns the ThreadTrace instance for the specified thread,     * creating one if needed.     */    ThreadTrace threadTrace(ThreadReference thread) {        ThreadTrace trace = traceMap.get(thread);        if (trace == null) {            trace = new ThreadTrace(thread);            traceMap.put(thread, trace);        }        return trace;    }     /**     * Dispatch incoming events     */    private void handleEvent(Event event) {        if (event instanceof ExceptionEvent) {            exceptionEvent((ExceptionEvent)event);        } else if (event instanceof ModificationWatchpointEvent) {            fieldWatchEvent((ModificationWatchpointEvent)event);        } else if (event instanceof MethodEntryEvent) {            methodEntryEvent((MethodEntryEvent)event);        } else if (event instanceof MethodExitEvent) {            methodExitEvent((MethodExitEvent)event);        } else if (event instanceof StepEvent) {            stepEvent((StepEvent)event);        } else if (event instanceof ThreadDeathEvent) {            threadDeathEvent((ThreadDeathEvent)event);        } else if (event instanceof ClassPrepareEvent) {            classPrepareEvent((ClassPrepareEvent)event);        } else if (event instanceof VMStartEvent) {            vmStartEvent((VMStartEvent)event);        } else if (event instanceof VMDeathEvent) {            vmDeathEvent((VMDeathEvent)event);        } else if (event instanceof VMDisconnectEvent) {            vmDisconnectEvent((VMDisconnectEvent)event);        } else {            throw new Error("Unexpected event type");        }    }     /***     * A VMDisconnectedException has happened while dealing with     * another event. We need to flush the event queue, dealing only     * with exit events (VMDeath, VMDisconnect) so that we terminate     * correctly.     */    synchronized void handleDisconnectedException() {        EventQueue queue = vm.eventQueue();        while (connected) {            try {                EventSet eventSet = queue.remove();                EventIterator iter = eventSet.eventIterator();                while (iter.hasNext()) {                    Event event = iter.nextEvent();                    if (event instanceof VMDeathEvent) {                        vmDeathEvent((VMDeathEvent)event);                    } else if (event instanceof VMDisconnectEvent) {                        vmDisconnectEvent((VMDisconnectEvent)event);                    }                }                eventSet.resume(); // Resume the VM            } catch (InterruptedException exc) {                // ignore            }        }    }     private void vmStartEvent(VMStartEvent event)  {         writer.println("-- VM Started --");    }     // Forward event for thread specific processing    private void methodEntryEvent(MethodEntryEvent event)  {         threadTrace(event.thread()).methodEntryEvent(event);    }     // Forward event for thread specific processing    private void methodExitEvent(MethodExitEvent event)  {         threadTrace(event.thread()).methodExitEvent(event);    }     // Forward event for thread specific processing    private void stepEvent(StepEvent event)  {         threadTrace(event.thread()).stepEvent(event);    }     // Forward event for thread specific processing    private void fieldWatchEvent(ModificationWatchpointEvent event)  {         threadTrace(event.thread()).fieldWatchEvent(event);    }     void threadDeathEvent(ThreadDeathEvent event)  {        ThreadTrace trace = traceMap.get(event.thread());        if (trace != null) {  // only want threads we care about            trace.threadDeathEvent(event);   // Forward event        }    }     /**     * A new class has been loaded.     * Set watchpoints on each of its fields     */    private void classPrepareEvent(ClassPrepareEvent event)  {        EventRequestManager mgr = vm.eventRequestManager();        List<Field> fields = event.referenceType().visibleFields();        for (Field field : fields) {            ModificationWatchpointRequest req =                     mgr.createModificationWatchpointRequest(field);            for (int i=0; i<excludes.length; ++i) {                req.addClassExclusionFilter(excludes[i]);            }            req.setSuspendPolicy(EventRequest.SUSPEND_NONE);            req.enable();        }    }     private void exceptionEvent(ExceptionEvent event) {        ThreadTrace trace = traceMap.get(event.thread());        if (trace != null) {  // only want threads we care about            trace.exceptionEvent(event);      // Forward event        }    }     public void vmDeathEvent(VMDeathEvent event) {        vmDied = true;        writer.println("-- The application exited --");    }     public void vmDisconnectEvent(VMDisconnectEvent event) {        connected = false;        if (!vmDied) {            writer.println("-- The application has been disconnected --");        }    }}