static void trimStackTraces(Throwable thrown) {
        if (thrown == null)
            return;

        StackTraceElement[] stack = thrown.getStackTrace();
        int i = 0;
        int firstReflectFrame = -1;
        int cutoff = 0;
        boolean rootedInTestFramework = false;
        while (++i < stack.length) {
            rootedInTestFramework |= testFrameworkRootClasses.contains(stack[i].getClassName());
            if (firstReflectFrame == -1 && (stack[i].getClassName().startsWith("jdk.internal.reflect.") || stack[i].getClassName().startsWith("java.lang.reflect.")))
                firstReflectFrame = i; // jdk.internal.reflect class invokes the first user test frame, on both jdk 17 and 21.
            if (rootedInTestFramework && firstReflectFrame > 0) {
                cutoff = firstReflectFrame;
                break;
            }
            boolean isDynamicTestInvocation = "org.junit.jupiter.engine.descriptor.DynamicTestTestDescriptor".equals(stack[i].getClassName());
            if (isDynamicTestInvocation) {
                cutoff = i;
                break;
            }
        }
        if (cutoff == 0) {
            while (--i >= 0 && (   stack[i].isNativeMethod()
                                || stack[i].getClassName().startsWith("java.lang.reflect.")
                                || stack[i].getClassName().startsWith("java.util.")));
            cutoff = i + 1;
        }
        thrown.setStackTrace(copyOf(stack, cutoff));

        for (Throwable suppressed : thrown.getSuppressed())
            trimStackTraces(suppressed);

        trimStackTraces(thrown.getCause());
    }
Recursively trims stack traces for the given throwable and its causes/suppressed.
This is based on the assumption that the relevant stack is anything above the first native
reflection invocation, above any frame in the given root class.
vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/TestReport.java