1 /* 2 * Copyright (c) 2013, 2019, 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 package jdk.jfr.api.consumer; 26 27 import java.nio.file.Files; 28 import java.nio.file.Path; 29 import java.time.Duration; 30 import java.util.ArrayList; 31 import java.util.List; 32 33 import jdk.jfr.Recording; 34 import jdk.jfr.consumer.RecordedEvent; 35 import jdk.jfr.consumer.RecordedFrame; 36 import jdk.jfr.consumer.RecordedStackTrace; 37 import jdk.jfr.consumer.RecordingFile; 38 import jdk.testlibrary.Asserts; 39 import jdk.testlibrary.jfr.EventNames; 40 import jdk.testlibrary.jfr.Events; 41 import jdk.testlibrary.jfr.RecurseThread; 42 43 /* 44 * @test 45 * @key jfr 46 * @library /lib/testlibrary 47 * @run main/othervm jdk.jfr.api.consumer.TestRecordedFullStackTrace 48 */ 49 public class TestRecordedFullStackTrace { 50 51 private final static String EVENT_NAME = EventNames.ExecutionSample; 52 private final static int MAX_DEPTH = 64; // currently hardcoded in jvm 53 54 public static void main(String[] args) throws Throwable { 55 56 RecurseThread[] threads = new RecurseThread[3]; 57 for (int i = 0; i < threads.length; ++i) { 58 int depth = MAX_DEPTH - 1 + i; 59 threads[i] = new RecurseThread(depth); 60 threads[i].setName("recursethread-" + depth); 61 threads[i].start(); 62 } 63 64 for (RecurseThread thread : threads) { 65 while (!thread.isInRunLoop()) { 66 Thread.sleep(20); 67 } 68 } 69 70 assertStackTraces(threads); 71 72 for (RecurseThread thread : threads) { 73 thread.quit(); 74 thread.join(); 75 } 76 } 77 78 private static void assertStackTraces(RecurseThread[] threads) throws Throwable { 79 Path path = null; 80 do { 81 Recording recording = new Recording(); 82 recording.enable(EVENT_NAME).withPeriod(Duration.ofMillis(50)); 83 recording.start(); 84 Thread.sleep(500); 85 recording.stop(); 86 // Dump the recording to a file 87 path = Files.createTempFile("recording", ".jfr"); 88 System.out.println("Dumping to " + path); 89 recording.dump(path); 90 recording.close(); 91 } while (!hasValidStackTraces(path, threads)); 92 } 93 94 private static boolean hasValidStackTraces(Path path, RecurseThread[] threads) throws Throwable { 95 boolean[] isEventFound = new boolean[threads.length]; 96 97 for (RecordedEvent event : RecordingFile.readAllEvents(path)) { 98 //System.out.println("Event: " + event); 99 String threadName = Events.assertField(event, "sampledThread.javaName").getValue(); 100 long threadId = Events.assertField(event, "sampledThread.javaThreadId").getValue(); 101 102 for (int threadIndex = 0; threadIndex < threads.length; ++threadIndex) { 103 RecurseThread currThread = threads[threadIndex]; 104 if (threadId == currThread.getId()) { 105 System.out.println("ThreadName=" + currThread.getName() + ", depth=" + currThread.totalDepth); 106 Asserts.assertEquals(threadName, currThread.getName(), "Wrong thread name"); 107 if ("recurseEnd".equals(getTopMethodName(event))) { 108 isEventFound[threadIndex] = true; 109 checkEvent(event, currThread.totalDepth); 110 break; 111 } 112 } 113 } 114 } 115 116 for (int i = 0; i < threads.length; ++i) { 117 String msg = "threadIndex=%d, recurseDepth=%d, isEventFound=%b%n"; 118 System.out.printf(msg, i, threads[i].totalDepth, isEventFound[i]); 119 } 120 for (int i = 0; i < threads.length; ++i) { 121 if (!isEventFound[i]) { 122 // no assertion, let's retry. 123 // Could be race condition, i.e safe point during Thread.sleep 124 System.out.println("Falied to validate all threads, will retry."); 125 return false; 126 } 127 } 128 return true; 129 } 130 131 public static String getTopMethodName(RecordedEvent event) { 132 List<RecordedFrame> frames = event.getStackTrace().getFrames(); 133 Asserts.assertFalse(frames.isEmpty(), "JavaFrames was empty"); 134 return frames.get(0).getMethod().getName(); 135 } 136 137 private static void checkEvent(RecordedEvent event, int expectedDepth) throws Throwable { 138 RecordedStackTrace stacktrace = null; 139 try { 140 stacktrace = event.getStackTrace(); 141 List<RecordedFrame> frames = stacktrace.getFrames(); 142 Asserts.assertEquals(Math.min(MAX_DEPTH, expectedDepth), frames.size(), "Wrong stacktrace depth. Expected:" + expectedDepth); 143 List<String> expectedMethods = getExpectedMethods(expectedDepth); 144 Asserts.assertEquals(expectedMethods.size(), frames.size(), "Wrong expectedMethods depth. Test error."); 145 146 for (int i = 0; i < frames.size(); ++i) { 147 String name = frames.get(i).getMethod().getName(); 148 String expectedName = expectedMethods.get(i); 149 System.out.printf("method[%d]=%s, expected=%s%n", i, name, expectedName); 150 Asserts.assertEquals(name, expectedName, "Wrong method name"); 151 } 152 153 boolean isTruncated = stacktrace.isTruncated(); 154 boolean isTruncateExpected = expectedDepth > MAX_DEPTH; 155 Asserts.assertEquals(isTruncated, isTruncateExpected, "Wrong value for isTruncated. Expected:" + isTruncateExpected); 156 157 String firstMethod = frames.get(frames.size() - 1).getMethod().getName(); 158 boolean isFullTrace = "run".equals(firstMethod); 159 String msg = String.format("Wrong values for isTruncated=%b, isFullTrace=%b", isTruncated, isFullTrace); 160 Asserts.assertTrue(isTruncated != isFullTrace, msg); 161 } catch (Throwable t) { 162 System.out.println(String.format("stacktrace:%n%s", stacktrace)); 163 throw t; 164 } 165 } 166 167 private static List<String> getExpectedMethods(int depth) { 168 List<String> methods = new ArrayList<>(); 169 methods.add("recurseEnd"); 170 for (int i = 0; i < depth - 2; ++i) { 171 methods.add((i % 2) == 0 ? "recurseA" : "recurseB"); 172 } 173 methods.add("run"); 174 if (depth > MAX_DEPTH) { 175 methods = methods.subList(0, MAX_DEPTH); 176 } 177 return methods; 178 } 179 }