Showing the changes to java/pkg/com/sun/tools/example/trace/EventThread.java (1 of 18352 files). Show all
Unified · Split
| @@ -0,0 +1,344 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package com.sun.tools.example.trace; | ||
| 27 | |||
| 28 | import com.sun.jdi.*; | ||
| 29 | import com.sun.jdi.request.*; | ||
| 30 | import com.sun.jdi.event.*; | ||
| 31 | |||
| 32 | import java.util.*; | ||
| 33 | import java.io.PrintWriter; | ||
| 34 | |||
| 35 | /** | ||
| 36 | * This class processes incoming JDI events and displays them | ||
| 37 | * | ||
| 38 | * @author Robert Field | ||
| 39 | */ | ||
| 40 | public class EventThread extends Thread { | ||
| 41 | |||
| 42 | private final VirtualMachine vm; // Running VM | ||
| 43 | private final String[] excludes; // Packages to exclude | ||
| 44 | private final PrintWriter writer; // Where output goes | ||
| 45 | |||
| 46 | static String nextBaseIndent = ""; // Starting indent for next thread | ||
| 47 | |||
| 48 | private boolean connected = true; // Connected to VM | ||
| 49 | private boolean vmDied = true; // VMDeath occurred | ||
| 50 | |||
| 51 | // Maps ThreadReference to ThreadTrace instances | ||
| 52 | private Map<ThreadReference, ThreadTrace> traceMap = | ||
| 53 | new HashMap<>(); | ||
| 54 | |||
| 55 | EventThread(VirtualMachine vm, String[] excludes, PrintWriter writer) { | ||
| 56 | super("event-handler"); | ||
| 57 | this.vm = vm; | ||
| 58 | this.excludes = excludes; | ||
| 59 | this.writer = writer; | ||
| 60 | } | ||
| 61 | |||
| 62 | /** | ||
| 63 | * Run the event handling thread. | ||
| 64 | * As long as we are connected, get event sets off | ||
| 65 | * the queue and dispatch the events within them. | ||
| 66 | */ | ||
| 67 | @Override | ||
| 68 | public void run() { | ||
| 69 | EventQueue queue = vm.eventQueue(); | ||
| 70 | while (connected) { | ||
| 71 | try { | ||
| 72 | EventSet eventSet = queue.remove(); | ||
| 73 | EventIterator it = eventSet.eventIterator(); | ||
| 74 | while (it.hasNext()) { | ||
| 75 | handleEvent(it.nextEvent()); | ||
| 76 | } | ||
| 77 | eventSet.resume(); | ||
| 78 | } catch (InterruptedException exc) { | ||
| 79 | // Ignore | ||
| 80 | } catch (VMDisconnectedException discExc) { | ||
| 81 | handleDisconnectedException(); | ||
| 82 | break; | ||
| 83 | } | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | /** | ||
| 88 | * Create the desired event requests, and enable | ||
| 89 | * them so that we will get events. | ||
| 90 | * @param excludes Class patterns for which we don't want events | ||
| 91 | * @param watchFields Do we want to watch assignments to fields | ||
| 92 | */ | ||
| 93 | void setEventRequests(boolean watchFields) { | ||
| 94 | EventRequestManager mgr = vm.eventRequestManager(); | ||
| 95 | |||
| 96 | // want all exceptions | ||
| 97 | ExceptionRequest excReq = mgr.createExceptionRequest(null, | ||
| 98 | true, true); | ||
| 99 | // suspend so we can step | ||
| 100 | excReq.setSuspendPolicy(EventRequest.SUSPEND_ALL); | ||
| 101 | excReq.enable(); | ||
| 102 | |||
| 103 | MethodEntryRequest menr = mgr.createMethodEntryRequest(); | ||
| 104 | for (int i=0; i<excludes.length; ++i) { | ||
| 105 | menr.addClassExclusionFilter(excludes[i]); | ||
| 106 | } | ||
| 107 | menr.setSuspendPolicy(EventRequest.SUSPEND_NONE); | ||
| 108 | menr.enable(); | ||
| 109 | |||
| 110 | MethodExitRequest mexr = mgr.createMethodExitRequest(); | ||
| 111 | for (int i=0; i<excludes.length; ++i) { | ||
| 112 | mexr.addClassExclusionFilter(excludes[i]); | ||
| 113 | } | ||
| 114 | mexr.setSuspendPolicy(EventRequest.SUSPEND_NONE); | ||
| 115 | mexr.enable(); | ||
| 116 | |||
| 117 | ThreadDeathRequest tdr = mgr.createThreadDeathRequest(); | ||
| 118 | // Make sure we sync on thread death | ||
| 119 | tdr.setSuspendPolicy(EventRequest.SUSPEND_ALL); | ||
| 120 | tdr.enable(); | ||
| 121 | |||
| 122 | if (watchFields) { | ||
| 123 | ClassPrepareRequest cpr = mgr.createClassPrepareRequest(); | ||
| 124 | for (int i=0; i<excludes.length; ++i) { | ||
| 125 | cpr.addClassExclusionFilter(excludes[i]); | ||
| 126 | } | ||
| 127 | cpr.setSuspendPolicy(EventRequest.SUSPEND_ALL); | ||
| 128 | cpr.enable(); | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | /** | ||
| 133 | * This class keeps context on events in one thread. | ||
| 134 | * In this implementation, context is the indentation prefix. | ||
| 135 | */ | ||
| 136 | class ThreadTrace { | ||
| 137 | final ThreadReference thread; | ||
| 138 | final String baseIndent; | ||
| 139 | static final String threadDelta = " "; | ||
| 140 | StringBuffer indent; | ||
| 141 | |||
| 142 | ThreadTrace(ThreadReference thread) { | ||
| 143 | this.thread = thread; | ||
| 144 | this.baseIndent = nextBaseIndent; | ||
| 145 | indent = new StringBuffer(baseIndent); | ||
| 146 | nextBaseIndent += threadDelta; | ||
| 147 | println("====== " + thread.name() + " ======"); | ||
| 148 | } | ||
| 149 | |||
| 150 | private void println(String str) { | ||
| 151 | writer.print(indent); | ||
| 152 | writer.println(str); | ||
| 153 | } | ||
| 154 | |||
| 155 | void methodEntryEvent(MethodEntryEvent event) { | ||
| 156 | println(event.method().name() + " -- " | ||
| 157 | + event.method().declaringType().name()); | ||
| 158 | indent.append("| "); | ||
| 159 | } | ||
| 160 | |||
| 161 | void methodExitEvent(MethodExitEvent event) { | ||
| 162 | indent.setLength(indent.length()-2); | ||
| 163 | } | ||
| 164 | |||
| 165 | void fieldWatchEvent(ModificationWatchpointEvent event) { | ||
| 166 | Field field = event.field(); | ||
| 167 | Value value = event.valueToBe(); | ||
| 168 | println(" " + field.name() + " = " + value); | ||
| 169 | } | ||
| 170 | |||
| 171 | void exceptionEvent(ExceptionEvent event) { | ||
| 172 | println("Exception: " + event.exception() + | ||
| 173 | " catch: " + event.catchLocation()); | ||
| 174 | |||
| 175 | // Step to the catch | ||
| 176 | EventRequestManager mgr = vm.eventRequestManager(); | ||
| 177 | StepRequest req = mgr.createStepRequest(thread, | ||
| 178 | StepRequest.STEP_MIN, | ||
| 179 | StepRequest.STEP_INTO); | ||
| 180 | req.addCountFilter(1); // next step only | ||
| 181 | req.setSuspendPolicy(EventRequest.SUSPEND_ALL); | ||
| 182 | req.enable(); | ||
| 183 | } | ||
| 184 | |||
| 185 | // Step to exception catch | ||
| 186 | void stepEvent(StepEvent event) { | ||
| 187 | // Adjust call depth | ||
| 188 | int cnt = 0; | ||
| 189 | indent = new StringBuffer(baseIndent); | ||
| 190 | try { | ||
| 191 | cnt = thread.frameCount(); | ||
| 192 | } catch (IncompatibleThreadStateException exc) { | ||
| 193 | } | ||
| 194 | while (cnt-- > 0) { | ||
| 195 | indent.append("| "); | ||
| 196 | } | ||
| 197 | |||
| 198 | EventRequestManager mgr = vm.eventRequestManager(); | ||
| 199 | mgr.deleteEventRequest(event.request()); | ||
| 200 | } | ||
| 201 | |||
| 202 | void threadDeathEvent(ThreadDeathEvent event) { | ||
| 203 | indent = new StringBuffer(baseIndent); | ||
| 204 | println("====== " + thread.name() + " end ======"); | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | /** | ||
| 209 | * Returns the ThreadTrace instance for the specified thread, | ||
| 210 | * creating one if needed. | ||
| 211 | */ | ||
| 212 | ThreadTrace threadTrace(ThreadReference thread) { | ||
| 213 | ThreadTrace trace = traceMap.get(thread); | ||
| 214 | if (trace == null) { | ||
| 215 | trace = new ThreadTrace(thread); | ||
| 216 | traceMap.put(thread, trace); | ||
| 217 | } | ||
| 218 | return trace; | ||
| 219 | } | ||
| 220 | |||
| 221 | /** | ||
| 222 | * Dispatch incoming events | ||
| 223 | */ | ||
| 224 | private void handleEvent(Event event) { | ||
| 225 | if (event instanceof ExceptionEvent) { | ||
| 226 | exceptionEvent((ExceptionEvent)event); | ||
| 227 | } else if (event instanceof ModificationWatchpointEvent) { | ||
| 228 | fieldWatchEvent((ModificationWatchpointEvent)event); | ||
| 229 | } else if (event instanceof MethodEntryEvent) { | ||
| 230 | methodEntryEvent((MethodEntryEvent)event); | ||
| 231 | } else if (event instanceof MethodExitEvent) { | ||
| 232 | methodExitEvent((MethodExitEvent)event); | ||
| 233 | } else if (event instanceof StepEvent) { | ||
| 234 | stepEvent((StepEvent)event); | ||
| 235 | } else if (event instanceof ThreadDeathEvent) { | ||
| 236 | threadDeathEvent((ThreadDeathEvent)event); | ||
| 237 | } else if (event instanceof ClassPrepareEvent) { | ||
| 238 | classPrepareEvent((ClassPrepareEvent)event); | ||
| 239 | } else if (event instanceof VMStartEvent) { | ||
| 240 | vmStartEvent((VMStartEvent)event); | ||
| 241 | } else if (event instanceof VMDeathEvent) { | ||
| 242 | vmDeathEvent((VMDeathEvent)event); | ||
| 243 | } else if (event instanceof VMDisconnectEvent) { | ||
| 244 | vmDisconnectEvent((VMDisconnectEvent)event); | ||
| 245 | } else { | ||
| 246 | throw new Error("Unexpected event type"); | ||
| 247 | } | ||
| 248 | } | ||
| 249 | |||
| 250 | /*** | ||
| 251 | * A VMDisconnectedException has happened while dealing with | ||
| 252 | * another event. We need to flush the event queue, dealing only | ||
| 253 | * with exit events (VMDeath, VMDisconnect) so that we terminate | ||
| 254 | * correctly. | ||
| 255 | */ | ||
| 256 | synchronized void handleDisconnectedException() { | ||
| 257 | EventQueue queue = vm.eventQueue(); | ||
| 258 | while (connected) { | ||
| 259 | try { | ||
| 260 | EventSet eventSet = queue.remove(); | ||
| 261 | EventIterator iter = eventSet.eventIterator(); | ||
| 262 | while (iter.hasNext()) { | ||
| 263 | Event event = iter.nextEvent(); | ||
| 264 | if (event instanceof VMDeathEvent) { | ||
| 265 | vmDeathEvent((VMDeathEvent)event); | ||
| 266 | } else if (event instanceof VMDisconnectEvent) { | ||
| 267 | vmDisconnectEvent((VMDisconnectEvent)event); | ||
| 268 | } | ||
| 269 | } | ||
| 270 | eventSet.resume(); // Resume the VM | ||
| 271 | } catch (InterruptedException exc) { | ||
| 272 | // ignore | ||
| 273 | } | ||
| 274 | } | ||
| 275 | } | ||
| 276 | |||
| 277 | private void vmStartEvent(VMStartEvent event) { | ||
| 278 | writer.println("-- VM Started --"); | ||
| 279 | } | ||
| 280 | |||
| 281 | // Forward event for thread specific processing | ||
| 282 | private void methodEntryEvent(MethodEntryEvent event) { | ||
| 283 | threadTrace(event.thread()).methodEntryEvent(event); | ||
| 284 | } | ||
| 285 | |||
| 286 | // Forward event for thread specific processing | ||
| 287 | private void methodExitEvent(MethodExitEvent event) { | ||
| 288 | threadTrace(event.thread()).methodExitEvent(event); | ||
| 289 | } | ||
| 290 | |||
| 291 | // Forward event for thread specific processing | ||
| 292 | private void stepEvent(StepEvent event) { | ||
| 293 | threadTrace(event.thread()).stepEvent(event); | ||
| 294 | } | ||
| 295 | |||
| 296 | // Forward event for thread specific processing | ||
| 297 | private void fieldWatchEvent(ModificationWatchpointEvent event) { | ||
| 298 | threadTrace(event.thread()).fieldWatchEvent(event); | ||
| 299 | } | ||
| 300 | |||
| 301 | void threadDeathEvent(ThreadDeathEvent event) { | ||
| 302 | ThreadTrace trace = traceMap.get(event.thread()); | ||
| 303 | if (trace != null) { // only want threads we care about | ||
| 304 | trace.threadDeathEvent(event); // Forward event | ||
| 305 | } | ||
| 306 | } | ||
| 307 | |||
| 308 | /** | ||
| 309 | * A new class has been loaded. | ||
| 310 | * Set watchpoints on each of its fields | ||
| 311 | */ | ||
| 312 | private void classPrepareEvent(ClassPrepareEvent event) { | ||
| 313 | EventRequestManager mgr = vm.eventRequestManager(); | ||
| 314 | List<Field> fields = event.referenceType().visibleFields(); | ||
| 315 | for (Field field : fields) { | ||
| 316 | ModificationWatchpointRequest req = | ||
| 317 | mgr.createModificationWatchpointRequest(field); | ||
| 318 | for (int i=0; i<excludes.length; ++i) { | ||
| 319 | req.addClassExclusionFilter(excludes[i]); | ||
| 320 | } | ||
| 321 | req.setSuspendPolicy(EventRequest.SUSPEND_NONE); | ||
| 322 | req.enable(); | ||
| 323 | } | ||
| 324 | } | ||
| 325 | |||
| 326 | private void exceptionEvent(ExceptionEvent event) { | ||
| 327 | ThreadTrace trace = traceMap.get(event.thread()); | ||
| 328 | if (trace != null) { // only want threads we care about | ||
| 329 | trace.exceptionEvent(event); // Forward event | ||
| 330 | } | ||
| 331 | } | ||
| 332 | |||
| 333 | public void vmDeathEvent(VMDeathEvent event) { | ||
| 334 | vmDied = true; | ||
| 335 | writer.println("-- The application exited --"); | ||
| 336 | } | ||
| 337 | |||
| 338 | public void vmDisconnectEvent(VMDisconnectEvent event) { | ||
| 339 | connected = false; | ||
| 340 | if (!vmDied) { | ||
| 341 | writer.println("-- The application has been disconnected --"); | ||
| 342 | } | ||
| 343 | } | ||
| 344 | } | ||