1 /* 2 * Copyright (c) 1998, 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 /* 27 * This source code is provided to illustrate the usage of a given feature 28 * or technique and has been deliberately simplified. Additional steps 29 * required for a production-quality application, such as security checks, 30 * input validation and proper error handling, might not be present in 31 * this sample code. 32 */ 33 34 35 package com.sun.tools.example.debug.tty; 36 37 import com.sun.jdi.ThreadReference; 38 import com.sun.jdi.ThreadGroupReference; 39 import com.sun.jdi.IncompatibleThreadStateException; 40 import com.sun.jdi.StackFrame; 41 import java.util.List; 42 import java.util.ArrayList; 43 import java.util.Collections; 44 45 class ThreadInfo { 46 // This is a list of all known ThreadInfo objects. It survives 47 // ThreadInfo.invalidateAll, unlike the other static fields below. 48 private static List<ThreadInfo> threads = Collections.synchronizedList(new ArrayList<ThreadInfo>()); 49 private static boolean gotInitialThreads = false; 50 51 private static ThreadInfo current = null; 52 private static ThreadGroupReference group = null; 53 54 private final ThreadReference thread; 55 private int currentFrameIndex = 0; 56 ThreadInfo(ThreadReference thread)57 private ThreadInfo(ThreadReference thread) { 58 this.thread = thread; 59 if (thread == null) { 60 MessageOutput.fatalError("Internal error: null ThreadInfo created"); 61 } 62 } 63 initThreads()64 private static void initThreads() { 65 if (!gotInitialThreads) { 66 for (ThreadReference thread : Env.vm().allThreads()) { 67 threads.add(new ThreadInfo(thread)); 68 } 69 gotInitialThreads = true; 70 } 71 } 72 addThread(ThreadReference thread)73 static void addThread(ThreadReference thread) { 74 synchronized (threads) { 75 initThreads(); 76 ThreadInfo ti = new ThreadInfo(thread); 77 // Guard against duplicates. Duplicates can happen during 78 // initialization when a particular thread might be added both 79 // by a thread start event and by the initial call to threads() 80 if (getThreadInfo(thread) == null) { 81 threads.add(ti); 82 } 83 } 84 } 85 removeThread(ThreadReference thread)86 static void removeThread(ThreadReference thread) { 87 if (thread.equals(ThreadInfo.current)) { 88 // Current thread has died. 89 90 // Be careful getting the thread name. If its death happens 91 // as part of VM termination, it may be too late to get the 92 // information, and an exception will be thrown. 93 String currentThreadName; 94 try { 95 currentThreadName = "\"" + thread.name() + "\""; 96 } catch (Exception e) { 97 currentThreadName = ""; 98 } 99 100 setCurrentThread(null); 101 102 MessageOutput.println(); 103 MessageOutput.println("Current thread died. Execution continuing...", 104 currentThreadName); 105 } 106 threads.remove(getThreadInfo(thread)); 107 } 108 threads()109 static List<ThreadInfo> threads() { 110 synchronized(threads) { 111 initThreads(); 112 // Make a copy to allow iteration without synchronization 113 return new ArrayList<ThreadInfo>(threads); 114 } 115 } 116 invalidateAll()117 static void invalidateAll() { 118 current = null; 119 group = null; 120 synchronized (threads) { 121 for (ThreadInfo ti : threads()) { 122 ti.invalidate(); 123 } 124 } 125 } 126 setThreadGroup(ThreadGroupReference tg)127 static void setThreadGroup(ThreadGroupReference tg) { 128 group = tg; 129 } 130 setCurrentThread(ThreadReference tr)131 static void setCurrentThread(ThreadReference tr) { 132 if (tr == null) { 133 setCurrentThreadInfo(null); 134 } else { 135 ThreadInfo tinfo = getThreadInfo(tr); 136 setCurrentThreadInfo(tinfo); 137 } 138 } 139 setCurrentThreadInfo(ThreadInfo tinfo)140 static void setCurrentThreadInfo(ThreadInfo tinfo) { 141 current = tinfo; 142 if (current != null) { 143 current.invalidate(); 144 } 145 } 146 147 /** 148 * Get the current ThreadInfo object. 149 * 150 * @return the ThreadInfo for the current thread. 151 */ getCurrentThreadInfo()152 static ThreadInfo getCurrentThreadInfo() { 153 return current; 154 } 155 156 /** 157 * Get the thread from this ThreadInfo object. 158 * 159 * @return the Thread wrapped by this ThreadInfo. 160 */ getThread()161 ThreadReference getThread() { 162 return thread; 163 } 164 group()165 static ThreadGroupReference group() { 166 if (group == null) { 167 // Current thread group defaults to the first top level 168 // thread group. 169 setThreadGroup(Env.vm().topLevelThreadGroups().get(0)); 170 } 171 return group; 172 } 173 getThreadInfo(long id)174 static ThreadInfo getThreadInfo(long id) { 175 ThreadInfo retInfo = null; 176 177 synchronized (threads) { 178 for (ThreadInfo ti : threads()) { 179 if (ti.thread.uniqueID() == id) { 180 retInfo = ti; 181 break; 182 } 183 } 184 } 185 return retInfo; 186 } 187 getThreadInfo(ThreadReference tr)188 static ThreadInfo getThreadInfo(ThreadReference tr) { 189 return getThreadInfo(tr.uniqueID()); 190 } 191 getThreadInfo(String idToken)192 static ThreadInfo getThreadInfo(String idToken) { 193 ThreadInfo tinfo = null; 194 if (idToken.startsWith("t@")) { 195 idToken = idToken.substring(2); 196 } 197 try { 198 long threadId = Long.decode(idToken).longValue(); 199 tinfo = getThreadInfo(threadId); 200 } catch (NumberFormatException e) { 201 tinfo = null; 202 } 203 return tinfo; 204 } 205 206 /** 207 * Get the thread stack frames. 208 * 209 * @return a <code>List</code> of the stack frames. 210 */ getStack()211 List<StackFrame> getStack() throws IncompatibleThreadStateException { 212 return thread.frames(); 213 } 214 215 /** 216 * Get the current stackframe. 217 * 218 * @return the current stackframe. 219 */ getCurrentFrame()220 StackFrame getCurrentFrame() throws IncompatibleThreadStateException { 221 if (thread.frameCount() == 0) { 222 return null; 223 } 224 return thread.frame(currentFrameIndex); 225 } 226 227 /** 228 * Invalidate the current stackframe index. 229 */ invalidate()230 void invalidate() { 231 currentFrameIndex = 0; 232 } 233 234 /* Throw IncompatibleThreadStateException if not suspended */ assureSuspended()235 private void assureSuspended() throws IncompatibleThreadStateException { 236 if (!thread.isSuspended()) { 237 throw new IncompatibleThreadStateException(); 238 } 239 } 240 241 /** 242 * Get the current stackframe index. 243 * 244 * @return the number of the current stackframe. Frame zero is the 245 * closest to the current program counter 246 */ getCurrentFrameIndex()247 int getCurrentFrameIndex() { 248 return currentFrameIndex; 249 } 250 251 /** 252 * Set the current stackframe to a specific frame. 253 * 254 * @param nFrame the number of the desired stackframe. Frame zero is the 255 * closest to the current program counter 256 * @exception IllegalAccessError when the thread isn't 257 * suspended or waiting at a breakpoint 258 * @exception ArrayIndexOutOfBoundsException when the 259 * requested frame is beyond the stack boundary 260 */ setCurrentFrameIndex(int nFrame)261 void setCurrentFrameIndex(int nFrame) throws IncompatibleThreadStateException { 262 assureSuspended(); 263 if ((nFrame < 0) || (nFrame >= thread.frameCount())) { 264 throw new ArrayIndexOutOfBoundsException(); 265 } 266 currentFrameIndex = nFrame; 267 } 268 269 /** 270 * Change the current stackframe to be one or more frames higher 271 * (as in, away from the current program counter). 272 * 273 * @param nFrames the number of stackframes 274 * @exception IllegalAccessError when the thread isn't 275 * suspended or waiting at a breakpoint 276 * @exception ArrayIndexOutOfBoundsException when the 277 * requested frame is beyond the stack boundary 278 */ up(int nFrames)279 void up(int nFrames) throws IncompatibleThreadStateException { 280 setCurrentFrameIndex(currentFrameIndex + nFrames); 281 } 282 283 /** 284 * Change the current stackframe to be one or more frames lower 285 * (as in, toward the current program counter). * 286 * @param nFrames the number of stackframes 287 * @exception IllegalAccessError when the thread isn't 288 * suspended or waiting at a breakpoint 289 * @exception ArrayIndexOutOfBoundsException when the 290 * requested frame is beyond the stack boundary 291 */ down(int nFrames)292 void down(int nFrames) throws IncompatibleThreadStateException { 293 setCurrentFrameIndex(currentFrameIndex - nFrames); 294 } 295 296 } 297