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