1 /* 2 * Copyright (c) 1997, 2023, 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 java.util.Objects; 35 36 /** 37 * An array of values, that is, an array of ints, boolean, floats, etc. 38 * or flat array of primitive objects. 39 * 40 * @author Bill Foote 41 */ 42 public class JavaValueArray extends JavaLazyReadObject 43 /*imports*/ implements ArrayTypeCodes { 44 45 private static int elementSize(byte type) { 46 switch (type) { 47 case 'B': 48 case 'Z': 49 return 1; 50 case 'C': 51 case 'S': 52 return 2; 53 case 'I': 54 case 'F': 55 return 4; 56 case 'J': 57 case 'D': 58 return 8; 59 default: 60 throw new RuntimeException("invalid array element type: " + type); 61 } 62 } 63 64 /* 65 * Java primitive array record (HPROF_GC_PRIM_ARRAY_DUMP) looks 66 * as below: 67 * 68 * object ID 69 * stack trace serial number (int) 70 * number of elements (int) 71 * element type (byte) 72 * array data 73 */ 74 @Override 75 protected final long readValueLength() throws IOException { 76 long offset = getOffset() + idSize() + 4; 77 // length of the array in elements 78 long len = buf().getInt(offset); 79 // byte length of array 80 return len * elementSize(getRealElementType()); 81 } 82 83 private long dataStartOffset() { 84 return getOffset() + idSize() + 4 + 4 + 1; 85 } 86 87 88 @Override 89 protected final JavaThing[] readValue() throws IOException { 90 int len = getLength(); 91 long offset = dataStartOffset(); 92 93 JavaThing[] res = new JavaThing[len]; 94 synchronized (buf()) { 95 switch (getElementType()) { 96 case 'Z': { 97 for (int i = 0; i < len; i++) { 98 res[i] = new JavaBoolean(booleanAt(offset)); 99 offset += 1; 100 } 101 return res; 102 } 103 case 'B': { 104 for (int i = 0; i < len; i++) { 105 res[i] = new JavaByte(byteAt(offset)); 106 offset += 1; 107 } 108 return res; 109 } 110 case 'C': { 111 for (int i = 0; i < len; i++) { 112 res[i] = new JavaChar(charAt(offset)); 113 offset += 2; 114 } 115 return res; 116 } 117 case 'S': { 118 for (int i = 0; i < len; i++) { 119 res[i] = new JavaShort(shortAt(offset)); 120 offset += 2; 121 } 122 return res; 123 } 124 case 'I': { 125 for (int i = 0; i < len; i++) { 126 res[i] = new JavaInt(intAt(offset)); 127 offset += 4; 128 } 129 return res; 130 } 131 case 'J': { 132 for (int i = 0; i < len; i++) { 133 res[i] = new JavaLong(longAt(offset)); 134 offset += 8; 135 } 136 return res; 137 } 138 case 'F': { 139 for (int i = 0; i < len; i++) { 140 res[i] = new JavaFloat(floatAt(offset)); 141 offset += 4; 142 } 143 return res; 144 } 145 case 'D': { 146 for (int i = 0; i < len; i++) { 147 res[i] = new JavaDouble(doubleAt(offset)); 148 offset += 8; 149 } 150 return res; 151 } 152 case 'Q': { 153 for (int i = 0; i < len; i++) { 154 res[i] = new InlinedJavaObject(flatArrayElementClass, offset); 155 offset += flatArrayElementClass.getInlinedInstanceSize(); 156 } 157 return res; 158 } 159 default: { 160 throw new RuntimeException("unknown primitive type?"); 161 } 162 } 163 } 164 } 165 166 // JavaClass set only after resolve. 167 private JavaClass clazz; 168 169 private long objID; 170 171 // This field contains elementSignature byte and 172 // divider to be used to calculate length. Note that 173 // length of content byte[] is not same as array length. 174 // Actual array length is (byte[].length / divider) 175 private int data; 176 177 // First 8 bits of data is used for element signature 178 private static final int SIGNATURE_MASK = 0x0FF; 179 180 // Next 8 bits of data is used for length divider 181 private static final int LENGTH_DIVIDER_MASK = 0x0FF00; 182 183 // Number of bits to shift to get length divider 184 private static final int LENGTH_DIVIDER_SHIFT = 8; 185 186 // Flat array support. 187 private JavaClass flatArrayElementClass; 188 189 public JavaValueArray(long id, byte elementSignature, long offset) { 190 super(offset); 191 this.objID = id; 192 this.data = (elementSignature & SIGNATURE_MASK); 193 } 194 195 public JavaClass getClazz() { 196 return clazz; 197 } 198 199 public boolean isFlatArray() { 200 return flatArrayElementClass != null; 201 } 202 203 public JavaClass getFlatElementClazz() { 204 return flatArrayElementClass; 205 } 206 207 public void visitReferencedObjects(JavaHeapObjectVisitor v) { 208 super.visitReferencedObjects(v); 209 } 210 211 public void resolve(Snapshot snapshot) { 212 if (clazz instanceof JavaClass) { 213 return; 214 } 215 216 byte elementType = getElementType(); 217 String elementSig = "" + (char)elementType; 218 // Check if this is a flat array of primitive objects. 219 Number elementClassID = snapshot.findFlatArrayElementType(objID); 220 if (elementClassID != null) { 221 // This is flat array. 222 JavaHeapObject elementClazz = snapshot.findThing(getIdValue(elementClassID)); 223 if (elementClazz instanceof JavaClass elementJavaClazz) { 224 flatArrayElementClass = elementJavaClazz; 225 // need to resolve the element class 226 flatArrayElementClass.resolve(snapshot); 227 elementSig = "Q" + flatArrayElementClass.getName() + ";"; 228 } else { 229 // The class not found. 230 System.out.println("WARNING: flat array element class not found"); 231 } 232 } 233 clazz = snapshot.getArrayClass(elementSig); 234 getClazz().addInstance(this); 235 super.resolve(snapshot); 236 } 237 238 public int getLength() { 239 int divider = (data & LENGTH_DIVIDER_MASK) >>> LENGTH_DIVIDER_SHIFT; 240 if (divider == 0) { 241 byte elementSignature = getElementType(); 242 switch (elementSignature) { 243 case 'B': 244 case 'Z': 245 divider = 1; 246 break; 247 case 'C': 248 case 'S': 249 divider = 2; 250 break; 251 case 'I': 252 case 'F': 253 divider = 4; 254 break; 255 case 'J': 256 case 'D': 257 divider = 8; 258 break; 259 case 'Q': 260 divider = flatArrayElementClass.getInlinedInstanceSize(); 261 break; 262 default: 263 throw new RuntimeException("unknown primitive type: " + 264 elementSignature); 265 } 266 data |= (divider << LENGTH_DIVIDER_SHIFT); 267 } 268 return (int)(getValueLength() / divider); 269 } 270 271 public JavaThing[] getElements() { 272 return getValue(); 273 } 274 275 public byte getElementType() { 276 return isFlatArray() ? (byte)'Q' : getRealElementType(); 277 } 278 279 private byte getRealElementType() { 280 return (byte) (data & SIGNATURE_MASK); 281 } 282 283 private void checkIndex(int index) { 284 Objects.checkIndex(index, getLength()); 285 } 286 287 private void requireType(char type) { 288 if (getElementType() != type) { 289 throw new RuntimeException("not of type : " + type); 290 } 291 } 292 293 public String valueString() { 294 return valueString(true); 295 } 296 297 public String valueString(boolean bigLimit) { 298 // Char arrays deserve special treatment 299 StringBuilder result; 300 JavaThing[] things = getValue(); 301 byte elementSignature = getElementType(); 302 if (elementSignature == 'C' && !isFlatArray()) { 303 result = new StringBuilder(); 304 for (int i = 0; i < things.length; i++) { 305 result.append(things[i]); 306 } 307 } else { 308 int limit = 8; 309 if (bigLimit) { 310 limit = 1000; 311 } 312 result = new StringBuilder("{"); 313 for (int i = 0; i < things.length; i++) { 314 if (i > 0) { 315 result.append(", "); 316 } 317 if (i >= limit) { 318 result.append("... "); 319 break; 320 } 321 switch (elementSignature) { 322 case 'Z': { 323 boolean val = ((JavaBoolean)things[i]).value; 324 if (val) { 325 result.append("true"); 326 } else { 327 result.append("false"); 328 } 329 break; 330 } 331 case 'B': { 332 byte val = ((JavaByte)things[i]).value; 333 result.append("0x").append(Integer.toString(val, 16)); 334 break; 335 } 336 case 'S': { 337 short val = ((JavaShort)things[i]).value; 338 result.append(val); 339 break; 340 } 341 case 'I': { 342 int val = ((JavaInt)things[i]).value; 343 result.append(val); 344 break; 345 } 346 case 'J': { // long 347 long val = ((JavaLong)things[i]).value; 348 result.append(val); 349 break; 350 } 351 case 'F': { 352 float val = ((JavaFloat)things[i]).value; 353 result.append(val); 354 break; 355 } 356 case 'D': { // double 357 double val = ((JavaDouble)things[i]).value; 358 result.append(val); 359 break; 360 } 361 case 'Q': { 362 InlinedJavaObject obj = (InlinedJavaObject)things[i]; 363 result.append(obj); 364 } 365 default: { 366 throw new RuntimeException("unknown primitive type?"); 367 } 368 } 369 } 370 result.append('}'); 371 } 372 return result.toString(); 373 } 374 375 // Tries to represent the value as string (used by JavaObject.toString). 376 public String valueAsString() { 377 if (getElementType() == 'B') { 378 JavaThing[] things = getValue(); 379 byte[] bytes = new byte[things.length]; 380 for (int i = 0; i < things.length; i++) { 381 bytes[i] = ((JavaByte)things[i]).value; 382 } 383 return new String(bytes); 384 } 385 // fallback 386 return valueString(); 387 } 388 }