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