111ecdbaf5
8210375: StackWalker::getCallerClass throws UnsupportedOperationException Reviewed-by: coleenp, dfuchs, bchristi
307 lines
10 KiB
Java
307 lines
10 KiB
Java
/*
|
|
* Copyright (c) 2015, 2023, 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.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
import java.lang.StackWalker.StackFrame;
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
import java.util.Optional;
|
|
import java.util.Set;
|
|
import java.util.logging.Logger;
|
|
import java.util.stream.Collectors;
|
|
|
|
import static java.lang.StackWalker.Option.*;
|
|
|
|
/**
|
|
* @test
|
|
* @bug 8140450 8210375
|
|
* @summary Stack Stream Test
|
|
* @modules java.logging
|
|
* @run main/othervm StackStreamTest
|
|
*/
|
|
public class StackStreamTest {
|
|
public static void main(String[] argv) throws Exception {
|
|
new StackStreamTest().test();
|
|
}
|
|
|
|
private static Logger logger = Logger.getLogger("stackstream");
|
|
public StackStreamTest() {
|
|
}
|
|
|
|
public void test() {
|
|
A.a();
|
|
}
|
|
static class A {
|
|
public static void a() {
|
|
B.b();
|
|
}
|
|
}
|
|
static class B {
|
|
public static void b() {
|
|
C.c();
|
|
}
|
|
}
|
|
static class C {
|
|
public static void c() {
|
|
D.d();
|
|
}
|
|
}
|
|
static class D {
|
|
public static void d() {
|
|
E.e();
|
|
}
|
|
}
|
|
static class E {
|
|
public static void e() {
|
|
F.f();
|
|
}
|
|
}
|
|
static class F {
|
|
public static void f() {
|
|
logger.severe("log message");
|
|
G.g();
|
|
new K().k();
|
|
}
|
|
}
|
|
|
|
private static boolean isTestClass(StackFrame f) {
|
|
// Filter jtreg frames from the end of the stack
|
|
return f.getClassName().startsWith("StackStreamTest");
|
|
}
|
|
|
|
static class G {
|
|
static StackWalker STE_WALKER = StackWalker.getInstance();
|
|
static StackWalker DEFAULT_WALKER = StackWalker.getInstance();
|
|
|
|
private static final List<String> GOLDEN_CLASS_NAMES =
|
|
Arrays.asList("StackStreamTest$G",
|
|
"StackStreamTest$F",
|
|
"StackStreamTest$E",
|
|
"StackStreamTest$D",
|
|
"StackStreamTest$C",
|
|
"StackStreamTest$B",
|
|
"StackStreamTest$A",
|
|
"StackStreamTest",
|
|
"StackStreamTest");
|
|
private static final List<String> GOLDEN_METHOD_NAMES =
|
|
Arrays.asList("g", "f", "e", "d", "c", "b", "a", "test", "main");
|
|
|
|
|
|
public static void g() {
|
|
|
|
System.out.println("Thread dump");
|
|
Thread.dumpStack();
|
|
|
|
caller();
|
|
firstFrame();
|
|
|
|
// Check class names
|
|
System.out.println("check class names");
|
|
List<String> sfs = DEFAULT_WALKER.walk(s -> {
|
|
return s.filter(StackStreamTest::isTestClass)
|
|
.map(StackFrame::getClassName)
|
|
.collect(Collectors.toList());
|
|
});
|
|
equalsOrThrow("class names", sfs, GOLDEN_CLASS_NAMES);
|
|
|
|
// Check method names
|
|
System.out.println("methodNames()");
|
|
sfs = DEFAULT_WALKER.walk(s -> {
|
|
return s.filter(StackStreamTest::isTestClass)
|
|
.map(StackFrame::getMethodName)
|
|
.collect(Collectors.toList());}
|
|
);
|
|
equalsOrThrow("method names", sfs, GOLDEN_METHOD_NAMES);
|
|
|
|
Exception exc = new Exception("G.g stack");
|
|
exc.printStackTrace();
|
|
|
|
System.out.println("Stream of StackTraceElement");
|
|
StackWalker.getInstance()
|
|
.walk(s ->
|
|
{
|
|
s.map(StackFrame::toStackTraceElement)
|
|
.forEach(ste -> System.out.println("STE: " + ste));
|
|
return null;
|
|
});
|
|
|
|
// Do we need this?
|
|
System.out.println("Collect StackTraceElement");
|
|
List<StackTraceElement> stacktrace = STE_WALKER.walk(s ->
|
|
{
|
|
// Filter out jtreg frames
|
|
return s.filter(StackStreamTest::isTestClass)
|
|
.collect(Collectors.mapping(StackFrame::toStackTraceElement, Collectors.toList()));
|
|
});
|
|
int i=0;
|
|
for (StackTraceElement s : stacktrace) {
|
|
System.out.format(" %d: %s%n", i++, s);
|
|
}
|
|
|
|
// Check STEs for correctness
|
|
checkStackTraceElements(GOLDEN_CLASS_NAMES, GOLDEN_METHOD_NAMES, stacktrace);
|
|
|
|
System.out.println("Collect classes");
|
|
List<Class<?>> classes = StackWalker.getInstance(Set.of(DROP_METHOD_INFO, RETAIN_CLASS_REFERENCE))
|
|
.walk(s -> {
|
|
return s.map(StackFrame::getDeclaringClass).collect(Collectors.toList());
|
|
});
|
|
for (i=0; i < GOLDEN_CLASS_NAMES.size(); i++) {
|
|
Class<?> c = classes.get(i);
|
|
if (!GOLDEN_CLASS_NAMES.get(i).equals(c.getName())) {
|
|
throw new RuntimeException("unexpected class at " + i + " " + c.getName() +
|
|
" expected " + GOLDEN_CLASS_NAMES.get(i));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void checkStackTraceElements(List<String> classNames,
|
|
List<String> methodNames,
|
|
List<StackTraceElement> stes) {
|
|
if (classNames.size() != methodNames.size() ) {
|
|
throw new RuntimeException("Test error: classNames and methodNames should be same size");
|
|
}
|
|
if (classNames.size() != stes.size()) {
|
|
dumpSTEInfo(classNames, methodNames, stes);
|
|
throw new RuntimeException("wrong number of elements in stes");
|
|
}
|
|
for (int i = 0; i < classNames.size() ; i++) {
|
|
if (!classNames.get(i).equals(stes.get(i).getClassName()) ||
|
|
!methodNames.get(i).equals(stes.get(i).getMethodName())) {
|
|
dumpSTEInfo(classNames, methodNames, stes);
|
|
throw new RuntimeException("class & method names don't match");
|
|
}
|
|
}
|
|
}
|
|
|
|
static void dumpSTEInfo(List<String> classNames, List<String> methodNames,
|
|
List<StackTraceElement> stes) {
|
|
System.out.println("Observed class, method names:");
|
|
for (StackTraceElement ste : stes) {
|
|
System.out.println(" " + ste.getClassName() + ", " + ste.getMethodName());
|
|
}
|
|
System.out.println("Expected class, method names:");
|
|
for (int i = 0; i < classNames.size(); i++) {
|
|
System.out.println(" " + classNames.get(i) + ", " + methodNames.get(i));
|
|
}
|
|
}
|
|
|
|
static void firstFrame() {
|
|
System.out.println("first frame()");
|
|
StackWalker sw = StackWalker.getInstance(RETAIN_CLASS_REFERENCE);
|
|
sw.forEach(e -> {
|
|
System.out.println(e.getClassName() + "," + e.getMethodName());
|
|
});
|
|
System.out.println("\n");
|
|
Optional<StackFrame> frame = sw.walk(s ->
|
|
{
|
|
return s.filter(e -> {
|
|
System.err.println(e.getClassName() + " == " +
|
|
e.getClassName().equals("StackStreamTest"));
|
|
return e.getClassName().equals("StackStreamTest");
|
|
}).findFirst();
|
|
});
|
|
Class<?> c = frame.get().getDeclaringClass();
|
|
System.out.println("\nfirst frame: " + c);
|
|
if (c != StackStreamTest.class) {
|
|
throw new RuntimeException("Unexpected first caller class " + c);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static <T> void equalsOrThrow(String label, List<T> list, List<T> expected) {
|
|
System.out.println("List: " + list);
|
|
System.out.println("Expected: " + expected);
|
|
if (!list.equals(expected)) {
|
|
System.err.println("Observed " + label);
|
|
for (T s1 : list) {
|
|
System.out.println(" " + s1);
|
|
}
|
|
System.err.println("Expected " + label);
|
|
for (T s2 : expected) {
|
|
System.out.println(" " + s2);
|
|
}
|
|
throw new RuntimeException("Error with " + label);
|
|
}
|
|
}
|
|
|
|
|
|
static class K {
|
|
void k() {
|
|
k1();
|
|
}
|
|
void k1() {
|
|
k2();
|
|
}
|
|
void k2() {
|
|
k3();
|
|
}
|
|
void k3() {
|
|
k4();
|
|
}
|
|
void k4() {
|
|
k5();
|
|
}
|
|
void k5() {
|
|
k6();
|
|
}
|
|
void k6() {
|
|
k7();
|
|
}
|
|
void k7() {
|
|
k8();
|
|
}
|
|
void k8() {
|
|
k9();
|
|
}
|
|
void k9() {
|
|
k10();
|
|
}
|
|
void k10() {
|
|
k20();
|
|
}
|
|
void k20() {
|
|
new Caller().test();
|
|
}
|
|
|
|
class Caller {
|
|
void test() {
|
|
Class<?> c = StackWalker.getInstance(RETAIN_CLASS_REFERENCE).getCallerClass();
|
|
System.out.println("\nTesting K class : " + c);
|
|
Thread.dumpStack();
|
|
if (c != K.class) {
|
|
throw new RuntimeException("Unexpected caller class "+ c);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void caller() {
|
|
Class<?> c = StackWalker.getInstance(RETAIN_CLASS_REFERENCE).getCallerClass();
|
|
System.out.println("\ncaller class : " + c);
|
|
if (c != G.class) {
|
|
throw new RuntimeException("Unexpected caller class "+ c);
|
|
}
|
|
}
|
|
|
|
}
|