Path: blob/master/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackLineNumbers.java
41149 views
/*1* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223import java.io.OutputStream;24import java.util.regex.Matcher;25import java.util.regex.Pattern;26import java.util.stream.Collectors;27import java.util.TreeSet;2829import jdk.test.lib.apps.LingeredApp;30import jdk.test.lib.JDKToolLauncher;31import jdk.test.lib.process.OutputAnalyzer;32import jdk.test.lib.process.ProcessTools;33import jdk.test.lib.SA.SATestUtils;3435/**36* @test37* @requires vm.hasSA38* @requires os.arch=="amd64" | os.arch=="x86_64"39* @requires os.family=="windows" | os.family == "linux" | os.family == "mac"40* @library /test/lib41* @run main/othervm TestJhsdbJstackLineNumbers42*/4344/*45* This test makes sure that SA gets the most accurate value for the line number of46* the current (topmost) frame. Many SA ports just rely on frame->bcp, but it is47* usually out of date since the current BCP is cached in a register and only flushed48* to frame->bcp when the register is needed for something else. Therefore SA ports49* need to fetch the register that the BCP is stored in and see if it is valid,50* and only defer to frame->bcp if it is not valid.51*52* The test works by spawning a process that sits in a 10 line loop in the busywork() method,53* all while the main test does repeated jstacks on the process. The expectation is54* that at least 5 of the lines in the busywork() loop will eventually show up in at55* least one of the jstack runs.56*/5758class LingeredAppWithBusyWork extends LingeredApp {59static volatile boolean stop = false;6061private static int busywork(int[] x) {62int i = 0;63while (!stop) {64i = x[0];65i += x[1];66i += x[2];67i += x[3];68i += x[4];69i += x[5];70i += x[6];71i += x[7];72}73return i;74}7576public static void main(String... args) {77Thread t = new Thread(() -> {78busywork(new int[]{0,1,2,3,4,5,6,7});79});8081try {82t.setName("BusyWorkThread");83t.start();84LingeredApp.main(args);85stop = true;86t.join();87} catch (InterruptedException e) {88}89}90}9192public class TestJhsdbJstackLineNumbers {93// This is the number of lines in the busywork main loop94static final int TOTAL_BUSYWORK_LOOP_LINES = 10;95// The minimum number of lines that we must at some point see in the jstack output96static final int MIN_BUSYWORK_LOOP_LINES = 5;9798static final int MAX_NUMBER_OF_JSTACK_RUNS = 25;99100private static OutputAnalyzer runJstack(String... toolArgs) throws Exception {101JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jhsdb");102launcher.addToolArg("jstack");103if (toolArgs != null) {104for (String toolArg : toolArgs) {105launcher.addToolArg(toolArg);106}107}108109ProcessBuilder processBuilder = SATestUtils.createProcessBuilder(launcher);110System.out.println(processBuilder.command().stream().collect(Collectors.joining(" ")));111OutputAnalyzer output = ProcessTools.executeProcess(processBuilder);112113return output;114}115116public static void runTest(long pid) throws Exception {117// Keep running jstack until the target app is in the "busywork" method.118String output;119int maxRetries = 5;120do {121if (maxRetries-- == 0) {122throw new RuntimeException("Failed: LingeredAppWithBusyWork never entered busywork() method.");123}124OutputAnalyzer jstackOut = runJstack("--pid", Long.toString(pid));125output = jstackOut.getOutput();126System.out.println(output);127} while (!output.contains("busywork"));128129// This is for tracking all the line numbers in busywork() that we've seen.130// Since it is a TreeSet, it will always be sorted and have no duplicates.131TreeSet<Integer> lineNumbersSeen = new TreeSet<Integer>();132133// Keep running jstack until we see a sufficient number of different line134// numbers in the busywork() loop.135for (int x = 0; x < MAX_NUMBER_OF_JSTACK_RUNS; x++) {136OutputAnalyzer jstackOut = runJstack("--pid", Long.toString(pid));137output = jstackOut.getOutput();138// The stack dump will have a line that looks like:139// - LingeredAppWithBusyWork.busywork(int[]) @bci=32, line=74 (Interpreted frame)140// We want to match on the line number, "74" in this example. We also match on the141// full line just so we can print it out.142Pattern LINE_PATTERN = Pattern.compile(143".+(- LingeredAppWithBusyWork.busywork\\(int\\[\\]\\) \\@bci\\=[0-9]+, line\\=([0-9]+) \\(Interpreted frame\\)).+", Pattern.DOTALL);144Matcher matcher = LINE_PATTERN.matcher(output);145if (matcher.matches()) {146System.out.println(matcher.group(1)); // print matching stack trace line147int lineNum = Integer.valueOf(matcher.group(2)); // get matching line number148lineNumbersSeen.add(lineNum);149if (lineNumbersSeen.size() == MIN_BUSYWORK_LOOP_LINES) {150// We're done!151System.out.println("Found needed line numbers after " + (x+1) + " iterations");152break;153}154} else {155System.out.println("failed to match");156System.out.println(output);157continue; // Keep trying. This can happen on rare occasions when the stack cannot be determined.158}159}160System.out.println("Found Line Numbers: " + lineNumbersSeen);161162// Make sure we saw the minimum required number of lines in busywork().163if (lineNumbersSeen.size() < MIN_BUSYWORK_LOOP_LINES) {164throw new RuntimeException("Failed: Didn't find enough line numbers: " + lineNumbersSeen);165}166167// Make sure the distance between the lowest and highest line numbers seen168// is not more than the number of lines in the busywork() loop.169if (lineNumbersSeen.last() - lineNumbersSeen.first() > TOTAL_BUSYWORK_LOOP_LINES) {170throw new RuntimeException("Failed: lowest and highest line numbers are too far apart: " + lineNumbersSeen);171}172173}174175public static void main(String... args) throws Exception {176SATestUtils.skipIfCannotAttach(); // throws SkippedException if attach not expected to work.177178LingeredApp theApp = null;179try {180// Launch the LingeredAppWithBusyWork process with the busywork() loop181theApp = new LingeredAppWithBusyWork();182LingeredApp.startAppExactJvmOpts(theApp, "-Xint");183System.out.println("Started LingeredApp with pid " + theApp.getPid());184185runTest(theApp.getPid());186} finally {187LingeredApp.stopApp(theApp);188System.out.println("LingeredAppWithBusyWork finished");189}190}191}192193194