1 /* 2 * Copyright (c) 2013, 2018, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.jfr.event.profiling; 27 28 import java.time.Duration; 29 import java.util.ArrayList; 30 import java.util.List; 31 32 import jdk.jfr.Recording; 33 import jdk.jfr.consumer.RecordedEvent; 34 import jdk.jfr.consumer.RecordedFrame; 35 import jdk.jfr.consumer.RecordedStackTrace; 36 import jdk.test.lib.Asserts; 37 import jdk.test.lib.jfr.EventNames; 38 import jdk.test.lib.jfr.Events; 39 import jdk.test.lib.jfr.RecurseThread; 40 41 /* 42 * @test 43 * @key jfr 44 * @library /test/lib 45 * @run main/othervm jdk.jfr.event.profiling.TestFullStackTrace 46 */ 47 public class TestFullStackTrace { 48 private final static String EVENT_NAME = EventNames.ExecutionSample; 49 private final static int MAX_DEPTH = 64; // currently hardcoded in jvm 50 51 public static void main(String[] args) throws Throwable { 52 RecurseThread[] threads = new RecurseThread[3]; 53 for (int i = 0; i < threads.length; ++i) { 54 int depth = MAX_DEPTH - 1 + i; 55 threads[i] = new RecurseThread(depth); 56 threads[i].setName("recursethread-" + depth); 57 threads[i].start(); 58 } 59 60 for (RecurseThread thread : threads) { 61 while (!thread.isInRunLoop()) { 62 Thread.sleep(20); 63 } 64 } 65 66 assertStackTraces(threads); 67 68 for (RecurseThread thread : threads) { 69 thread.quit(); 70 thread.join(); 71 } 72 } 73 74 private static void assertStackTraces( RecurseThread[] threads) throws Throwable { 75 Recording recording= null; 76 do { 77 recording = new Recording(); 78 recording.enable(EVENT_NAME).withPeriod(Duration.ofMillis(50)); 79 recording.start(); 80 Thread.sleep(500); 81 recording.stop(); 82 } while (!hasValidStackTraces(recording, threads)); 83 } 84 85 private static boolean hasValidStackTraces(Recording recording, RecurseThread[] threads) throws Throwable { 86 boolean[] isEventFound = new boolean[threads.length]; 87 88 for (RecordedEvent event : Events.fromRecording(recording)) { 89 //System.out.println("Event: " + event); 90 String threadName = Events.assertField(event, "sampledThread.javaName").getValue(); 91 long threadId = Events.assertField(event, "sampledThread.javaThreadId").getValue(); 92 93 for (int threadIndex = 0; threadIndex < threads.length; ++threadIndex) { 94 RecurseThread currThread = threads[threadIndex]; 95 if (threadId == currThread.getId()) { 96 System.out.println("ThreadName=" + currThread.getName() + ", depth=" + currThread.totalDepth); 97 Asserts.assertEquals(threadName, currThread.getName(), "Wrong thread name"); 98 if ("recurseEnd".equals(getTopMethodName(event))) { 99 isEventFound[threadIndex] = true; 100 checkEvent(event, currThread.totalDepth); 101 break; 102 } 103 } 104 } 105 } 106 107 for (int i = 0; i < threads.length; ++i) { 108 String msg = "threadIndex=%d, recurseDepth=%d, isEventFound=%b%n"; 109 System.out.printf(msg, i, threads[i].totalDepth, isEventFound[i]); 110 } 111 for (int i = 0; i < threads.length; ++i) { 112 if(!isEventFound[i]) { 113 // no assertion, let's retry. 114 // Could be race condition, i.e safe point during Thread.sleep 115 System.out.println("Falied to validate all threads, will retry."); 116 return false; 117 } 118 } 119 return true; 120 } 121 122 public static String getTopMethodName(RecordedEvent event) { 123 List<RecordedFrame> frames = event.getStackTrace().getFrames(); 124 Asserts.assertFalse(frames.isEmpty(), "JavaFrames was empty"); 125 return frames.get(0).getMethod().getName(); 126 } 127 128 private static void checkEvent(RecordedEvent event, int expectedDepth) throws Throwable { 129 RecordedStackTrace stacktrace = null; 130 try { 131 stacktrace = event.getStackTrace(); 132 List<RecordedFrame> frames = stacktrace.getFrames(); 133 Asserts.assertEquals(Math.min(MAX_DEPTH, expectedDepth), frames.size(), "Wrong stacktrace depth. Expected:" + expectedDepth); 134 List<String> expectedMethods = getExpectedMethods(expectedDepth); 135 Asserts.assertEquals(expectedMethods.size(), frames.size(), "Wrong expectedMethods depth. Test error."); 136 137 for (int i = 0; i < frames.size(); ++i) { 138 String name = frames.get(i).getMethod().getName(); 139 String expectedName = expectedMethods.get(i); 140 System.out.printf("method[%d]=%s, expected=%s%n", i, name, expectedName); 141 Asserts.assertEquals(name, expectedName, "Wrong method name"); 142 } 143 144 boolean isTruncated = stacktrace.isTruncated(); 145 boolean isTruncateExpected = expectedDepth > MAX_DEPTH; 146 Asserts.assertEquals(isTruncated, isTruncateExpected, "Wrong value for isTruncated. Expected:" + isTruncateExpected); 147 148 String firstMethod = frames.get(frames.size() - 1).getMethod().getName(); 149 boolean isFullTrace = "run".equals(firstMethod); 150 String msg = String.format("Wrong values for isTruncated=%b, isFullTrace=%b", isTruncated, isFullTrace); 151 Asserts.assertTrue(isTruncated != isFullTrace, msg); 152 } catch (Throwable t) { 153 System.out.println(String.format("stacktrace:%n%s", stacktrace)); 154 throw t; 155 } 156 } 157 158 private static List<String> getExpectedMethods(int depth) { 159 List<String> methods = new ArrayList<>(); 160 methods.add("recurseEnd"); 161 for (int i = 0; i < depth - 2; ++i) { 162 methods.add((i % 2) == 0 ? "recurseA" : "recurseB"); 163 } 164 methods.add("run"); 165 if (depth > MAX_DEPTH) { 166 methods = methods.subList(0, MAX_DEPTH); 167 } 168 return methods; 169 } 170 }