1 /* 2 * Copyright (c) 1997, 2022, 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.util.Vector; 34 import java.util.Enumeration; 35 import jdk.test.lib.hprof.util.CompositeEnumeration; 36 import jdk.test.lib.hprof.parser.ReadBuffer; 37 38 /** 39 * 40 * @author Bill Foote 41 */ 42 43 44 public class JavaClass extends JavaHeapObject { 45 // my id 46 private long id; 47 // my name 48 private String name; 49 50 // These are JavaObjectRef before resolve 51 private JavaThing superclass; 52 private JavaThing loader; 53 private JavaThing signers; 54 private JavaThing protectionDomain; 55 56 // non-static fields 57 private JavaField[] fields; 58 // static fields 59 private JavaStatic[] statics; 60 61 private static final JavaClass[] EMPTY_CLASS_ARRAY = new JavaClass[0]; 62 // my subclasses 63 private JavaClass[] subclasses = EMPTY_CLASS_ARRAY; 64 65 // my instances 66 private Vector<JavaHeapObject> instances = new Vector<JavaHeapObject>(); 67 68 // Who I belong to. Set on resolve. 69 private Snapshot mySnapshot; 70 71 // Size of an instance, including VM overhead 72 private int instanceSize; 73 // Total number of fields including inherited ones 74 private int totalNumFields; 75 76 // Size of the instance in the object of the class is inlined. 77 // Calculated lazily. 78 private int inlinedInstanceSize = -1; 79 80 public JavaClass(long id, String name, long superclassId, long loaderId, 81 long signersId, long protDomainId, 82 JavaField[] fields, JavaStatic[] statics, 83 int instanceSize) { 84 this.id = id; 85 this.name = name; 86 this.superclass = new JavaObjectRef(superclassId); 87 this.loader = new JavaObjectRef(loaderId); 88 this.signers = new JavaObjectRef(signersId); 89 this.protectionDomain = new JavaObjectRef(protDomainId); 90 this.fields = fields; 91 this.statics = statics; 92 this.instanceSize = instanceSize; 93 } 94 95 public JavaClass(String name, long superclassId, long loaderId, 96 long signersId, long protDomainId, 97 JavaField[] fields, JavaStatic[] statics, 98 int instanceSize) { 99 this(-1L, name, superclassId, loaderId, signersId, 100 protDomainId, fields, statics, instanceSize); 101 } 102 103 public final JavaClass getClazz() { 104 return mySnapshot.getJavaLangClass(); 105 } 106 107 public final int getIdentifierSize() { 108 return mySnapshot.getIdentifierSize(); 109 } 110 111 public final int getMinimumObjectSize() { 112 return mySnapshot.getMinimumObjectSize(); 113 } 114 115 public void resolve(Snapshot snapshot) { 116 if (mySnapshot != null) { 117 return; 118 } 119 mySnapshot = snapshot; 120 121 // Resolve inlined fields. Should be done before resolveSuperclass for correct field counting 122 Snapshot.ClassInlinedFields[] inlinedFields = snapshot.findClassInlinedFields(id); 123 if (inlinedFields != null) { 124 int newCount = fields.length; 125 for (Snapshot.ClassInlinedFields f: inlinedFields) { 126 if (f.synthFieldCount == 0) { 127 // Empty primitive class. just skip it - no data there. 128 continue; 129 } 130 JavaHeapObject clazz = snapshot.findThing(f.fieldClassID); 131 if (clazz instanceof JavaClass fieldClass) { 132 fieldClass.resolve(snapshot); 133 134 // Set new field. 135 fields[f.fieldIndex] = new InlinedJavaField(f.fieldName, 'Q' + fieldClass.getName() + ';', fieldClass); 136 newCount -= (f.synthFieldCount - 1); 137 // Reset invalid fields. 138 for (int i = 1; i < f.synthFieldCount; i++) { 139 fields[f.fieldIndex + i] = null; 140 } 141 } else { 142 // The field class not found. 143 System.out.println("WARNING: class of inlined field not found:" + getName() + "." + f.fieldName); 144 } 145 } 146 147 // Set new fields. 148 JavaField[] newFields = new JavaField[newCount]; 149 int oldIndex = 0; 150 for (int i = 0; i < newFields.length; i++) { 151 while (fields[oldIndex] == null) { 152 oldIndex++; 153 } 154 newFields[i] = fields[oldIndex]; 155 oldIndex++; 156 } 157 fields = newFields; 158 } 159 160 resolveSuperclass(snapshot); 161 if (superclass != null) { 162 ((JavaClass) superclass).addSubclass(this); 163 } 164 165 loader = loader.dereference(snapshot, null); 166 signers = signers.dereference(snapshot, null); 167 protectionDomain = protectionDomain.dereference(snapshot, null); 168 169 for (int i = 0; i < statics.length; i++) { 170 statics[i].resolve(this, snapshot); 171 } 172 snapshot.getJavaLangClass().addInstance(this); 173 super.resolve(snapshot); 174 175 return; 176 } 177 178 /** 179 * Resolve our superclass. This might be called well before 180 * all instances are available (like when reading deferred 181 * instances in a 1.2 dump file :-) Calling this is sufficient 182 * to be able to explore this class' fields. 183 */ 184 public void resolveSuperclass(Snapshot snapshot) { 185 if (superclass == null) { 186 // We must be java.lang.Object, so we have no superclass. 187 } else { 188 totalNumFields = fields.length; 189 superclass = superclass.dereference(snapshot, null); 190 if (superclass == snapshot.getNullThing()) { 191 superclass = null; 192 } else { 193 try { 194 JavaClass sc = (JavaClass) superclass; 195 sc.resolveSuperclass(snapshot); 196 totalNumFields += sc.totalNumFields; 197 } catch (ClassCastException ex) { 198 System.out.println("Warning! Superclass of " + name + " is " + superclass); 199 superclass = null; 200 } 201 } 202 } 203 } 204 205 public boolean isString() { 206 return mySnapshot.getJavaLangString() == this; 207 } 208 209 public boolean isClassLoader() { 210 return mySnapshot.getJavaLangClassLoader().isAssignableFrom(this); 211 } 212 213 /** 214 * Get a numbered field from this class 215 */ 216 public JavaField getField(int i) { 217 if (i < 0 || i >= fields.length) { 218 throw new Error("No field " + i + " for " + name); 219 } 220 return fields[i]; 221 } 222 223 /** 224 * Get the total number of fields that are part of an instance of 225 * this class. That is, include superclasses. 226 */ 227 public int getNumFieldsForInstance() { 228 return totalNumFields; 229 } 230 231 /** 232 * Get a numbered field from all the fields that are part of instance 233 * of this class. That is, include superclasses. 234 */ 235 public JavaField getFieldForInstance(int i) { 236 if (superclass != null) { 237 JavaClass sc = (JavaClass) superclass; 238 if (i < sc.totalNumFields) { 239 return sc.getFieldForInstance(i); 240 } 241 i -= sc.totalNumFields; 242 } 243 return getField(i); 244 } 245 246 /** 247 * Get the class responsible for field i, where i is a field number that 248 * could be passed into getFieldForInstance. 249 * 250 * @see JavaClass.getFieldForInstance() 251 */ 252 public JavaClass getClassForField(int i) { 253 if (superclass != null) { 254 JavaClass sc = (JavaClass) superclass; 255 if (i < sc.totalNumFields) { 256 return sc.getClassForField(i); 257 } 258 } 259 return this; 260 } 261 262 public long getId() { 263 return id; 264 } 265 266 public String getName() { 267 return name; 268 } 269 270 public boolean isArray() { 271 return name.indexOf('[') != -1; 272 } 273 274 public Enumeration<JavaHeapObject> getInstances(boolean includeSubclasses) { 275 if (includeSubclasses) { 276 Enumeration<JavaHeapObject> res = instances.elements(); 277 for (int i = 0; i < subclasses.length; i++) { 278 res = new CompositeEnumeration(res, 279 subclasses[i].getInstances(true)); 280 } 281 return res; 282 } else { 283 return instances.elements(); 284 } 285 } 286 287 /** 288 * @return a count of the instances of this class 289 */ 290 public int getInstancesCount(boolean includeSubclasses) { 291 int result = instances.size(); 292 if (includeSubclasses) { 293 for (int i = 0; i < subclasses.length; i++) { 294 result += subclasses[i].getInstancesCount(includeSubclasses); 295 } 296 } 297 return result; 298 } 299 300 public JavaClass[] getSubclasses() { 301 return subclasses; 302 } 303 304 /** 305 * This can only safely be called after resolve() 306 */ 307 public JavaClass getSuperclass() { 308 return (JavaClass) superclass; 309 } 310 311 /** 312 * This can only safely be called after resolve() 313 */ 314 public JavaThing getLoader() { 315 return loader; 316 } 317 318 /** 319 * This can only safely be called after resolve() 320 */ 321 public boolean isBootstrap() { 322 return loader == mySnapshot.getNullThing(); 323 } 324 325 /** 326 * This can only safely be called after resolve() 327 */ 328 public JavaThing getSigners() { 329 return signers; 330 } 331 332 /** 333 * This can only safely be called after resolve() 334 */ 335 public JavaThing getProtectionDomain() { 336 return protectionDomain; 337 } 338 339 public JavaField[] getFields() { 340 return fields; 341 } 342 343 /** 344 * Includes superclass fields 345 */ 346 public JavaField[] getFieldsForInstance() { 347 Vector<JavaField> v = new Vector<JavaField>(); 348 addFields(v); 349 JavaField[] result = new JavaField[v.size()]; 350 for (int i = 0; i < v.size(); i++) { 351 result[i] = v.elementAt(i); 352 } 353 return result; 354 } 355 356 357 public JavaStatic[] getStatics() { 358 return statics; 359 } 360 361 // returns value of static field of given name 362 public JavaThing getStaticField(String name) { 363 for (int i = 0; i < statics.length; i++) { 364 JavaStatic s = statics[i]; 365 if (s.getField().getName().equals(name)) { 366 return s.getValue(); 367 } 368 } 369 return null; 370 } 371 372 public String toString() { 373 return "class " + name; 374 } 375 376 public int compareTo(JavaThing other) { 377 if (other instanceof JavaClass) { 378 return name.compareTo(((JavaClass) other).name); 379 } 380 return super.compareTo(other); 381 } 382 383 384 /** 385 * @return true iff a variable of type this is assignable from an instance 386 * of other 387 */ 388 public boolean isAssignableFrom(JavaClass other) { 389 if (this == other) { 390 return true; 391 } else if (other == null) { 392 return false; 393 } else { 394 return isAssignableFrom((JavaClass) other.superclass); 395 // Trivial tail recursion: I have faith in javac. 396 } 397 } 398 399 /** 400 * Describe the reference that this thing has to target. This will only 401 * be called if target is in the array returned by getChildrenForRootset. 402 */ 403 public String describeReferenceTo(JavaThing target, Snapshot ss) { 404 for (int i = 0; i < statics.length; i++) { 405 JavaField f = statics[i].getField(); 406 if (f.hasId()) { 407 JavaThing other = statics[i].getValue(); 408 if (other == target) { 409 return "static field " + f.getName(); 410 } 411 } 412 } 413 return super.describeReferenceTo(target, ss); 414 } 415 416 /** 417 * @return the size of an instance of this class. Gives 0 for an array 418 * type. 419 */ 420 public int getInstanceSize() { 421 return instanceSize + mySnapshot.getMinimumObjectSize(); 422 } 423 424 public int getInlinedInstanceSize() { 425 if (inlinedInstanceSize < 0) { 426 int size = 0; 427 for (JavaField f: fields) { 428 if (f instanceof InlinedJavaField inlinedField) { 429 size += inlinedField.getInlinedFieldClass().getInlinedInstanceSize(); 430 } else { 431 char sig = f.getSignature().charAt(0); 432 switch (sig) { 433 case 'Q': { 434 System.out.println("WARNING: (getInlinedInstanceSize) field " 435 + getClazz().getName() + "." + f.getName() 436 + " is not inlined, but has Q-signature: " + f.getSignature()); 437 } // continue as 'L' object 438 case 'L': 439 case '[': 440 size += mySnapshot.getIdentifierSize(); 441 break; 442 case 'B': 443 case 'Z': 444 size += 1; 445 break; 446 case 'C': 447 case 'S': 448 size += 2; 449 break; 450 case 'I': 451 case 'F': 452 size += 4; 453 break; 454 case 'J': 455 case 'D': 456 size += 8; 457 break; 458 default: 459 throw new RuntimeException("unknown field type: " + sig); 460 } 461 } 462 } 463 inlinedInstanceSize = size; 464 } 465 return inlinedInstanceSize; 466 } 467 468 /** 469 * @return The size of all instances of this class. Correctly handles 470 * arrays. 471 */ 472 public long getTotalInstanceSize() { 473 int count = instances.size(); 474 if (count == 0 || !isArray()) { 475 return count * instanceSize; 476 } 477 478 // array class and non-zero count, we have to 479 // get the size of each instance and sum it 480 long result = 0; 481 for (int i = 0; i < count; i++) { 482 JavaThing t = (JavaThing) instances.elementAt(i); 483 result += t.getSize(); 484 } 485 return result; 486 } 487 488 /** 489 * @return the size of this object 490 */ 491 @Override 492 public long getSize() { 493 JavaClass cl = mySnapshot.getJavaLangClass(); 494 if (cl == null) { 495 return 0; 496 } else { 497 return cl.getInstanceSize(); 498 } 499 } 500 501 public void visitReferencedObjects(JavaHeapObjectVisitor v) { 502 super.visitReferencedObjects(v); 503 JavaHeapObject sc = getSuperclass(); 504 if (sc != null) v.visit(getSuperclass()); 505 506 JavaThing other; 507 other = getLoader(); 508 if (other instanceof JavaHeapObject) { 509 v.visit((JavaHeapObject)other); 510 } 511 other = getSigners(); 512 if (other instanceof JavaHeapObject) { 513 v.visit((JavaHeapObject)other); 514 } 515 other = getProtectionDomain(); 516 if (other instanceof JavaHeapObject) { 517 v.visit((JavaHeapObject)other); 518 } 519 520 for (int i = 0; i < statics.length; i++) { 521 JavaField f = statics[i].getField(); 522 if (!v.exclude(this, f) && f.hasId()) { 523 other = statics[i].getValue(); 524 if (other instanceof JavaHeapObject) { 525 v.visit((JavaHeapObject) other); 526 } 527 } 528 } 529 } 530 531 // package-privates below this point 532 final ReadBuffer getReadBuffer() { 533 return mySnapshot.getReadBuffer(); 534 } 535 536 final void setNew(JavaHeapObject obj, boolean flag) { 537 mySnapshot.setNew(obj, flag); 538 } 539 540 final boolean isNew(JavaHeapObject obj) { 541 return mySnapshot.isNew(obj); 542 } 543 544 final StackTrace getSiteTrace(JavaHeapObject obj) { 545 return mySnapshot.getSiteTrace(obj); 546 } 547 548 final void addReferenceFromRoot(Root root, JavaHeapObject obj) { 549 mySnapshot.addReferenceFromRoot(root, obj); 550 } 551 552 final Root getRoot(JavaHeapObject obj) { 553 return mySnapshot.getRoot(obj); 554 } 555 556 final Snapshot getSnapshot() { 557 return mySnapshot; 558 } 559 560 void addInstance(JavaHeapObject inst) { 561 instances.addElement(inst); 562 } 563 564 // Internals only below this point 565 private void addFields(Vector<JavaField> v) { 566 if (superclass != null) { 567 ((JavaClass) superclass).addFields(v); 568 } 569 for (int i = 0; i < fields.length; i++) { 570 v.addElement(fields[i]); 571 } 572 } 573 574 private void addSubclassInstances(Vector<JavaHeapObject> v) { 575 for (int i = 0; i < subclasses.length; i++) { 576 subclasses[i].addSubclassInstances(v); 577 } 578 for (int i = 0; i < instances.size(); i++) { 579 v.addElement(instances.elementAt(i)); 580 } 581 } 582 583 private void addSubclass(JavaClass sub) { 584 JavaClass newValue[] = new JavaClass[subclasses.length + 1]; 585 System.arraycopy(subclasses, 0, newValue, 0, subclasses.length); 586 newValue[subclasses.length] = sub; 587 subclasses = newValue; 588 } 589 }