1 /* 2 * Copyright (c) 2015, 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. 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 * @test 26 * @bug 8020968 8147039 27 * @summary Tests for locals and operands 28 * @run testng LocalsAndOperands 29 */ 30 31 import org.testng.annotations.*; 32 import java.lang.StackWalker.StackFrame; 33 import java.lang.reflect.*; 34 import java.util.*; 35 import java.util.stream.*; 36 37 public class LocalsAndOperands { 38 static final boolean debug = true; 39 40 static Class<?> liveStackFrameClass; 41 static Class<?> primitiveValueClass; 42 static StackWalker extendedWalker; 43 static Method getLocals; 44 static Method getOperands; 45 static Method getMonitors; 46 static Method primitiveType; 47 48 static { 49 try { 50 liveStackFrameClass = Class.forName("java.lang.LiveStackFrame"); 51 primitiveValueClass = Class.forName("java.lang.LiveStackFrame$PrimitiveValue"); 52 53 getLocals = liveStackFrameClass.getDeclaredMethod("getLocals"); 54 getLocals.setAccessible(true); 55 56 getOperands = liveStackFrameClass.getDeclaredMethod("getStack"); 57 getOperands.setAccessible(true); 58 59 getMonitors = liveStackFrameClass.getDeclaredMethod("getMonitors"); 60 getMonitors.setAccessible(true); 61 62 primitiveType = primitiveValueClass.getDeclaredMethod("type"); 63 primitiveType.setAccessible(true); 64 65 Method method = liveStackFrameClass.getMethod("getStackWalker"); 66 method.setAccessible(true); 67 extendedWalker = (StackWalker) method.invoke(null); 68 } catch (Throwable t) { throw new RuntimeException(t); } 69 } 70 71 /** Helper method to return a StackFrame's locals */ 72 static Object[] invokeGetLocals(StackFrame arg) { 73 try { 74 return (Object[]) getLocals.invoke(arg); 75 } catch (Exception e) { throw new RuntimeException(e); } 76 } 77 78 /***************** 79 * DataProviders * 80 *****************/ 81 82 /** Calls testLocals() and provides LiveStackFrames for testLocals* methods */ 83 @DataProvider 84 public static StackFrame[][] provider() { 85 return new StackFrame[][] { 86 new Tester().testLocals() 87 }; 88 } 89 90 /** 91 * Calls testLocalsKeepAlive() and provides LiveStackFrames for testLocals* methods. 92 * Local variables in testLocalsKeepAlive() are ensured to not become dead. 93 */ 94 @DataProvider 95 public static StackFrame[][] keepAliveProvider() { 96 return new StackFrame[][] { 97 new Tester().testLocalsKeepAlive() 98 }; 99 } 100 101 /** 102 * Provides StackFrames from a StackWalker without the LOCALS_AND_OPERANDS 103 * option. 104 */ 105 @DataProvider 106 public static StackFrame[][] noLocalsProvider() { 107 // Use default StackWalker 108 return new StackFrame[][] { 109 new Tester(StackWalker.getInstance(), true).testLocals() 110 }; 111 } 112 113 /** 114 * Calls testLocals() and provides LiveStackFrames for *all* called methods, 115 * including test infrastructure (jtreg, testng, etc) 116 * 117 */ 118 @DataProvider 119 public static StackFrame[][] unfilteredProvider() { 120 return new StackFrame[][] { 121 new Tester(extendedWalker, false).testLocals() 122 }; 123 } 124 125 /**************** 126 * Test methods * 127 ****************/ 128 129 /** 130 * Check for expected local values and types in the LiveStackFrame 131 */ 132 @Test(dataProvider = "keepAliveProvider") 133 public static void checkLocalValues(StackFrame... frames) { 134 if (debug) { 135 System.out.println("Running checkLocalValues"); 136 dumpStackWithLocals(frames); 137 } 138 Arrays.stream(frames).filter(f -> f.getMethodName() 139 .equals("testLocalsKeepAlive")) 140 .forEach( 141 f -> { 142 Object[] locals = invokeGetLocals(f); 143 for (int i = 0; i < locals.length; i++) { 144 // Value 145 String expected = Tester.LOCAL_VALUES[i]; 146 Object observed = locals[i]; 147 if (expected != null /* skip nulls in golden values */ && 148 !expected.equals(observed.toString())) { 149 System.err.println("Local value mismatch:"); 150 if (!debug) { dumpStackWithLocals(frames); } 151 throw new RuntimeException("local " + i + " value is " + 152 observed + ", expected " + expected); 153 } 154 155 // Type 156 expected = Tester.LOCAL_TYPES[i]; 157 observed = type(locals[i]); 158 if (expected != null /* skip nulls in golden values */ && 159 !expected.equals(observed)) { 160 System.err.println("Local type mismatch:"); 161 if (!debug) { dumpStackWithLocals(frames); } 162 throw new RuntimeException("local " + i + " type is " + 163 observed + ", expected " + expected); 164 } 165 } 166 } 167 ); 168 } 169 170 /** 171 * Basic sanity check for locals and operands 172 */ 173 @Test(dataProvider = "provider") 174 public static void sanityCheck(StackFrame... frames) { 175 if (debug) { 176 System.out.println("Running sanityCheck"); 177 } 178 try { 179 Stream<StackFrame> stream = Arrays.stream(frames); 180 if (debug) { 181 stream.forEach(LocalsAndOperands::printLocals); 182 } else { 183 System.out.println(stream.count() + " frames"); 184 } 185 } catch (Throwable t) { 186 dumpStackWithLocals(frames); 187 throw t; 188 } 189 } 190 191 /** 192 * Sanity check for locals and operands, including testng/jtreg frames 193 */ 194 @Test(dataProvider = "unfilteredProvider") 195 public static void unfilteredSanityCheck(StackFrame... frames) { 196 if (debug) { 197 System.out.println("Running unfilteredSanityCheck"); 198 } 199 try { 200 Stream<StackFrame> stream = Arrays.stream(frames); 201 if (debug) { 202 stream.forEach(f -> { System.out.println(f + ": " + 203 invokeGetLocals(f).length + " locals"); } ); 204 } else { 205 System.out.println(stream.count() + " frames"); 206 } 207 } catch (Throwable t) { 208 dumpStackWithLocals(frames); 209 throw t; 210 } 211 } 212 213 /** 214 * Test that LiveStackFrames are not provided with the default StackWalker 215 * options. 216 */ 217 @Test(dataProvider = "noLocalsProvider") 218 public static void withoutLocalsAndOperands(StackFrame... frames) { 219 for (StackFrame frame : frames) { 220 if (liveStackFrameClass.isInstance(frame)) { 221 throw new RuntimeException("should not be LiveStackFrame"); 222 } 223 } 224 } 225 226 static class Tester { 227 private StackWalker walker; 228 private boolean filter = true; // Filter out testng/jtreg/etc frames? 229 230 Tester() { 231 this.walker = extendedWalker; 232 } 233 234 Tester(StackWalker walker, boolean filter) { 235 this.walker = walker; 236 this.filter = filter; 237 } 238 239 /** 240 * Perform stackwalk without keeping local variables alive and return an 241 * array of the collected StackFrames 242 */ 243 private synchronized StackFrame[] testLocals() { 244 // Unused local variables will become dead 245 int x = 10; 246 char c = 'z'; 247 String hi = "himom"; 248 long l = 1000000L; 249 double d = 3.1415926; 250 251 if (filter) { 252 return walker.walk(s -> s.filter(f -> TEST_METHODS.contains(f 253 .getMethodName())).collect(Collectors.toList())) 254 .toArray(new StackFrame[0]); 255 } else { 256 return walker.walk(s -> s.collect(Collectors.toList())) 257 .toArray(new StackFrame[0]); 258 } 259 } 260 261 /** 262 * Perform stackwalk, keeping local variables alive, and return a list of 263 * the collected StackFrames 264 */ 265 private synchronized StackFrame[] testLocalsKeepAlive() { 266 int x = 10; 267 char c = 'z'; 268 String hi = "himom"; 269 long l = 1000000L; 270 double d = 3.1415926; 271 272 List<StackWalker.StackFrame> frames; 273 if (filter) { 274 frames = walker.walk(s -> s.filter(f -> TEST_METHODS.contains(f 275 .getMethodName())).collect(Collectors.toList())); 276 } else { 277 frames = walker.walk(s -> s.collect(Collectors.toList())); 278 } 279 280 // Use local variables so they stay alive 281 System.out.println("Stayin' alive: "+x+" "+c+" "+hi+" "+l+" "+d); 282 return frames.toArray(new StackFrame[0]); // FIXME: convert to Array here 283 } 284 285 // Expected values for locals in testLocals() & testLocalsKeepAlive() 286 // TODO: use real values instead of Strings, rebuild doubles & floats, etc 287 private final static String[] LOCAL_VALUES = new String[] { 288 null, // skip, LocalsAndOperands$Tester@XXX identity is different each run 289 "10", 290 "122", 291 "himom", 292 "0", 293 null, // skip, fix in 8156073 294 null, // skip, fix in 8156073 295 null, // skip, fix in 8156073 296 "0" 297 }; 298 299 // Expected types for locals in testLocals() & testLocalsKeepAlive() 300 // TODO: use real types 301 private final static String[] LOCAL_TYPES = new String[] { 302 null, // skip 303 "I", 304 "I", 305 "java.lang.String", 306 "I", 307 "I", 308 "I", 309 "I", 310 "I" 311 }; 312 313 final static Map NUM_LOCALS = Map.of("testLocals", 8, 314 "testLocalsKeepAlive", 315 LOCAL_VALUES.length); 316 private final static Collection<String> TEST_METHODS = NUM_LOCALS.keySet(); 317 } 318 319 /** 320 * Print stack trace with locals 321 */ 322 public static void dumpStackWithLocals(StackFrame...frames) { 323 Arrays.stream(frames).forEach(LocalsAndOperands::printLocals); 324 } 325 326 /** 327 * Print the StackFrame and an indexed list of its locals 328 */ 329 public static void printLocals(StackWalker.StackFrame frame) { 330 try { 331 System.out.println(frame); 332 Object[] locals = (Object[]) getLocals.invoke(frame); 333 for (int i = 0; i < locals.length; i++) { 334 System.out.format(" local %d: %s type %s\n", i, locals[i], type(locals[i])); 335 } 336 337 Object[] operands = (Object[]) getOperands.invoke(frame); 338 for (int i = 0; i < operands.length; i++) { 339 System.out.format(" operand %d: %s type %s%n", i, operands[i], 340 type(operands[i])); 341 } 342 343 Object[] monitors = (Object[]) getMonitors.invoke(frame); 344 for (int i = 0; i < monitors.length; i++) { 345 System.out.format(" monitor %d: %s%n", i, monitors[i]); 346 } 347 } catch (Exception e) { throw new RuntimeException(e); } 348 } 349 350 private static String type(Object o) { 351 try { 352 if (o == null) { 353 return "null"; 354 } else if (primitiveValueClass.isInstance(o)) { 355 char c = (char)primitiveType.invoke(o); 356 return String.valueOf(c); 357 } else { 358 return o.getClass().getName(); 359 } 360 } catch(Exception e) { throw new RuntimeException(e); } 361 } 362 }