JavaObject.java revision 2224:2a8815d86b93
1/* 2 * Copyright (c) 1997, 2016, 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/* 28 * The Original Code is HAT. The Initial Developer of the 29 * Original Code is Bill Foote, with contributions from others 30 * at JavaSoft/Sun. 31 */ 32 33package jdk.test.lib.hprof.model; 34 35import java.io.IOException; 36import jdk.test.lib.hprof.parser.ReadBuffer; 37 38/** 39 * Represents Java instance 40 * 41 * @author Bill Foote 42 */ 43public class JavaObject extends JavaLazyReadObject { 44 45 private Object clazz; // Number before resolve 46 // JavaClass after resolve 47 /** 48 * Construct a new JavaObject. 49 * 50 * @param classID id of the class object 51 * @param offset The offset of field data 52 */ 53 public JavaObject(long classID, long offset) { 54 super(offset); 55 this.clazz = makeId(classID); 56 } 57 58 public void resolve(Snapshot snapshot) { 59 if (clazz instanceof JavaClass) { 60 return; 61 } 62 if (clazz instanceof Number) { 63 long classID = getIdValue((Number)clazz); 64 clazz = snapshot.findThing(classID); 65 if (! (clazz instanceof JavaClass)) { 66 warn("Class " + Long.toHexString(classID) + " not found, " + 67 "adding fake class!"); 68 int length; 69 ReadBuffer buf = snapshot.getReadBuffer(); 70 int idSize = snapshot.getIdentifierSize(); 71 long lenOffset = getOffset() + 2*idSize + 4; 72 try { 73 length = buf.getInt(lenOffset); 74 } catch (IOException exp) { 75 throw new RuntimeException(exp); 76 } 77 clazz = snapshot.addFakeInstanceClass(classID, length); 78 } 79 } else { 80 throw new InternalError("should not reach here"); 81 } 82 83 JavaClass cl = (JavaClass) clazz; 84 cl.resolve(snapshot); 85 86 // while resolving, parse fields in verbose mode. 87 // but, getFields calls parseFields in non-verbose mode 88 // to avoid printing warnings repeatedly. 89 parseFields(getValue(), true); 90 91 cl.addInstance(this); 92 super.resolve(snapshot); 93 } 94 95 /** 96 * Are we the same type as other? We are iff our clazz is the 97 * same type as other's. 98 */ 99 public boolean isSameTypeAs(JavaThing other) { 100 if (!(other instanceof JavaObject)) { 101 return false; 102 } 103 JavaObject oo = (JavaObject) other; 104 return getClazz().equals(oo.getClazz()); 105 } 106 107 /** 108 * Return our JavaClass object. This may only be called after resolve. 109 */ 110 public JavaClass getClazz() { 111 return (JavaClass) clazz; 112 } 113 114 public JavaThing[] getFields() { 115 // pass false to verbose mode so that dereference 116 // warnings are not printed. 117 return parseFields(getValue(), false); 118 } 119 120 // returns the value of field of given name 121 public JavaThing getField(String name) { 122 JavaThing[] flds = getFields(); 123 JavaField[] instFields = getClazz().getFieldsForInstance(); 124 for (int i = 0; i < instFields.length; i++) { 125 if (instFields[i].getName().equals(name)) { 126 return flds[i]; 127 } 128 } 129 return null; 130 } 131 132 public int compareTo(JavaThing other) { 133 if (other instanceof JavaObject) { 134 JavaObject oo = (JavaObject) other; 135 return getClazz().getName().compareTo(oo.getClazz().getName()); 136 } 137 return super.compareTo(other); 138 } 139 140 public void visitReferencedObjects(JavaHeapObjectVisitor v) { 141 super.visitReferencedObjects(v); 142 JavaThing[] flds = getFields(); 143 for (int i = 0; i < flds.length; i++) { 144 if (flds[i] != null) { 145 if (v.mightExclude() 146 && v.exclude(getClazz().getClassForField(i), 147 getClazz().getFieldForInstance(i))) 148 { 149 // skip it 150 } else if (flds[i] instanceof JavaHeapObject) { 151 v.visit((JavaHeapObject) flds[i]); 152 } 153 } 154 } 155 } 156 157 public boolean refersOnlyWeaklyTo(Snapshot ss, JavaThing other) { 158 if (ss.getWeakReferenceClass() != null) { 159 final int referentFieldIndex = ss.getReferentFieldIndex(); 160 if (ss.getWeakReferenceClass().isAssignableFrom(getClazz())) { 161 // 162 // REMIND: This introduces a dependency on the JDK 163 // implementation that is undesirable. 164 JavaThing[] flds = getFields(); 165 for (int i = 0; i < flds.length; i++) { 166 if (i != referentFieldIndex && flds[i] == other) { 167 return false; 168 } 169 } 170 return true; 171 } 172 } 173 return false; 174 } 175 176 /** 177 * Describe the reference that this thing has to target. This will only 178 * be called if target is in the array returned by getChildrenForRootset. 179 */ 180 public String describeReferenceTo(JavaThing target, Snapshot ss) { 181 JavaThing[] flds = getFields(); 182 for (int i = 0; i < flds.length; i++) { 183 if (flds[i] == target) { 184 JavaField f = getClazz().getFieldForInstance(i); 185 return "field " + f.getName(); 186 } 187 } 188 return super.describeReferenceTo(target, ss); 189 } 190 191 public String toString() { 192 if (getClazz().isString()) { 193 JavaThing value = getField("value"); 194 if (value instanceof JavaValueArray) { 195 return ((JavaValueArray)value).valueString(); 196 } else { 197 return "null"; 198 } 199 } else { 200 return super.toString(); 201 } 202 } 203 204 // Internals only below this point 205 206 /* 207 * Java instance record (HPROF_GC_INSTANCE_DUMP) looks as below: 208 * 209 * object ID 210 * stack trace serial number (int) 211 * class ID 212 * data length (int) 213 * byte[length] 214 */ 215 protected final int readValueLength() throws IOException { 216 JavaClass cl = getClazz(); 217 int idSize = cl.getIdentifierSize(); 218 long lengthOffset = getOffset() + 2*idSize + 4; 219 return cl.getReadBuffer().getInt(lengthOffset); 220 } 221 222 protected final byte[] readValue() throws IOException { 223 JavaClass cl = getClazz(); 224 int idSize = cl.getIdentifierSize(); 225 ReadBuffer buf = cl.getReadBuffer(); 226 long offset = getOffset() + 2*idSize + 4; 227 int length = buf.getInt(offset); 228 if (length == 0) { 229 return Snapshot.EMPTY_BYTE_ARRAY; 230 } else { 231 byte[] res = new byte[length]; 232 buf.get(offset + 4, res); 233 return res; 234 } 235 } 236 237 private JavaThing[] parseFields(byte[] data, boolean verbose) { 238 JavaClass cl = getClazz(); 239 int target = cl.getNumFieldsForInstance(); 240 JavaField[] fields = cl.getFields(); 241 JavaThing[] fieldValues = new JavaThing[target]; 242 Snapshot snapshot = cl.getSnapshot(); 243 int idSize = snapshot.getIdentifierSize(); 244 int fieldNo = 0; 245 // In the dump file, the fields are stored in this order: 246 // fields of most derived class (immediate class) are stored 247 // first and then the super class and so on. In this object, 248 // fields are stored in the reverse ("natural") order. i.e., 249 // fields of most super class are stored first. 250 251 // target variable is used to compensate for the fact that 252 // the dump file starts field values from the leaf working 253 // upwards in the inheritance hierarchy, whereas JavaObject 254 // starts with the top of the inheritance hierarchy and works down. 255 target -= fields.length; 256 JavaClass currClass = cl; 257 int index = 0; 258 for (int i = 0; i < fieldValues.length; i++, fieldNo++) { 259 while (fieldNo >= fields.length) { 260 currClass = currClass.getSuperclass(); 261 fields = currClass.getFields(); 262 fieldNo = 0; 263 target -= fields.length; 264 } 265 JavaField f = fields[fieldNo]; 266 char sig = f.getSignature().charAt(0); 267 switch (sig) { 268 case 'L': 269 case '[': { 270 long id = objectIdAt(index, data); 271 index += idSize; 272 JavaObjectRef ref = new JavaObjectRef(id); 273 fieldValues[target+fieldNo] = ref.dereference(snapshot, f, verbose); 274 break; 275 } 276 case 'Z': { 277 byte value = byteAt(index, data); 278 index++; 279 fieldValues[target+fieldNo] = new JavaBoolean(value != 0); 280 break; 281 } 282 case 'B': { 283 byte value = byteAt(index, data); 284 index++; 285 fieldValues[target+fieldNo] = new JavaByte(value); 286 break; 287 } 288 case 'S': { 289 short value = shortAt(index, data); 290 index += 2; 291 fieldValues[target+fieldNo] = new JavaShort(value); 292 break; 293 } 294 case 'C': { 295 char value = charAt(index, data); 296 index += 2; 297 fieldValues[target+fieldNo] = new JavaChar(value); 298 break; 299 } 300 case 'I': { 301 int value = intAt(index, data); 302 index += 4; 303 fieldValues[target+fieldNo] = new JavaInt(value); 304 break; 305 } 306 case 'J': { 307 long value = longAt(index, data); 308 index += 8; 309 fieldValues[target+fieldNo] = new JavaLong(value); 310 break; 311 } 312 case 'F': { 313 float value = floatAt(index, data); 314 index += 4; 315 fieldValues[target+fieldNo] = new JavaFloat(value); 316 break; 317 } 318 case 'D': { 319 double value = doubleAt(index, data); 320 index += 8; 321 fieldValues[target+fieldNo] = new JavaDouble(value); 322 break; 323 } 324 default: 325 throw new RuntimeException("invalid signature: " + sig); 326 } 327 } 328 return fieldValues; 329 } 330 331 private void warn(String msg) { 332 System.out.println("WARNING: " + msg); 333 } 334} 335