1 /*
   2  * Copyright (c) 2015, 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 8140450 8173898
  27  * @summary Basic test for the StackWalker::walk method
  28  * @run testng Basic
  29  */
  30 
  31 import java.lang.StackWalker.StackFrame;
  32 import java.util.List;
  33 import java.util.Objects;
  34 import java.util.stream.Collectors;
  35 import java.util.stream.Stream;
  36 import static java.lang.StackWalker.Option.*;
  37 
  38 import org.testng.annotations.DataProvider;
  39 import org.testng.annotations.Test;
  40 
  41 public class Basic {
  42     private static boolean verbose = false;
  43 
  44     @DataProvider(name = "stackDepths")
  45     public static Object[][] stackDepths() {
  46         return new Object[][] {
  47                 { new int[] { 12 },  new int[] { 4, 8, 12}      },
  48                 { new int[] { 18 },  new int[] { 8, 16, 20}     },
  49                 { new int[] { 32 },  new int[] { 16, 32, 64}    },
  50         };
  51     }
  52 
  53     /**
  54      * For a stack of a given depth, it creates a StackWalker with an estimate.
  55      * Test walking different number of frames
  56      */
  57     @Test(dataProvider = "stackDepths")
  58     public static void test(int[] depth, int[] estimates) {
  59         Basic test = new Basic(depth[0]);
  60         for (int estimate : estimates) {
  61             test.walk(estimate);
  62         }
  63     }
  64 
  65     @Test
  66     /**
  67      * @bug 8173898
  68      */
  69     public static void testConstructor() throws Exception {
  70         System.out.println("testConstructor:");
  71         List<String> found = ((Dummy)Dummy.class.getMethod("create")
  72                              .invoke(null)).found;
  73         assertEquals(List.of(Dummy.class.getName()+"::<init>",
  74                              Dummy.class.getName()+"::create",
  75                              Basic.class.getName()+"::testConstructor"),
  76                      found);
  77     }
  78 
  79     private final int depth;
  80     Basic(int depth) {
  81         this.depth = depth;
  82     }
  83 
  84     /*
  85      * Setup a stack builder with the expected stack depth
  86      * Walk the stack and count the frames.
  87      */
  88     void walk(int estimate) {
  89         int limit = Math.min(depth, 16);
  90         List<StackFrame> frames = new StackBuilder(depth, limit).build();
  91         System.out.format("depth=%d estimate=%d expected=%d walked=%d%n",
  92                           depth, estimate, limit, frames.size());
  93         assertEquals(limit, frames.size());
  94     }
  95 
  96     static class Dummy {
  97         static final StackWalker walker =
  98             StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
  99         List<String> found;
 100         public Dummy() {
 101             found = walker.walk(this::parse);
 102         }
 103         public boolean accept(StackFrame f) {
 104             if (!f.getDeclaringClass().getName().contains(".")) {
 105                 System.out.println("    " + f);
 106                 return true;
 107             }
 108             return false;
 109         }
 110         public String frame(StackFrame f) {
 111             return f.getDeclaringClass().getName() + "::" + f.getMethodName();
 112         }
 113         List<String> parse(Stream<StackFrame> s) {
 114             return s.filter(this::accept)
 115                     .map(this::frame)
 116                     .collect(Collectors.toList());
 117         }
 118         public static Dummy create() throws Exception {
 119             return Dummy.class.getConstructor().newInstance();
 120         }
 121     }
 122 
 123     class StackBuilder {
 124         private final int stackDepth;
 125         private final int limit;
 126         private int depth = 0;
 127         private List<StackFrame> result;
 128         StackBuilder(int stackDepth, int limit) {
 129             this.stackDepth = stackDepth; // build method;
 130             this.limit = limit;
 131         }
 132         List<StackFrame> build() {
 133             trace("build");
 134             m1();
 135             return result;
 136         }
 137         void m1() {
 138             trace("m1");
 139             m2();
 140         }
 141         void m2() {
 142             trace("m2");
 143             m3();
 144         }
 145         void m3() {
 146             trace("m3");
 147             m4();
 148         }
 149         void m4() {
 150             trace("m4");
 151             int remaining = stackDepth-depth-1;
 152             if (remaining >= 4) {
 153                 m1();
 154             } else {
 155                 filler(remaining);
 156             }
 157         }
 158         void filler(int i) {
 159             trace("filler");
 160             if (i == 0)
 161                 walk();
 162             else
 163                 filler(--i);
 164         }
 165 
 166         void walk() {
 167             StackWalker walker = StackWalker.getInstance(RETAIN_CLASS_REFERENCE);
 168             result = walker.walk(s -> s.limit(limit).collect(Collectors.toList()));
 169         }
 170         void trace(String methodname) {
 171             ++depth;
 172             if (verbose)
 173                 System.out.format("%2d: %s%n", depth, methodname);
 174         }
 175     }
 176 
 177     static void assertEquals(int x, int y) {
 178         if (x != y) {
 179             throw new RuntimeException(x + " != " + y);
 180         }
 181     }
 182 
 183     static void assertEquals(Object x, Object y) {
 184         if (!Objects.equals(x,y)) {
 185             throw new RuntimeException(x + " != " + y);
 186         }
 187     }
 188 }