< prev index next >

jdk/test/java/lang/StackWalker/LocalsAndOperands.java

Print this page

        

*** 1,7 **** /* ! * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. --- 1,7 ---- /* ! * Copyright (c) 2015, 2016 Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.
*** 21,49 **** * questions. */ /* * @test ! * @bug 8020968 ! * @summary Sanity test for locals and operands ! * @run main LocalsAndOperands */ import java.lang.StackWalker.StackFrame; import java.lang.reflect.*; ! import java.util.List; ! import java.util.stream.Collectors; public class LocalsAndOperands { static Class<?> liveStackFrameClass; static Class<?> primitiveValueClass; static StackWalker extendedWalker; static Method getLocals; static Method getOperands; static Method getMonitors; static Method primitiveType; ! public static void main(String... args) throws Exception { liveStackFrameClass = Class.forName("java.lang.LiveStackFrame"); primitiveValueClass = Class.forName("java.lang.LiveStackFrame$PrimitiveValue"); getLocals = liveStackFrameClass.getDeclaredMethod("getLocals"); getLocals.setAccessible(true); --- 21,54 ---- * questions. */ /* * @test ! * @bug 8020968 8147039 ! * @summary Tests for locals and operands ! * @run testng LocalsAndOperands */ + import org.testng.annotations.*; import java.lang.StackWalker.StackFrame; import java.lang.reflect.*; ! import java.util.*; ! import java.util.stream.*; public class LocalsAndOperands { + static final boolean debug = true; + static Class<?> liveStackFrameClass; static Class<?> primitiveValueClass; static StackWalker extendedWalker; static Method getLocals; static Method getOperands; static Method getMonitors; static Method primitiveType; ! ! static { ! try { liveStackFrameClass = Class.forName("java.lang.LiveStackFrame"); primitiveValueClass = Class.forName("java.lang.LiveStackFrame$PrimitiveValue"); getLocals = liveStackFrameClass.getDeclaredMethod("getLocals"); getLocals.setAccessible(true);
*** 58,132 **** primitiveType.setAccessible(true); Method method = liveStackFrameClass.getMethod("getStackWalker"); method.setAccessible(true); extendedWalker = (StackWalker) method.invoke(null); ! new LocalsAndOperands(extendedWalker, true).test(); ! // no access to local and operands. ! new LocalsAndOperands(StackWalker.getInstance(), false).test(); } ! private final StackWalker walker; ! private final boolean extended; ! LocalsAndOperands(StackWalker walker, boolean extended) { this.walker = walker; ! this.extended = extended; } ! synchronized void test() throws Exception { int x = 10; char c = 'z'; String hi = "himom"; long l = 1000000L; double d = 3.1415926; ! List<StackWalker.StackFrame> frames = walker.walk(s -> s.collect(Collectors.toList())); ! if (extended) { ! for (StackWalker.StackFrame f : frames) { ! System.out.println("frame: " + f); ! Object[] locals = (Object[]) getLocals.invoke(f); ! for (int i = 0; i < locals.length; i++) { ! System.out.format(" local %d: %s type %s\n", i, locals[i], type(locals[i])); ! // check for non-null locals in LocalsAndOperands.test() ! if (f.getClassName().equals("LocalsAndOperands") && ! f.getMethodName().equals("test")) { ! if (locals[i] == null) { ! throw new RuntimeException("kept-alive locals should not be null"); } } } ! Object[] operands = (Object[]) getOperands.invoke(f); for (int i = 0; i < operands.length; i++) { System.out.format(" operand %d: %s type %s%n", i, operands[i], type(operands[i])); } ! Object[] monitors = (Object[]) getMonitors.invoke(f); for (int i = 0; i < monitors.length; i++) { System.out.format(" monitor %d: %s%n", i, monitors[i]); } ! } ! } else { ! for (StackFrame f : frames) { ! if (liveStackFrameClass.isInstance(f)) { ! throw new RuntimeException("should not be LiveStackFrame"); ! } ! } ! } ! // Use local variables so they stay alive ! System.out.println("Stayin' alive: "+x+" "+c+" "+hi+" "+l+" "+d); } ! String type(Object o) throws Exception { if (o == null) { return "null"; } else if (primitiveValueClass.isInstance(o)) { char c = (char)primitiveType.invoke(o); return String.valueOf(c); } else { return o.getClass().getName(); } } } --- 63,362 ---- primitiveType.setAccessible(true); Method method = liveStackFrameClass.getMethod("getStackWalker"); method.setAccessible(true); extendedWalker = (StackWalker) method.invoke(null); ! } catch (Throwable t) { throw new RuntimeException(t); } ! } ! ! /** Helper method to return a StackFrame's locals */ ! static Object[] invokeGetLocals(StackFrame arg) { ! try { ! return (Object[]) getLocals.invoke(arg); ! } catch (Exception e) { throw new RuntimeException(e); } ! } ! ! /***************** ! * DataProviders * ! *****************/ ! ! /** Calls testLocals() and provides LiveStackFrames for testLocals* methods */ ! @DataProvider ! public static StackFrame[][] provider() { ! return new StackFrame[][] { ! new Tester().testLocals() ! }; ! } ! ! /** ! * Calls testLocalsKeepAlive() and provides LiveStackFrames for testLocals* methods. ! * Local variables in testLocalsKeepAlive() are ensured to not become dead. ! */ ! @DataProvider ! public static StackFrame[][] keepAliveProvider() { ! return new StackFrame[][] { ! new Tester().testLocalsKeepAlive() ! }; ! } ! ! /** ! * Provides StackFrames from a StackWalker without the LOCALS_AND_OPERANDS ! * option. ! */ ! @DataProvider ! public static StackFrame[][] noLocalsProvider() { ! // Use default StackWalker ! return new StackFrame[][] { ! new Tester(StackWalker.getInstance(), true).testLocals() ! }; ! } ! ! /** ! * Calls testLocals() and provides LiveStackFrames for *all* called methods, ! * including test infrastructure (jtreg, testng, etc) ! * ! */ ! @DataProvider ! public static StackFrame[][] unfilteredProvider() { ! return new StackFrame[][] { ! new Tester(extendedWalker, false).testLocals() ! }; ! } ! ! /**************** ! * Test methods * ! ****************/ ! ! /** ! * Check for expected local values and types in the LiveStackFrame ! */ ! @Test(dataProvider = "keepAliveProvider") ! public static void checkLocalValues(StackFrame... frames) { ! if (debug) { ! System.out.println("Running checkLocalValues"); ! dumpStackWithLocals(frames); ! } ! Arrays.stream(frames).filter(f -> f.getMethodName() ! .equals("testLocalsKeepAlive")) ! .forEach( ! f -> { ! Object[] locals = invokeGetLocals(f); ! for (int i = 0; i < locals.length; i++) { ! // Value ! String expected = Tester.LOCAL_VALUES[i]; ! Object observed = locals[i]; ! if (expected != null /* skip nulls in golden values */ && ! !expected.equals(observed.toString())) { ! System.err.println("Local value mismatch:"); ! if (!debug) { dumpStackWithLocals(frames); } ! throw new RuntimeException("local " + i + " value is " + ! observed + ", expected " + expected); ! } ! ! // Type ! expected = Tester.LOCAL_TYPES[i]; ! observed = type(locals[i]); ! if (expected != null /* skip nulls in golden values */ && ! !expected.equals(observed)) { ! System.err.println("Local type mismatch:"); ! if (!debug) { dumpStackWithLocals(frames); } ! throw new RuntimeException("local " + i + " type is " + ! observed + ", expected " + expected); ! } ! } ! } ! ); ! } ! ! /** ! * Basic sanity check for locals and operands ! */ ! @Test(dataProvider = "provider") ! public static void sanityCheck(StackFrame... frames) { ! if (debug) { ! System.out.println("Running sanityCheck"); ! } ! try { ! Stream<StackFrame> stream = Arrays.stream(frames); ! if (debug) { ! stream.forEach(LocalsAndOperands::printLocals); ! } else { ! System.out.println(stream.count() + " frames"); ! } ! } catch (Throwable t) { ! dumpStackWithLocals(frames); ! throw t; ! } ! } ! /** ! * Sanity check for locals and operands, including testng/jtreg frames ! */ ! @Test(dataProvider = "unfilteredProvider") ! public static void unfilteredSanityCheck(StackFrame... frames) { ! if (debug) { ! System.out.println("Running unfilteredSanityCheck"); ! } ! try { ! Stream<StackFrame> stream = Arrays.stream(frames); ! if (debug) { ! stream.forEach(f -> { System.out.println(f + ": " + ! invokeGetLocals(f).length + " locals"); } ); ! } else { ! System.out.println(stream.count() + " frames"); ! } ! } catch (Throwable t) { ! dumpStackWithLocals(frames); ! throw t; ! } } ! /** ! * Test that LiveStackFrames are not provided with the default StackWalker ! * options. ! */ ! @Test(dataProvider = "noLocalsProvider") ! public static void withoutLocalsAndOperands(StackFrame... frames) { ! for (StackFrame frame : frames) { ! if (liveStackFrameClass.isInstance(frame)) { ! throw new RuntimeException("should not be LiveStackFrame"); ! } ! } ! } ! ! static class Tester { ! private StackWalker walker; ! private boolean filter = true; // Filter out testng/jtreg/etc frames? ! ! Tester() { ! this.walker = extendedWalker; ! } ! ! Tester(StackWalker walker, boolean filter) { this.walker = walker; ! this.filter = filter; } ! /** ! * Perform stackwalk without keeping local variables alive and return an ! * array of the collected StackFrames ! */ ! private synchronized StackFrame[] testLocals() { ! // Unused local variables will become dead int x = 10; char c = 'z'; String hi = "himom"; long l = 1000000L; double d = 3.1415926; ! if (filter) { ! return walker.walk(s -> s.filter(f -> TEST_METHODS.contains(f ! .getMethodName())).collect(Collectors.toList())) ! .toArray(new StackFrame[0]); ! } else { ! return walker.walk(s -> s.collect(Collectors.toList())) ! .toArray(new StackFrame[0]); ! } ! } ! ! /** ! * Perform stackwalk, keeping local variables alive, and return a list of ! * the collected StackFrames ! */ ! private synchronized StackFrame[] testLocalsKeepAlive() { ! int x = 10; ! char c = 'z'; ! String hi = "himom"; ! long l = 1000000L; ! double d = 3.1415926; ! List<StackWalker.StackFrame> frames; ! if (filter) { ! frames = walker.walk(s -> s.filter(f -> TEST_METHODS.contains(f ! .getMethodName())).collect(Collectors.toList())); ! } else { ! frames = walker.walk(s -> s.collect(Collectors.toList())); } + + // Use local variables so they stay alive + System.out.println("Stayin' alive: "+x+" "+c+" "+hi+" "+l+" "+d); + return frames.toArray(new StackFrame[0]); // FIXME: convert to Array here } + + // Expected values for locals in testLocals() & testLocalsKeepAlive() + // TODO: use real values instead of Strings, rebuild doubles & floats, etc + private final static String[] LOCAL_VALUES = new String[] { + null, // skip, LocalsAndOperands$Tester@XXX identity is different each run + "10", + "122", + "himom", + "0", + "1000000", + "0", + "1293080650", + "0" + }; + + // Expected types for locals in testLocals() & testLocalsKeepAlive() + // TODO: use real types + private final static String[] LOCAL_TYPES = new String[] { + null, // skip + "I", + "I", + "java.lang.String", + "I", + "I", + "I", + "I", + "I" + }; + + final static Map NUM_LOCALS = Map.of("testLocals", 8, + "testLocalsKeepAlive", + LOCAL_VALUES.length); + private final static Collection<String> TEST_METHODS = NUM_LOCALS.keySet(); + } + + /** + * Print stack trace with locals + */ + public static void dumpStackWithLocals(StackFrame...frames) { + Arrays.stream(frames).forEach(LocalsAndOperands::printLocals); } ! /** ! * Print the StackFrame and an indexed list of its locals ! */ ! public static void printLocals(StackWalker.StackFrame frame) { ! try { ! System.out.println(frame); ! Object[] locals = (Object[]) getLocals.invoke(frame); ! for (int i = 0; i < locals.length; i++) { ! System.out.format(" local %d: %s type %s\n", i, locals[i], type(locals[i])); ! } ! ! Object[] operands = (Object[]) getOperands.invoke(frame); for (int i = 0; i < operands.length; i++) { System.out.format(" operand %d: %s type %s%n", i, operands[i], type(operands[i])); } ! Object[] monitors = (Object[]) getMonitors.invoke(frame); for (int i = 0; i < monitors.length; i++) { System.out.format(" monitor %d: %s%n", i, monitors[i]); } ! } catch (Exception e) { throw new RuntimeException(e); } } ! private static String type(Object o) { ! try { if (o == null) { return "null"; } else if (primitiveValueClass.isInstance(o)) { char c = (char)primitiveType.invoke(o); return String.valueOf(c); } else { return o.getClass().getName(); } + } catch(Exception e) { throw new RuntimeException(e); } } }
< prev index next >