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 import static java.lang.StackWalker.Option.*;
  25 import java.lang.StackWalker.StackFrame;
  26 import java.util.Arrays;
  27 import java.util.List;
  28 import java.util.Optional;
  29 import java.util.logging.Logger;
  30 import java.util.stream.Collectors;
  31 
  32 /**
  33  * @test
  34  * @bug 8140450
  35  * @summary Stack Stream Test
  36  * @run main/othervm StackStreamTest
  37  */
  38 public class StackStreamTest {
  39     public static void main(String[] argv) throws Exception {
  40         new StackStreamTest().test();
  41     }
  42 
  43     private static Logger logger = Logger.getLogger("stackstream");
  44     public StackStreamTest() {
  45     }
  46 
  47     public void test() {
  48         A.a();
  49     }
  50     static class A {
  51         public static void a() {
  52             B.b();
  53         }
  54     }
  55     static class B {
  56         public static void b() {
  57             C.c();
  58         }
  59     }
  60     static class C {
  61         public static void c() {
  62             D.d();
  63         }
  64     }
  65     static class D {
  66         public static void d() {
  67             E.e();
  68         }
  69     }
  70     static class E {
  71         public static void e() {
  72             F.f();
  73         }
  74     }
  75     static class F {
  76         public static void f() {
  77             logger.severe("log message");
  78             G.g();
  79             new K().k();
  80         }
  81     }
  82 
  83     private static boolean isTestClass(StackFrame f) {
  84         // Filter jtreg frames from the end of the stack
  85         return f.getClassName().startsWith("StackStreamTest");
  86     }
  87 
  88     static class G {
  89         static StackWalker STE_WALKER = StackWalker.create();
  90         static StackWalker DEFAULT_WALKER = StackWalker.create();
  91 
  92         private static final List<String> GOLDEN_CLASS_NAMES =
  93                 Arrays.asList("StackStreamTest$G",
  94                               "StackStreamTest$F",
  95                               "StackStreamTest$E",
  96                               "StackStreamTest$D",
  97                               "StackStreamTest$C",
  98                               "StackStreamTest$B",
  99                               "StackStreamTest$A",
 100                               "StackStreamTest",
 101                               "StackStreamTest");
 102         private static final List<String> GOLDEN_METHOD_NAMES =
 103             Arrays.asList("g", "f", "e", "d", "c", "b", "a", "test", "main");
 104 
 105 
 106         public static void g() {
 107 
 108             System.out.println("Thread dump");
 109             Thread.dumpStack();
 110 
 111             caller();
 112             firstFrame();
 113 
 114             // Check class names
 115             System.out.println("check class names");
 116             List<String> sfs = DEFAULT_WALKER.walk(s -> {
 117                 return s.filter(StackStreamTest::isTestClass)
 118                         .map(StackFrame::getClassName)
 119                         .collect(Collectors.toList());
 120             });
 121             equalsOrThrow("class names", sfs, GOLDEN_CLASS_NAMES);
 122 
 123             // Check method names
 124             System.out.println("methodNames()");
 125             sfs = DEFAULT_WALKER.walk(s -> {
 126                 return s.filter(StackStreamTest::isTestClass)
 127                         .map(StackFrame::getMethodName)
 128                         .collect(Collectors.toList());}
 129             );
 130             equalsOrThrow("method names", sfs, GOLDEN_METHOD_NAMES);
 131 
 132             Exception exc = new Exception("G.g stack");
 133             exc.printStackTrace();
 134 
 135             System.out.println("Stream of StackTraceElement");
 136             StackWalker.create()
 137                 .walk(s ->
 138                 {
 139                     s.map(StackFrame::toStackTraceElement)
 140                             .forEach(ste -> System.out.println("STE: " + ste));
 141                     return null;
 142                 });
 143 
 144             // Do we need this?
 145             System.out.println("Collect StackTraceElement");
 146             List<StackTraceElement> stacktrace = STE_WALKER.walk(s ->
 147             {
 148                 // Filter out jtreg frames
 149                 return s.filter(StackStreamTest::isTestClass)
 150                         .collect(Collectors.mapping(StackFrame::toStackTraceElement, Collectors.toList()));
 151             });
 152             int i=0;
 153             for (StackTraceElement s : stacktrace) {
 154                 System.out.format("  %d: %s%n", i++, s);
 155             }
 156 
 157             // Check STEs for correctness
 158             checkStackTraceElements(GOLDEN_CLASS_NAMES, GOLDEN_METHOD_NAMES, stacktrace);
 159         }
 160 
 161         static void checkStackTraceElements(List<String> classNames,
 162                                             List<String> methodNames,
 163                                             List<StackTraceElement> stes) {
 164             if (classNames.size() != methodNames.size() ) {
 165                 throw new RuntimeException("Test error: classNames and methodNames should be same size");
 166             }
 167             if (classNames.size() != stes.size()) {
 168                 dumpSTEInfo(classNames, methodNames, stes);
 169                 throw new RuntimeException("wrong number of elements in stes");
 170             }
 171             for (int i = 0; i < classNames.size() ; i++) {
 172                 if (!classNames.get(i).equals(stes.get(i).getClassName()) ||
 173                     !methodNames.get(i).equals(stes.get(i).getMethodName())) {
 174                     dumpSTEInfo(classNames, methodNames, stes);
 175                     throw new RuntimeException("class & method names don't match");
 176                 }
 177             }
 178         }
 179 
 180         static void dumpSTEInfo(List<String> classNames, List<String> methodNames,
 181                                 List<StackTraceElement> stes) {
 182             System.out.println("Observed class, method names:");
 183             for (StackTraceElement ste : stes) {
 184                 System.out.println("  " + ste.getClassName() + ", " + ste.getMethodName());
 185             }
 186             System.out.println("Expected class, method names:");
 187             for (int i = 0; i < classNames.size(); i++) {
 188                 System.out.println("  " + classNames.get(i) + ", " + methodNames.get(i));
 189             }
 190         }
 191 
 192         static void firstFrame() {
 193             System.out.println("first frame()");
 194             StackWalker sw = StackWalker.create(RETAIN_CLASS_REFERENCE);
 195             sw.forEach(e -> {
 196                 System.out.println(e.getClassName() + "," + e.getMethodName());
 197             });
 198             System.out.println("\n");
 199             Optional<StackFrame> frame = sw.walk(s ->
 200             {
 201                  return s.filter(e -> {
 202                             System.err.println(e.getClassName() + " == " +
 203                                                e.getClassName().equals("StackStreamTest"));
 204                             return e.getClassName().equals("StackStreamTest");
 205                         }).findFirst();
 206             });
 207             Class<?> c = frame.get().getDeclaringClass();
 208             System.out.println("\nfirst frame: " + c);
 209             if (c != StackStreamTest.class) {
 210                 throw new RuntimeException("Unexpected first caller class " + c);
 211             }
 212         }
 213     }
 214 
 215     private static <T> void equalsOrThrow(String label, List<T> list, List<T> expected) {
 216         System.out.println("List:    " + list);
 217         System.out.println("Expectd: " + list);
 218         if (!list.equals(expected)) {
 219             System.err.println("Observed " + label);
 220             for (T s1 : list) {
 221                 System.out.println("  " + s1);
 222             }
 223             System.err.println("Expected " + label);
 224             for (T s2 : expected) {
 225                 System.out.println("  " + s2);
 226             }
 227             throw new RuntimeException("Error with " + label);
 228         }
 229     }
 230 
 231 
 232     static class K {
 233         void k() {
 234             k1();
 235         }
 236         void k1() {
 237             k2();
 238         }
 239         void k2() {
 240             k3();
 241         }
 242         void k3() {
 243             k4();
 244         }
 245         void k4() {
 246             k5();
 247         }
 248         void k5() {
 249             k6();
 250         }
 251         void k6() {
 252             k7();
 253         }
 254         void k7() {
 255             k8();
 256         }
 257         void k8() {
 258             k9();
 259         }
 260         void k9() {
 261             k10();
 262         }
 263         void k10() {
 264             k20();
 265         }
 266         void k20() {
 267             new Caller().test();
 268         }
 269 
 270         class Caller {
 271             void test() {
 272                 Class<?> c = StackWalker.create(RETAIN_CLASS_REFERENCE).getCallerClass().get();
 273                 System.out.println("\nTesting K class : " + c);
 274                 Thread.dumpStack();
 275                 if (c != K.class) {
 276                     throw new RuntimeException("Unexpected caller class "+ c);
 277                 }
 278             }
 279         }
 280     }
 281 
 282     static void caller() {
 283         Class<?> c = StackWalker.create(RETAIN_CLASS_REFERENCE).getCallerClass().get();
 284         System.out.println("\ncaller class : " + c);
 285         if (c != G.class) {
 286             throw new RuntimeException("Unexpected caller class "+ c);
 287         }
 288     }
 289 
 290 }