1 /* 2 * Copyright (c) 2000, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 package sun.jvm.hotspot.oops; 26 27 import java.util.*; 28 29 import sun.jvm.hotspot.debugger.*; 30 import sun.jvm.hotspot.memory.*; 31 import sun.jvm.hotspot.runtime.*; 32 import sun.jvm.hotspot.types.TypeDataBase; 33 import sun.jvm.hotspot.utilities.*; 34 35 /** A utility class encapsulating useful oop operations */ 36 37 public class OopUtilities { 38 39 // FIXME: access should be synchronized and cleared when VM is 40 // resumed 41 // String fields 42 private static ByteField coderField; 43 private static OopField valueField; 44 // ThreadGroup fields 45 private static OopField threadGroupParentField; 46 private static OopField threadGroupNameField; 47 private static IntField threadGroupNThreadsField; 48 private static OopField threadGroupThreadsField; 49 private static IntField threadGroupNGroupsField; 50 private static OopField threadGroupGroupsField; 51 // Thread fields 52 private static OopField threadNameField; 53 private static OopField threadGroupField; 54 private static LongField threadEETopField; 55 //tid field is new since 1.5 56 private static LongField threadTIDField; 57 // threadStatus field is new since 1.5 58 private static IntField threadStatusField; 59 // parkBlocker field is new since 1.6 60 private static OopField threadParkBlockerField; 61 62 private static IntField threadPriorityField; 63 private static BooleanField threadDaemonField; 64 65 // possible values of java_lang_Thread::ThreadStatus 66 public static int THREAD_STATUS_NEW; 67 68 public static int THREAD_STATUS_RUNNABLE; 69 public static int THREAD_STATUS_SLEEPING; 70 public static int THREAD_STATUS_IN_OBJECT_WAIT; 71 public static int THREAD_STATUS_IN_OBJECT_WAIT_TIMED; 72 public static int THREAD_STATUS_PARKED; 73 public static int THREAD_STATUS_PARKED_TIMED; 74 public static int THREAD_STATUS_BLOCKED_ON_MONITOR_ENTER; 75 public static int THREAD_STATUS_TERMINATED; 76 77 // java.util.concurrent.locks.AbstractOwnableSynchronizer fields 78 private static OopField absOwnSyncOwnerThreadField; 79 80 private static final int JVMTI_THREAD_STATE_ALIVE = 0x0001; 81 82 static { 83 VM.registerVMInitializedObserver(new Observer() { 84 public void update(Observable o, Object data) { 85 initialize(VM.getVM().getTypeDataBase()); 86 } 87 }); 88 } 89 90 private static synchronized void initialize(TypeDataBase db) { 91 // FIXME: don't need this observer; however, do need a VM resumed 92 // and suspended observer to refetch fields 93 } 94 95 public static String charArrayToString(TypeArray charArray) { 96 if (charArray == null) { 97 return null; 98 } 99 int length = (int)charArray.getLength(); 100 StringBuffer buf = new StringBuffer(length); 101 for (int i = 0; i < length; i++) { 102 buf.append(charArray.getCharAt(i)); 103 } 104 return buf.toString(); 105 } 106 107 public static String byteArrayToString(TypeArray byteArray, byte coder) { 108 if (byteArray == null) { 109 return null; 110 } 111 int length = (int)byteArray.getLength() >> coder; 112 StringBuffer buf = new StringBuffer(length); 113 if (coder == 0) { 114 // Latin1 encoded 115 for (int i = 0; i < length; i++) { 116 buf.append((char)(byteArray.getByteAt(i) & 0xff)); 117 } 118 } else { 119 // UTF16 encoded 120 for (int i = 0; i < length; i++) { 121 buf.append(byteArray.getCharAt(i)); 122 } 123 } 124 return buf.toString(); 125 } 126 127 public static String escapeString(String s) { 128 StringBuilder sb = null; 129 for (int index = 0; index < s.length(); index++) { 130 char value = s.charAt(index); 131 if (value >= 32 && value < 127 || value == '\'' || value == '\\') { 132 if (sb != null) { 133 sb.append(value); 134 } 135 } else { 136 if (sb == null) { 137 sb = new StringBuilder(s.length() * 2); 138 sb.append(s, 0, index); 139 } 140 sb.append("\\u"); 141 if (value < 0x10) sb.append("000"); 142 else if (value < 0x100) sb.append("00"); 143 else if (value < 0x1000) sb.append("0"); 144 sb.append(Integer.toHexString(value)); 145 } 146 } 147 if (sb != null) { 148 return sb.toString(); 149 } 150 return s; 151 } 152 153 public static String stringOopToString(Oop stringOop) { 154 InstanceKlass k = (InstanceKlass) stringOop.getKlass(); 155 coderField = (ByteField) k.findField("coder", "B"); 156 valueField = (OopField) k.findField("value", "[B"); 157 if (Assert.ASSERTS_ENABLED) { 158 Assert.that(coderField != null, "Field \'coder\' of java.lang.String not found"); 159 Assert.that(valueField != null, "Field \'value\' of java.lang.String not found"); 160 } 161 return byteArrayToString((TypeArray) valueField.getValue(stringOop), coderField.getValue(stringOop)); 162 } 163 164 public static String stringOopToEscapedString(Oop stringOop) { 165 return escapeString(stringOopToString(stringOop)); 166 } 167 168 private static void initThreadGroupFields() { 169 if (threadGroupParentField == null) { 170 SystemDictionary sysDict = VM.getVM().getSystemDictionary(); 171 InstanceKlass k = sysDict.getThreadGroupKlass(); 172 threadGroupParentField = (OopField) k.findField("parent", "Ljava/lang/ThreadGroup;"); 173 threadGroupNameField = (OopField) k.findField("name", "Ljava/lang/String;"); 174 threadGroupNThreadsField = (IntField) k.findField("nthreads", "I"); 175 threadGroupThreadsField = (OopField) k.findField("threads", "[Ljava/lang/Thread;"); 176 threadGroupNGroupsField = (IntField) k.findField("ngroups", "I"); 177 threadGroupGroupsField = (OopField) k.findField("groups", "[Ljava/lang/ThreadGroup;"); 178 if (Assert.ASSERTS_ENABLED) { 179 Assert.that(threadGroupParentField != null && 180 threadGroupNameField != null && 181 threadGroupNThreadsField != null && 182 threadGroupThreadsField != null && 183 threadGroupNGroupsField != null && 184 threadGroupGroupsField != null, "must find all java.lang.ThreadGroup fields"); 185 } 186 } 187 } 188 189 public static Oop threadGroupOopGetParent(Oop threadGroupOop) { 190 initThreadGroupFields(); 191 return threadGroupParentField.getValue(threadGroupOop); 192 } 193 194 public static String threadGroupOopGetName(Oop threadGroupOop) { 195 initThreadGroupFields(); 196 return stringOopToString(threadGroupNameField.getValue(threadGroupOop)); 197 } 198 199 public static Oop[] threadGroupOopGetThreads(Oop threadGroupOop) { 200 initThreadGroupFields(); 201 int nthreads = threadGroupNThreadsField.getValue(threadGroupOop); 202 Oop[] result = new Oop[nthreads]; 203 ObjArray threads = (ObjArray) threadGroupThreadsField.getValue(threadGroupOop); 204 for (int i = 0; i < nthreads; i++) { 205 result[i] = threads.getObjAt(i); 206 } 207 return result; 208 } 209 210 public static Oop[] threadGroupOopGetGroups(Oop threadGroupOop) { 211 initThreadGroupFields(); 212 int ngroups = threadGroupNGroupsField.getValue(threadGroupOop); 213 Oop[] result = new Oop[ngroups]; 214 ObjArray groups = (ObjArray) threadGroupGroupsField.getValue(threadGroupOop); 215 for (int i = 0; i < ngroups; i++) { 216 result[i] = groups.getObjAt(i); 217 } 218 return result; 219 } 220 221 private static void initThreadFields() { 222 if (threadNameField == null) { 223 SystemDictionary sysDict = VM.getVM().getSystemDictionary(); 224 InstanceKlass k = sysDict.getThreadKlass(); 225 threadNameField = (OopField) k.findField("name", "Ljava/lang/String;"); 226 threadGroupField = (OopField) k.findField("group", "Ljava/lang/ThreadGroup;"); 227 threadEETopField = (LongField) k.findField("eetop", "J"); 228 threadTIDField = (LongField) k.findField("tid", "J"); 229 threadStatusField = (IntField) k.findField("threadStatus", "I"); 230 threadParkBlockerField = (OopField) k.findField("parkBlocker", 231 "Ljava/lang/Object;"); 232 threadPriorityField = (IntField) k.findField("priority", "I"); 233 threadDaemonField = (BooleanField) k.findField("daemon", "Z"); 234 TypeDataBase db = VM.getVM().getTypeDataBase(); 235 THREAD_STATUS_NEW = db.lookupIntConstant("java_lang_Thread::NEW").intValue(); 236 237 THREAD_STATUS_RUNNABLE = db.lookupIntConstant("java_lang_Thread::RUNNABLE").intValue(); 238 THREAD_STATUS_SLEEPING = db.lookupIntConstant("java_lang_Thread::SLEEPING").intValue(); 239 THREAD_STATUS_IN_OBJECT_WAIT = db.lookupIntConstant("java_lang_Thread::IN_OBJECT_WAIT").intValue(); 240 THREAD_STATUS_IN_OBJECT_WAIT_TIMED = db.lookupIntConstant("java_lang_Thread::IN_OBJECT_WAIT_TIMED").intValue(); 241 THREAD_STATUS_PARKED = db.lookupIntConstant("java_lang_Thread::PARKED").intValue(); 242 THREAD_STATUS_PARKED_TIMED = db.lookupIntConstant("java_lang_Thread::PARKED_TIMED").intValue(); 243 THREAD_STATUS_BLOCKED_ON_MONITOR_ENTER = db.lookupIntConstant("java_lang_Thread::BLOCKED_ON_MONITOR_ENTER").intValue(); 244 THREAD_STATUS_TERMINATED = db.lookupIntConstant("java_lang_Thread::TERMINATED").intValue(); 245 246 if (Assert.ASSERTS_ENABLED) { 247 // it is okay to miss threadStatusField, because this was 248 // introduced only in 1.5 JDK. 249 Assert.that(threadNameField != null && 250 threadGroupField != null && 251 threadEETopField != null, "must find all java.lang.Thread fields"); 252 } 253 } 254 } 255 256 public static Oop threadOopGetThreadGroup(Oop threadOop) { 257 initThreadFields(); 258 return threadGroupField.getValue(threadOop); 259 } 260 261 public static String threadOopGetName(Oop threadOop) { 262 initThreadFields(); 263 return stringOopToString(threadNameField.getValue(threadOop)); 264 } 265 266 /** May return null if, e.g., thread was not started */ 267 public static JavaThread threadOopGetJavaThread(Oop threadOop) { 268 initThreadFields(); 269 Address addr = threadOop.getHandle().getAddressAt(threadEETopField.getOffset()); 270 if (addr == null) { 271 return null; 272 } 273 return VM.getVM().getThreads().createJavaThreadWrapper(addr); 274 } 275 276 public static long threadOopGetTID(Oop threadOop) { 277 initThreadFields(); 278 if (threadTIDField != null) { 279 return threadTIDField.getValue(threadOop); 280 } else { 281 return 0; 282 } 283 } 284 285 /** returns value of java.lang.Thread.threadStatus field */ 286 public static int threadOopGetThreadStatus(Oop threadOop) { 287 initThreadFields(); 288 // The threadStatus is only present starting in 1.5 289 if (threadStatusField != null) { 290 return (int) threadStatusField.getValue(threadOop); 291 } else { 292 // All we can easily figure out is if it is alive, but that is 293 // enough info for a valid unknown status. 294 JavaThread thr = threadOopGetJavaThread(threadOop); 295 if (thr == null) { 296 // the thread hasn't run yet or is in the process of exiting 297 return THREAD_STATUS_NEW; 298 } else { 299 return JVMTI_THREAD_STATE_ALIVE; 300 } 301 } 302 } 303 304 /** returns value of java.lang.Thread.parkBlocker field */ 305 public static Oop threadOopGetParkBlocker(Oop threadOop) { 306 initThreadFields(); 307 if (threadParkBlockerField != null) { 308 return threadParkBlockerField.getValue(threadOop); 309 } 310 return null; 311 } 312 313 // initialize fields for j.u.c.l AbstractOwnableSynchornizer class 314 private static void initAbsOwnSyncFields() { 315 if (absOwnSyncOwnerThreadField == null) { 316 SystemDictionary sysDict = VM.getVM().getSystemDictionary(); 317 InstanceKlass k = sysDict.getAbstractOwnableSynchronizerKlass(); 318 absOwnSyncOwnerThreadField = 319 (OopField) k.findField("exclusiveOwnerThread", 320 "Ljava/lang/Thread;"); 321 } 322 } 323 324 // return exclusiveOwnerThread field of AbstractOwnableSynchronizer class 325 public static Oop abstractOwnableSynchronizerGetOwnerThread(Oop oop) { 326 initAbsOwnSyncFields(); 327 if (absOwnSyncOwnerThreadField == null) { 328 return null; // pre-1.6 VM? 329 } else { 330 return absOwnSyncOwnerThreadField.getValue(oop); 331 } 332 } 333 334 public static int threadOopGetPriority(Oop threadOop) { 335 initThreadFields(); 336 if (threadPriorityField != null) { 337 return threadPriorityField.getValue(threadOop); 338 } else { 339 return 0; 340 } 341 } 342 343 public static boolean threadOopGetDaemon(Oop threadOop) { 344 initThreadFields(); 345 if (threadDaemonField != null) { 346 return threadDaemonField.getValue(threadOop); 347 } else { 348 return false; 349 } 350 } 351 352 public static String threadOopGetThreadStatusName(Oop threadOop) { 353 int status = OopUtilities.threadOopGetThreadStatus(threadOop); 354 if(status == THREAD_STATUS_NEW){ 355 return "NEW"; 356 }else if(status == THREAD_STATUS_RUNNABLE){ 357 return "RUNNABLE"; 358 }else if(status == THREAD_STATUS_SLEEPING){ 359 return "TIMED_WAITING (sleeping)"; 360 }else if(status == THREAD_STATUS_IN_OBJECT_WAIT){ 361 return "WAITING (on object monitor)"; 362 }else if(status == THREAD_STATUS_IN_OBJECT_WAIT_TIMED){ 363 return "TIMED_WAITING (on object monitor)"; 364 }else if(status == THREAD_STATUS_PARKED){ 365 return "WAITING (parking)"; 366 }else if(status == THREAD_STATUS_PARKED_TIMED){ 367 return "TIMED_WAITING (parking)"; 368 }else if(status == THREAD_STATUS_BLOCKED_ON_MONITOR_ENTER){ 369 return "BLOCKED (on object monitor)"; 370 }else if(status == THREAD_STATUS_TERMINATED){ 371 return "TERMINATED"; 372 } 373 return "UNKNOWN"; 374 } 375 }