1 /* 2 * Copyright (c) 1999, 2011, 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.jdi; 27 28 import com.sun.jdi.*; 29 30 import java.lang.ref.WeakReference; 31 import java.util.*; 32 33 class VMState { 34 private final VirtualMachineImpl vm; 35 36 // Listeners 37 private final List<WeakReference<VMListener>> listeners = new ArrayList<WeakReference<VMListener>>(); // synchronized (this) 38 private boolean notifyingListeners = false; // synchronized (this) 39 40 /* 41 * Certain information can be cached only when the entire VM is 42 * suspended and there are no pending resumes. The fields below 43 * are used to track whether there are pending resumes. (There 44 * is an assumption that JDWP command ids are increasing over time.) 45 */ 46 private int lastCompletedCommandId = 0; // synchronized (this) 47 private int lastResumeCommandId = 0; // synchronized (this) 48 49 // This is cached only while the VM is suspended 50 private static class Cache { 51 List<ThreadGroupReference> groups = null; // cached Top Level ThreadGroups 52 List<ThreadReference> threads = null; // cached Threads 53 } 54 55 private Cache cache = null; // synchronized (this) 56 private static final Cache markerCache = new Cache(); 57 disableCache()58 private void disableCache() { 59 synchronized (this) { 60 cache = null; 61 } 62 } 63 enableCache()64 private void enableCache() { 65 synchronized (this) { 66 cache = markerCache; 67 } 68 } 69 getCache()70 private Cache getCache() { 71 synchronized (this) { 72 if (cache == markerCache) { 73 cache = new Cache(); 74 } 75 return cache; 76 } 77 } 78 VMState(VirtualMachineImpl vm)79 VMState(VirtualMachineImpl vm) { 80 this.vm = vm; 81 } 82 83 /** 84 * Is the VM currently suspended, for the purpose of caching? 85 * Must be called synchronized on vm.state() 86 */ isSuspended()87 boolean isSuspended() { 88 return cache != null; 89 } 90 91 /* 92 * A JDWP command has been completed (reply has been received). 93 * Update data that tracks pending resume commands. 94 */ notifyCommandComplete(int id)95 synchronized void notifyCommandComplete(int id) { 96 lastCompletedCommandId = id; 97 } 98 freeze()99 synchronized void freeze() { 100 if (cache == null && (lastCompletedCommandId >= lastResumeCommandId)) { 101 /* 102 * No pending resumes to worry about. The VM is suspended 103 * and additional state can be cached. Notify all 104 * interested listeners. 105 */ 106 processVMAction(new VMAction(vm, VMAction.VM_SUSPENDED)); 107 enableCache(); 108 } 109 } 110 thawCommand(CommandSender sender)111 synchronized PacketStream thawCommand(CommandSender sender) { 112 PacketStream stream = sender.send(); 113 lastResumeCommandId = stream.id(); 114 thaw(); 115 return stream; 116 } 117 118 /** 119 * All threads are resuming 120 */ thaw()121 void thaw() { 122 thaw(null); 123 } 124 125 /** 126 * Tell listeners to invalidate suspend-sensitive caches. 127 * If resumingThread != null, then only that thread is being 128 * resumed. 129 */ thaw(ThreadReference resumingThread)130 synchronized void thaw(ThreadReference resumingThread) { 131 if (cache != null) { 132 if ((vm.traceFlags & VirtualMachine.TRACE_OBJREFS) != 0) { 133 vm.printTrace("Clearing VM suspended cache"); 134 } 135 disableCache(); 136 } 137 processVMAction(new VMAction(vm, resumingThread, VMAction.VM_NOT_SUSPENDED)); 138 } 139 processVMAction(VMAction action)140 private synchronized void processVMAction(VMAction action) { 141 if (!notifyingListeners) { 142 // Prevent recursion 143 notifyingListeners = true; 144 145 Iterator<WeakReference<VMListener>> iter = listeners.iterator(); 146 while (iter.hasNext()) { 147 WeakReference<VMListener> ref = iter.next(); 148 VMListener listener = ref.get(); 149 if (listener != null) { 150 boolean keep = true; 151 switch (action.id()) { 152 case VMAction.VM_SUSPENDED: 153 keep = listener.vmSuspended(action); 154 break; 155 case VMAction.VM_NOT_SUSPENDED: 156 keep = listener.vmNotSuspended(action); 157 break; 158 } 159 if (!keep) { 160 iter.remove(); 161 } 162 } else { 163 // Listener is unreachable; clean up 164 iter.remove(); 165 } 166 } 167 168 notifyingListeners = false; 169 } 170 } 171 addListener(VMListener listener)172 synchronized void addListener(VMListener listener) { 173 listeners.add(new WeakReference<VMListener>(listener)); 174 } 175 hasListener(VMListener listener)176 synchronized boolean hasListener(VMListener listener) { 177 return listeners.contains(listener); 178 } 179 removeListener(VMListener listener)180 synchronized void removeListener(VMListener listener) { 181 Iterator<WeakReference<VMListener>> iter = listeners.iterator(); 182 while (iter.hasNext()) { 183 WeakReference<VMListener> ref = iter.next(); 184 if (listener.equals(ref.get())) { 185 iter.remove(); 186 break; 187 } 188 } 189 } 190 allThreads()191 List<ThreadReference> allThreads() { 192 List<ThreadReference> threads = null; 193 try { 194 Cache local = getCache(); 195 196 if (local != null) { 197 // may be stale when returned, but not provably so 198 threads = local.threads; 199 } 200 if (threads == null) { 201 threads = Arrays.asList((ThreadReference[])JDWP.VirtualMachine.AllThreads. 202 process(vm).threads); 203 if (local != null) { 204 local.threads = threads; 205 if ((vm.traceFlags & VirtualMachine.TRACE_OBJREFS) != 0) { 206 vm.printTrace("Caching all threads (count = " + 207 threads.size() + ") while VM suspended"); 208 } 209 } 210 } 211 } catch (JDWPException exc) { 212 throw exc.toJDIException(); 213 } 214 return threads; 215 } 216 217 topLevelThreadGroups()218 List<ThreadGroupReference> topLevelThreadGroups() { 219 List<ThreadGroupReference> groups = null; 220 try { 221 Cache local = getCache(); 222 223 if (local != null) { 224 groups = local.groups; 225 } 226 if (groups == null) { 227 groups = Arrays.asList( 228 (ThreadGroupReference[])JDWP.VirtualMachine.TopLevelThreadGroups. 229 process(vm).groups); 230 if (local != null) { 231 local.groups = groups; 232 if ((vm.traceFlags & VirtualMachine.TRACE_OBJREFS) != 0) { 233 vm.printTrace( 234 "Caching top level thread groups (count = " + 235 groups.size() + ") while VM suspended"); 236 } 237 } 238 } 239 } catch (JDWPException exc) { 240 throw exc.toJDIException(); 241 } 242 return groups; 243 } 244 245 } 246