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