1 /* 2 * Copyright (c) 2024, 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 import java.io.PrintStream; 25 import java.util.ArrayList; 26 import java.util.Collections; 27 import java.util.HashMap; 28 import java.util.List; 29 30 import javax.management.RuntimeErrorException; 31 32 import jdk.test.lib.Asserts; 33 import jdk.test.lib.process.OutputAnalyzer; 34 import jdk.test.lib.process.ProcessTools; 35 36 public class FieldLayoutAnalyzer { 37 38 // Mutable wrapper around log output to manage the cursor while parsing 39 static class LogOutput { 40 List<String> lines; 41 int cursor; 42 43 public LogOutput(List<String> lines) { 44 this.lines = lines; 45 cursor = 0; 46 } 47 48 String getCurrentLine() { return lines.get(cursor); } 49 String get(int idx) { return lines.get(idx); } 50 int size() { return lines.size(); } 51 void moveToNextLine() { cursor = cursor + 1; } 52 boolean hasMoreLines() { return cursor < lines.size(); } 53 } 54 55 static enum BlockType { 56 RESERVED, 57 INHERITED, 58 EMPTY, 59 REGULAR, 60 PADDING, 61 FLAT, 62 NULL_MARKER; 63 64 static BlockType parseType(String s) { 65 switch(s) { 66 case "RESERVED" : return RESERVED; 67 case "INHERITED" : return INHERITED; 68 case "EMPTY" : return EMPTY; 69 case "REGULAR" : return REGULAR; 70 case "PADDING" : return PADDING; 71 case "FLAT" : return FLAT; 72 case "NULL_MARKER" : return NULL_MARKER; 73 default: 74 throw new RuntimeException("Unknown block type: " + s); 75 } 76 } 77 } 78 79 static enum LayoutKind { 80 NON_FLAT, 81 NON_ATOMIC_FLAT, 82 ATOMIC_FLAT, 83 NULLABLE_FLAT; 84 85 static LayoutKind parseLayoutKind(String s) { 86 switch(s) { 87 case "" : return NON_FLAT; 88 case "NON_ATOMIC_FLAT" : return NON_ATOMIC_FLAT; 89 case "ATOMIC_FLAT" : return ATOMIC_FLAT; 90 case "NULLABLE_ATOMIC_FLAT" : return NULLABLE_FLAT; 91 default: 92 throw new RuntimeException("Unknown layout kind: " + s); 93 } 94 } 95 } 96 97 static public record FieldBlock (int offset, 98 BlockType type, 99 int size, 100 int alignment, 101 String name, 102 String signature, 103 String fieldClass, 104 LayoutKind layoutKind) { 105 106 static FieldBlock createSpecialBlock(int offset, BlockType type, int size, int alignment) { 107 return new FieldBlock(offset, type, size, alignment, null, null, null, LayoutKind.NON_FLAT); 108 } 109 110 static FieldBlock createJavaFieldBlock(int offset, BlockType type, int size, int alignment, String name, String signature, String fieldClass, LayoutKind layoutKind) { 111 return new FieldBlock(offset, type, size, alignment, name, signature, fieldClass, layoutKind); 112 } 113 114 void print(PrintStream out) { 115 out.println("Offset=" + offset+ 116 " type=" + type + 117 " size=" + size + 118 " alignment=" + alignment + 119 " name=" + name + 120 " signature=" + signature + 121 " fieldClass=" + fieldClass); 122 } 123 124 boolean isFlat() { return type == BlockType.FLAT; } // Warning: always return false for inherited fields, even flat ones 125 126 static FieldBlock parseField(String line) { 127 String[] fieldLine = line.split("\\s+"); 128 // for(String s : fieldLine) { 129 // System.out.print("["+s+"]"); // debugging statement to be removed 130 // } 131 // System.out.println(); 132 int offset = Integer.parseInt(fieldLine[1].substring(1, fieldLine[1].length())); 133 BlockType type = BlockType.parseType(fieldLine[2]); 134 String[] size_align = fieldLine[3].split("/"); 135 int size = Integer.parseInt(size_align[0]); 136 int alignment = -1; 137 if (type != BlockType.RESERVED) { 138 alignment = Integer.parseInt(size_align[1]); 139 } else { 140 Asserts.assertTrue(size_align[1].equals("-")); 141 } 142 FieldBlock block = null; 143 switch(type) { 144 case BlockType.RESERVED: 145 case BlockType.EMPTY: 146 case BlockType.PADDING: { 147 block = FieldBlock.createSpecialBlock(offset, type, size, alignment); 148 break; 149 } 150 case BlockType.REGULAR: 151 case BlockType.INHERITED: 152 case BlockType.FLAT: { 153 String name = fieldLine[4]; 154 String signature = fieldLine[5]; 155 String fieldClass = ""; 156 String layoutKind = ""; 157 int nullMarkerOffset = -1; 158 if (type == BlockType.FLAT) { 159 fieldClass = fieldLine[6]; 160 layoutKind = fieldLine[7]; 161 } 162 block = FieldBlock.createJavaFieldBlock(offset, type, size, alignment, name, signature, fieldClass, LayoutKind.parseLayoutKind(layoutKind)); 163 break; 164 } 165 case BlockType.NULL_MARKER: { 166 block = FieldBlock.createSpecialBlock(offset, type, size, alignment); 167 break; 168 } 169 } 170 Asserts.assertNotNull(block); 171 return block; 172 } 173 174 } 175 176 static class ClassLayout { 177 String name; 178 String superName; 179 boolean isValue; 180 int instanceSize; 181 int payloadSize; 182 int payloadAlignment; 183 int firstFieldOffset; 184 int nonAtomicLayoutSize; // -1 if no non-nullable layout 185 int nonAtomicLayoutAlignment; // -1 if no non-nullable layout 186 int atomicLayoutSize; // -1 if no atomic layout 187 int atomicLayoutAlignment; // -1 if no atomic layout 188 int nullableLayoutSize; // -1 if no nullable layout 189 int nullableLayoutAlignment; // -1 if no nullable layout 190 int nullMarkerOffset; // -1 if no nullable layout 191 String[] lines; 192 ArrayList<FieldBlock> staticFields; 193 ArrayList<FieldBlock> nonStaticFields; 194 195 private ClassLayout() { 196 staticFields = new ArrayList<FieldBlock>(); 197 nonStaticFields = new ArrayList<FieldBlock>(); 198 } 199 200 boolean hasNonAtomicLayout() { return nonAtomicLayoutSize != -1; } 201 boolean hasAtomicLayout() { return atomicLayoutSize != -1; } 202 boolean hasNullableLayout() { return nullableLayoutSize != -1; } 203 boolean hasNullMarker() {return nullMarkerOffset != -1; } 204 205 int getSize(LayoutKind layoutKind) { 206 switch(layoutKind) { 207 case NON_FLAT: 208 throw new RuntimeException("Should not be called on non-flat fields"); 209 case NON_ATOMIC_FLAT: 210 Asserts.assertTrue(nonAtomicLayoutSize != -1); 211 return nonAtomicLayoutSize; 212 case ATOMIC_FLAT: 213 Asserts.assertTrue(atomicLayoutSize != -1); 214 return atomicLayoutSize; 215 case NULLABLE_FLAT: 216 Asserts.assertTrue(nullableLayoutSize != -1); 217 return nullableLayoutSize; 218 default: 219 throw new RuntimeException("Unknown LayoutKind " + layoutKind); 220 } 221 } 222 223 int getAlignment(LayoutKind layoutKind) { 224 switch(layoutKind) { 225 case NON_FLAT: 226 throw new RuntimeException("Should not be called on non-flat fields"); 227 case NON_ATOMIC_FLAT: 228 Asserts.assertTrue(nonAtomicLayoutSize != -1); 229 return nonAtomicLayoutAlignment; 230 case ATOMIC_FLAT: 231 Asserts.assertTrue(atomicLayoutSize != -1); 232 return atomicLayoutAlignment; 233 case NULLABLE_FLAT: 234 Asserts.assertTrue(nullableLayoutSize != -1); 235 return nullableLayoutAlignment; 236 default: 237 throw new RuntimeException("Unknown LayoutKind " + layoutKind); 238 } 239 } 240 241 void processField(String line, boolean isStatic) { 242 FieldBlock block = FieldBlock.parseField(line); 243 if (isStatic) { 244 Asserts.assertTrue(block.type != BlockType.INHERITED); // static fields cannotbe inherited 245 staticFields.add(block); 246 } else { 247 nonStaticFields.add(block); 248 } 249 } 250 251 static ClassLayout parseClassLayout(LogOutput lo) { 252 ClassLayout cl = new ClassLayout(); 253 // Parsing class name 254 Asserts.assertTrue(lo.getCurrentLine().startsWith("Layout of class"), lo.getCurrentLine()); 255 String[] first = lo.getCurrentLine().split("\\s+"); 256 cl.name = first[3]; 257 if (first.length == 6) { 258 Asserts.assertEquals(first[4], "extends"); 259 cl.superName = first[5]; 260 } else { 261 cl.superName = null; 262 } 263 // System.out.println("Class name: " + cl.name); 264 lo.moveToNextLine(); 265 Asserts.assertTrue(lo.getCurrentLine().startsWith("Instance fields:"), lo.getCurrentLine()); 266 lo.moveToNextLine(); 267 // Parsing instance fields 268 while (lo.getCurrentLine().startsWith(" @")) { 269 cl.processField(lo.getCurrentLine(), false); 270 lo.moveToNextLine(); 271 } 272 Asserts.assertTrue(lo.getCurrentLine().startsWith("Static fields:"), lo.getCurrentLine()); 273 lo.moveToNextLine(); 274 // Parsing static fields 275 while (lo.getCurrentLine().startsWith(" @")) { 276 cl.processField(lo.getCurrentLine(), true); 277 lo.moveToNextLine(); 278 } 279 Asserts.assertTrue(lo.getCurrentLine().startsWith("Instance size ="), lo.getCurrentLine()); 280 String[] sizeLine = lo.getCurrentLine().split("\\s+"); 281 cl.instanceSize = Integer.parseInt(sizeLine[3]); 282 lo.moveToNextLine(); 283 if (lo.getCurrentLine().startsWith("First field offset =")) { 284 // The class is a value class, more lines to parse 285 cl.isValue = true; 286 // First field offset = xx 287 String[] firstOffsetLine = lo.getCurrentLine().split("\\s+"); 288 cl.firstFieldOffset = Integer.parseInt(firstOffsetLine[4]); 289 lo.moveToNextLine(); 290 // Payload layout: x/y 291 Asserts.assertTrue(lo.getCurrentLine().startsWith("Payload layout")); 292 String[] payloadLayoutLine = lo.getCurrentLine().split("\\s+"); 293 String[] size_align = payloadLayoutLine[2].split("/"); 294 cl.payloadSize = Integer.parseInt(size_align[0]); 295 cl.payloadAlignment = Integer.parseInt(size_align[1]); 296 lo.moveToNextLine(); 297 // Non atomic flat layout: x/y 298 Asserts.assertTrue(lo.getCurrentLine().startsWith("Non atomic flat layout")); 299 String[] nonAtomicLayoutLine = lo.getCurrentLine().split("\\s+"); 300 size_align = nonAtomicLayoutLine[4].split("/"); 301 if (size_align[0].contentEquals("-")) { 302 Asserts.assertTrue(size_align[1].contentEquals("-"), "Size/Alignment mismatch"); 303 cl.nonAtomicLayoutSize = -1; 304 cl.nonAtomicLayoutAlignment = -1; 305 } else { 306 cl.nonAtomicLayoutSize = Integer.parseInt(size_align[0]); 307 cl.nonAtomicLayoutAlignment = Integer.parseInt(size_align[1]); 308 } 309 lo.moveToNextLine(); 310 // Atomic flat layout: x/y 311 Asserts.assertTrue(lo.getCurrentLine().startsWith("Atomic flat layout")); 312 String[] atomicLayoutLine = lo.getCurrentLine().split("\\s+"); 313 size_align = atomicLayoutLine[3].split("/"); 314 if (size_align[0].contentEquals("-")) { 315 Asserts.assertTrue(size_align[1].contentEquals("-"), "Size/Alignment mismatch"); 316 cl.atomicLayoutSize = -1; 317 cl.atomicLayoutAlignment = -1; 318 } else { 319 cl.atomicLayoutSize = Integer.parseInt(size_align[0]); 320 cl.atomicLayoutAlignment = Integer.parseInt(size_align[1]); 321 } 322 lo.moveToNextLine(); 323 // Nullable flat layout: x/y 324 Asserts.assertTrue(lo.getCurrentLine().startsWith("Nullable flat layout")); 325 String[] nullableLayoutLine = lo.getCurrentLine().split("\\s+"); 326 size_align = nullableLayoutLine[3].split("/"); 327 if (size_align[0].contentEquals("-")) { 328 Asserts.assertTrue(size_align[1].contentEquals("-"), "Size/Alignment mismatch"); 329 cl.nullableLayoutSize = -1; 330 cl.nullableLayoutAlignment = -1; 331 } else { 332 cl.nullableLayoutSize = Integer.parseInt(size_align[0]); 333 cl.nullableLayoutAlignment = Integer.parseInt(size_align[1]); 334 } 335 lo.moveToNextLine(); 336 // Null marker offset = 15 (if class has a nullable flat layout) 337 if (cl.nullableLayoutSize != -1) { 338 Asserts.assertTrue(lo.getCurrentLine().startsWith("Null marker offset")); 339 String[] nullMarkerLine = lo.getCurrentLine().split("\\s+"); 340 cl.nullMarkerOffset = Integer.parseInt(nullMarkerLine[4]); 341 lo.moveToNextLine(); 342 } else { 343 cl.nullMarkerOffset = -1; 344 } 345 } else { 346 cl.isValue = false; 347 } 348 349 Asserts.assertTrue(lo.getCurrentLine().startsWith("---"), lo.getCurrentLine()); 350 lo.moveToNextLine(); 351 return cl; 352 } 353 354 FieldBlock getFieldAtOffset(int offset, boolean isStatic) { 355 ArrayList<FieldBlock> fields = isStatic ? staticFields : nonStaticFields; 356 for (FieldBlock block : fields) { 357 if (block.offset == offset) return block; 358 } 359 throw new RuntimeException("No " + (isStatic ? "static" : "nonstatic") + " field found at offset "+ offset); 360 } 361 362 FieldBlock getFieldFromName(String fieldName, boolean isStatic) { 363 FieldBlock block = getFieldFromNameOrNull(fieldName, isStatic); 364 if (block == null) { 365 throw new RuntimeException("No " + (isStatic ? "static" : "nonstatic") + " field found with name "+ fieldName); 366 } 367 return block; 368 } 369 370 FieldBlock getFieldFromNameOrNull(String fieldName, boolean isStatic) { 371 Asserts.assertTrue(fieldName != null); 372 ArrayList<FieldBlock> fields = isStatic ? staticFields : nonStaticFields; 373 for (FieldBlock block : fields) { 374 if (block.name() == null) continue; 375 String n = block.name().substring(1, block.name().length() - 1); // in the log, name is surrounded by double quotes 376 if (fieldName.equals(n)) return block; 377 } 378 return null; 379 } 380 381 } 382 383 ArrayList<ClassLayout> layouts; 384 int oopSize; 385 386 static String signatureToName(String sig) { 387 Asserts.assertTrue((sig.charAt(0) == 'L')); 388 Asserts.assertTrue((sig.charAt(sig.length() - 1) == ';')); 389 return sig.substring(1, sig.length() - 1); 390 } 391 392 private FieldLayoutAnalyzer() { 393 layouts = new ArrayList<ClassLayout>(); 394 } 395 396 public static FieldLayoutAnalyzer createFieldLayoutAnalyzer(LogOutput lo) { 397 FieldLayoutAnalyzer fla = new FieldLayoutAnalyzer(); 398 fla.generate(lo); 399 return fla; 400 } 401 402 ClassLayout getClassLayout(String name) { 403 for(ClassLayout layout : layouts) { 404 if (layout.name.equals(name)) return layout; 405 } 406 return null; 407 } 408 409 ClassLayout getClassLayoutFromName(String name) { 410 for(ClassLayout layout : layouts) { 411 String sub = layout.name.substring(0, layout.name.indexOf('@')); 412 if (name.equals(sub)) return layout; 413 } 414 return null; 415 } 416 417 void checkOffsetOnFields(ArrayList<FieldBlock> fields) { 418 HashMap<Integer, FieldBlock> map = new HashMap<Integer, FieldBlock>(); 419 for (FieldBlock fb : fields) { 420 Asserts.assertFalse(map.containsKey(fb.offset()), "Duplicate offset at " + fb.offset()); 421 map.put(fb.offset(), fb); 422 } 423 } 424 425 void checkOffsets() { 426 for (ClassLayout layout : layouts) { 427 try { 428 checkOffsetOnFields(layout.staticFields); 429 checkOffsetOnFields(layout.nonStaticFields); 430 } catch(Throwable t) { 431 System.out.println("Unexpection exception when checking offsets in class " + layout.name); 432 throw t; 433 } 434 } 435 } 436 437 void checkNoOverlapOnFields(ArrayList<FieldBlock> fields) { 438 for (int i = 0; i < fields.size() - 1; i++) { 439 FieldBlock f0 = fields.get(i); 440 FieldBlock f1 = fields.get(i + 1); 441 if (f0.offset + f0.size < f1.offset) { 442 throw new RuntimeException("Hole issue found at offset " + f1.offset); 443 } else if (f0.offset + f0.size > f1.offset) { 444 throw new RuntimeException("Overlap issue found at offset " + f1.offset); 445 } 446 } 447 } 448 449 void checkNoOverlap() { 450 for (ClassLayout layout : layouts) { 451 try { 452 checkNoOverlapOnFields(layout.staticFields); 453 checkNoOverlapOnFields(layout.nonStaticFields); 454 } catch(Throwable t) { 455 System.out.println("Unexpection exception when checking for overlaps/holes in class " + layout.name); 456 throw t; 457 } 458 } 459 } 460 461 void checkSizeAndAlignmentForField(FieldBlock block) { 462 Asserts.assertTrue(block.size() > 0); 463 if (block.type == BlockType.RESERVED) { 464 Asserts.assertTrue(block.alignment == -1); 465 return; 466 } 467 if (block.type == BlockType.EMPTY || block.type == BlockType.PADDING 468 || block.type == BlockType.NULL_MARKER) { 469 Asserts.assertTrue(block.alignment == 1, "alignment = " + block.alignment); 470 return; 471 } 472 473 switch(block.signature()) { 474 case "Z" : 475 case "B" : Asserts.assertTrue(block.size() == 1); 476 Asserts.assertTrue(block.alignment() == 1); 477 break; 478 case "S" : 479 case "C" : Asserts.assertTrue(block.size() == 2); 480 Asserts.assertTrue(block.alignment() == 2); 481 break; 482 case "F" : 483 case "I" : Asserts.assertTrue(block.size() == 4); 484 Asserts.assertTrue(block.alignment() == 4); 485 break; 486 case "J" : 487 case "D" : Asserts.assertTrue(block.size() == 8); 488 Asserts.assertTrue(block.alignment() == 8); 489 break; 490 default: { 491 if (block.signature().startsWith("[")) { 492 Asserts.assertEquals(oopSize, block.size()); 493 } else if (block.signature().startsWith("L")) { 494 if (block.type == BlockType.INHERITED) { 495 // Skip for now, will be verified when checking the class declaring the field 496 } else if (block.type == BlockType.REGULAR) { 497 Asserts.assertEquals(oopSize, block.size()); 498 } else { 499 Asserts.assertEquals(BlockType.FLAT, block.type); 500 ClassLayout fcl = getClassLayout(block.fieldClass); 501 Asserts.assertNotNull(fcl); 502 Asserts.assertEquals(block.size(), fcl.getSize(block.layoutKind)); 503 Asserts.assertEquals(block.alignment(), fcl.getAlignment(block.layoutKind)); 504 } 505 } else { 506 throw new RuntimeException("Unknown signature type: " + block.signature); 507 } 508 } 509 Asserts.assertTrue(block.offset % block.alignment == 0); 510 } 511 } 512 513 void checkSizeAndAlignment() { 514 for (ClassLayout layout : layouts) { 515 try { 516 for (FieldBlock block : layout.staticFields) { 517 checkSizeAndAlignmentForField(block); 518 } 519 } catch(Throwable t) { 520 System.out.println("Unexpected exception when checking size and alignment in static fields of class " + layout.name); 521 throw t; 522 } 523 try { 524 for (FieldBlock block : layout.nonStaticFields) { 525 checkSizeAndAlignmentForField(block); 526 } 527 } catch(Throwable t) { 528 System.out.println("Unexpected exception when checking size and alignment in non-static fields of class " + layout.name); 529 throw t; 530 } 531 } 532 } 533 534 // Verify that fields marked as INHERITED are declared in a super class 535 void checkInheritedFields() { 536 for (ClassLayout layout : layouts) { 537 try { 538 // Preparing the list of ClassLayout of super classes 539 ArrayList<ClassLayout> supers = new ArrayList<ClassLayout>(); 540 String className = layout.superName; 541 while (className != null) { 542 ClassLayout cl = getClassLayout(className); 543 supers.add(cl); 544 className = cl.superName; 545 } 546 for (FieldBlock field : layout.nonStaticFields) { 547 if (field.type == BlockType.INHERITED) { 548 int i = 0; 549 boolean found = false; 550 FieldBlock b = null; 551 while(i < supers.size() && !found) { 552 b = supers.get(i).getFieldAtOffset(field.offset, false); 553 if (b.type != BlockType.INHERITED) found = true; 554 i++; 555 } 556 String location = new String(" at " + layout.name + " offset " + field.offset()); 557 Asserts.assertTrue(found, "No declaration found for an inherited field " + location); 558 Asserts.assertNotEquals(field.type, BlockType.EMPTY, location); 559 Asserts.assertEquals(field.size, b.size, location); 560 Asserts.assertEquals(field.alignment, b.alignment, location ); 561 Asserts.assertEquals(field.name(), b.name(), location); 562 Asserts.assertEquals(field.signature(), b.signature(), location); 563 } 564 } 565 } catch(Throwable t) { 566 System.out.println("Unexpexted exception when checking inherited fields in class " + layout.name); 567 } 568 } 569 } 570 571 static class Node { 572 ClassLayout classLayout; 573 Node superClass; 574 ArrayList<Node> subClasses = new ArrayList<Node>(); 575 } 576 577 // Verify that all fields declared in a class are present in all subclass 578 void checkSubClasses() { 579 // Generating the class inheritance graph 580 HashMap<String, Node> nodes = new HashMap<String, Node>(); 581 for (ClassLayout layout : layouts) { 582 try { 583 if (layout.name.contains("$$Lambda@0")) continue; // Skipping lambda classes 584 Node current = nodes.get(layout.name); 585 if (current == null) { 586 current = new Node(); 587 nodes.put(layout.name, current); 588 } 589 if (current.classLayout == null) { 590 current.classLayout = layout; 591 } else { 592 Asserts.assertEQ(current.classLayout, layout); 593 } 594 if (layout.superName != null) { 595 Node superNode = nodes.get(layout.superName); 596 if (superNode == null) { 597 superNode = new Node(); 598 superNode.subClasses.add(current); 599 nodes.put(layout.superName, superNode); 600 } 601 superNode.subClasses.add(current); 602 } 603 } catch(Throwable t) { 604 System.out.println("Unexpected exception when generating list of sub-classes of class " + layout.name); 605 throw t; 606 } 607 } 608 // Field verification 609 for (Node node : nodes.values()) { 610 ClassLayout layout = node.classLayout; 611 for (FieldBlock block : layout.nonStaticFields) { 612 if (block.offset() == 0) continue; // Skip object header 613 if (block.type() == BlockType.EMPTY) continue; // Empty spaces can be used by subclasses 614 if (block.type() == BlockType.PADDING) continue; // PADDING should have a finer inspection, preserved for @Contended and other imperative padding, and relaxed for abstract value conservative padding 615 if (block.type() == BlockType.NULL_MARKER) continue; 616 // A special case for PADDING might be needed too => must NOT be used in subclasses 617 for (Node subnode : node.subClasses) { 618 try { 619 checkFieldInClass(block, subnode); 620 } catch(Throwable t) { 621 System.out.println("Unexpected exception when checking subclass " + subnode.classLayout.name + " of class " + layout.name); 622 throw t; 623 } 624 } 625 } 626 } 627 } 628 629 void checkFieldInClass(FieldBlock block, Node node) { 630 FieldBlock b = node.classLayout.getFieldAtOffset(block.offset, false); 631 Asserts.assertTrue(b.type == BlockType.INHERITED); 632 Asserts.assertEquals(b.signature(), block.signature()); 633 Asserts.assertEquals(b.name(), block.name()); 634 Asserts.assertEquals(b.size(), block.size()); 635 Asserts.assertTrue(b.alignment() == block.alignment()); 636 for (Node subnode : node.subClasses) { 637 checkFieldInClass(block, subnode); 638 } 639 } 640 641 void checkNullMarkers() { 642 for (ClassLayout layout : layouts) { 643 try { 644 BlockType last_type = BlockType.RESERVED; 645 boolean has_empty_slot = false; 646 for (FieldBlock block : layout.nonStaticFields) { 647 last_type = block.type; 648 if (block.type() == BlockType.NULL_MARKER) { 649 Asserts.assertTrue(layout.hasNullMarker()); 650 Asserts.assertTrue(layout.hasNullableLayout()); 651 Asserts.assertEQ(block.offset(), layout.nullMarkerOffset); 652 } 653 if (block.type() == BlockType.EMPTY) has_empty_slot = true; 654 } 655 // null marker should not be added at the end of the layout if there's an empty slot 656 Asserts.assertTrue(last_type != BlockType.NULL_MARKER || has_empty_slot == false, 657 "Problem detected in layout of class " + layout.name); 658 // static layout => must not have NULL_MARKERS because static fields are never flat 659 for (FieldBlock block : layout.staticFields) { 660 Asserts.assertNotEquals(block.type(), BlockType.NULL_MARKER); 661 Asserts.assertNotEquals(block.type(), BlockType.FLAT); 662 } 663 } catch(Throwable t) { 664 System.out.println("Unexpected exception while checking null markers in class " + layout.name); 665 throw t; 666 } 667 } 668 } 669 670 void check() { 671 checkOffsets(); 672 checkNoOverlap(); 673 checkSizeAndAlignment(); 674 checkInheritedFields(); 675 checkSubClasses(); 676 checkNullMarkers(); 677 } 678 679 private void generate(LogOutput lo) { 680 try { 681 while (lo.hasMoreLines()) { 682 if (lo.getCurrentLine().startsWith("Heap oop size = ")) { 683 String[] oopSizeLine = lo.getCurrentLine().split("\\s+"); 684 oopSize = Integer.parseInt(oopSizeLine[4]); 685 Asserts.assertTrue(oopSize == 4 || oopSize == 8); 686 } 687 if (lo.getCurrentLine().startsWith("Layout of class")) { 688 ClassLayout cl = ClassLayout.parseClassLayout(lo); 689 layouts.add(cl); 690 } else { 691 lo.moveToNextLine(); // skipping line 692 } 693 } 694 Asserts.assertTrue(oopSize != 0); 695 } catch (Throwable t) { 696 System.out.println("Error while processing line: " + lo.getCurrentLine()); 697 throw t; 698 } 699 } 700 }