jdk/test/java/lang/StackWalker/LocalsAndOperands.java
changeset 38347 a3fdbd11148f
parent 34934 7fbff68dbc12
child 40684 2e37c119dc2a
equal deleted inserted replaced
38346:5ed176fa7d97 38347:a3fdbd11148f
     1 /*
     1 /*
     2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
     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.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     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
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.
     7  * published by the Free Software Foundation.
    21  * questions.
    21  * questions.
    22  */
    22  */
    23 
    23 
    24 /*
    24 /*
    25  * @test
    25  * @test
    26  * @bug 8020968
    26  * @bug 8020968 8147039
    27  * @summary Sanity test for locals and operands
    27  * @summary Tests for locals and operands
    28  * @run main LocalsAndOperands
    28  * @run testng LocalsAndOperands
    29  */
    29  */
    30 
    30 
       
    31 import org.testng.annotations.*;
    31 import java.lang.StackWalker.StackFrame;
    32 import java.lang.StackWalker.StackFrame;
    32 import java.lang.reflect.*;
    33 import java.lang.reflect.*;
    33 import java.util.List;
    34 import java.util.*;
    34 import java.util.stream.Collectors;
    35 import java.util.stream.*;
    35 
    36 
    36 public class LocalsAndOperands {
    37 public class LocalsAndOperands {
       
    38     static final boolean debug = true;
       
    39 
    37     static Class<?> liveStackFrameClass;
    40     static Class<?> liveStackFrameClass;
    38     static Class<?> primitiveValueClass;
    41     static Class<?> primitiveValueClass;
    39     static StackWalker extendedWalker;
    42     static StackWalker extendedWalker;
    40     static Method getLocals;
    43     static Method getLocals;
    41     static Method getOperands;
    44     static Method getOperands;
    42     static Method getMonitors;
    45     static Method getMonitors;
    43     static Method primitiveType;
    46     static Method primitiveType;
    44     public static void main(String... args) throws Exception {
    47 
    45         liveStackFrameClass = Class.forName("java.lang.LiveStackFrame");
    48     static {
    46         primitiveValueClass = Class.forName("java.lang.LiveStackFrame$PrimitiveValue");
    49         try {
    47 
    50             liveStackFrameClass = Class.forName("java.lang.LiveStackFrame");
    48         getLocals = liveStackFrameClass.getDeclaredMethod("getLocals");
    51             primitiveValueClass = Class.forName("java.lang.LiveStackFrame$PrimitiveValue");
    49         getLocals.setAccessible(true);
    52 
    50 
    53             getLocals = liveStackFrameClass.getDeclaredMethod("getLocals");
    51         getOperands = liveStackFrameClass.getDeclaredMethod("getStack");
    54             getLocals.setAccessible(true);
    52         getOperands.setAccessible(true);
    55 
    53 
    56             getOperands = liveStackFrameClass.getDeclaredMethod("getStack");
    54         getMonitors = liveStackFrameClass.getDeclaredMethod("getMonitors");
    57             getOperands.setAccessible(true);
    55         getMonitors.setAccessible(true);
    58 
    56 
    59             getMonitors = liveStackFrameClass.getDeclaredMethod("getMonitors");
    57         primitiveType = primitiveValueClass.getDeclaredMethod("type");
    60             getMonitors.setAccessible(true);
    58         primitiveType.setAccessible(true);
    61 
    59 
    62             primitiveType = primitiveValueClass.getDeclaredMethod("type");
    60         Method method = liveStackFrameClass.getMethod("getStackWalker");
    63             primitiveType.setAccessible(true);
    61         method.setAccessible(true);
    64 
    62         extendedWalker = (StackWalker) method.invoke(null);
    65             Method method = liveStackFrameClass.getMethod("getStackWalker");
    63         new LocalsAndOperands(extendedWalker, true).test();
    66             method.setAccessible(true);
    64 
    67             extendedWalker = (StackWalker) method.invoke(null);
    65         // no access to local and operands.
    68         } catch (Throwable t) { throw new RuntimeException(t); }
    66         new LocalsAndOperands(StackWalker.getInstance(), false).test();
    69     }
    67     }
    70 
    68 
    71     /** Helper method to return a StackFrame's locals */
    69     private final StackWalker walker;
    72     static Object[] invokeGetLocals(StackFrame arg) {
    70     private final boolean extended;
    73         try {
    71     LocalsAndOperands(StackWalker walker, boolean extended) {
    74             return (Object[]) getLocals.invoke(arg);
    72         this.walker = walker;
    75         } catch (Exception e) { throw new RuntimeException(e); }
    73         this.extended = extended;
    76     }
    74     }
    77 
    75 
    78     /*****************
    76     synchronized void test() throws Exception {
    79      * DataProviders *
    77         int x = 10;
    80      *****************/
    78         char c = 'z';
    81 
    79         String hi = "himom";
    82     /** Calls testLocals() and provides LiveStackFrames for testLocals* methods */
    80         long l = 1000000L;
    83     @DataProvider
    81         double d =  3.1415926;
    84     public static StackFrame[][] provider() {
    82 
    85         return new StackFrame[][] {
    83         List<StackWalker.StackFrame> frames = walker.walk(s -> s.collect(Collectors.toList()));
    86             new Tester().testLocals()
    84         if (extended) {
    87         };
    85             for (StackWalker.StackFrame f : frames) {
    88     }
    86                 System.out.println("frame: " + f);
    89 
    87                 Object[] locals = (Object[]) getLocals.invoke(f);
    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);
    88                 for (int i = 0; i < locals.length; i++) {
   143                 for (int i = 0; i < locals.length; i++) {
    89                     System.out.format("  local %d: %s type %s\n", i, locals[i], type(locals[i]));
   144                     // Value
    90 
   145                     String expected = Tester.LOCAL_VALUES[i];
    91                     // check for non-null locals in LocalsAndOperands.test()
   146                     Object observed = locals[i];
    92                     if (f.getClassName().equals("LocalsAndOperands") &&
   147                     if (expected != null /* skip nulls in golden values */ &&
    93                             f.getMethodName().equals("test")) {
   148                             !expected.equals(observed.toString())) {
    94                         if (locals[i] == null) {
   149                         System.err.println("Local value mismatch:");
    95                             throw new RuntimeException("kept-alive locals should not be null");
   150                         if (!debug) { dumpStackWithLocals(frames); }
    96                         }
   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);
    97                     }
   164                     }
    98                 }
   165                 }
    99 
   166             }
   100                 Object[] operands = (Object[]) getOperands.invoke(f);
   167         );
   101                 for (int i = 0; i < operands.length; i++) {
   168     }
   102                     System.out.format("  operand %d: %s type %s%n", i, operands[i],
   169 
   103                                       type(operands[i]));
   170     /**
   104                 }
   171      * Basic sanity check for locals and operands
   105 
   172      */
   106                 Object[] monitors = (Object[]) getMonitors.invoke(f);
   173     @Test(dataProvider = "provider")
   107                 for (int i = 0; i < monitors.length; i++) {
   174     public static void sanityCheck(StackFrame... frames) {
   108                     System.out.format("  monitor %d: %s%n", i, monitors[i]);
   175         if (debug) {
   109                 }
   176             System.out.println("Running sanityCheck");
   110             }
   177         }
   111         } else {
   178         try {
   112             for (StackFrame f : frames) {
   179             Stream<StackFrame> stream = Arrays.stream(frames);
   113                 if (liveStackFrameClass.isInstance(f)) {
   180             if (debug) {
   114                     throw new RuntimeException("should not be LiveStackFrame");
   181                 stream.forEach(LocalsAndOperands::printLocals);
   115                 }
   182             } else {
   116             }
   183                 System.out.println(stream.count() + " frames");
   117         }
   184             }
   118         // Use local variables so they stay alive
   185         } catch (Throwable t) {
   119         System.out.println("Stayin' alive: "+x+" "+c+" "+hi+" "+l+" "+d);
   186             dumpStackWithLocals(frames);
   120     }
   187             throw t;
   121 
   188         }
   122     String type(Object o) throws Exception {
   189     }
   123         if (o == null) {
   190 
   124             return "null";
   191     /**
   125         } else if (primitiveValueClass.isInstance(o)) {
   192      * Sanity check for locals and operands, including testng/jtreg frames
   126             char c = (char)primitiveType.invoke(o);
   193      */
   127             return String.valueOf(c);
   194     @Test(dataProvider = "unfilteredProvider")
   128         } else {
   195     public static void unfilteredSanityCheck(StackFrame... frames) {
   129             return o.getClass().getName();
   196         if (debug) {
   130         }
   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); }
   131     }
   361     }
   132 }
   362 }