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