Path: blob/master/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackLineNumbers.java
64478 views
/*1* Copyright (c) 2020, 2021, 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* @requires vm.flagless41* @library /test/lib42* @run driver TestJhsdbJstackLineNumbers43*/4445/*46* This test makes sure that SA gets the most accurate value for the line number of47* the current (topmost) frame. Many SA ports just rely on frame->bcp, but it is48* usually out of date since the current BCP is cached in a register and only flushed49* to frame->bcp when the register is needed for something else. Therefore SA ports50* need to fetch the register that the BCP is stored in and see if it is valid,51* and only defer to frame->bcp if it is not valid.52*53* The test works by spawning a process that sits in a 10 line loop in the busywork() method,54* all while the main test does repeated jstacks on the process. The expectation is55* that at least 5 of the lines in the busywork() loop will eventually show up in at56* least one of the jstack runs.57*/5859class LingeredAppWithBusyWork extends LingeredApp {60static volatile boolean stop = false;6162private static int busywork(int[] x) {63int i = 0;64while (!stop) {65i = x[0];66i += x[1];67i += x[2];68i += x[3];69i += x[4];70i += x[5];71i += x[6];72i += x[7];73}74return i;75}7677public static void main(String... args) {78Thread t = new Thread(() -> {79busywork(new int[]{0,1,2,3,4,5,6,7});80});8182try {83t.setName("BusyWorkThread");84t.start();85LingeredApp.main(args);86stop = true;87t.join();88} catch (InterruptedException e) {89}90}91}9293public class TestJhsdbJstackLineNumbers {94// This is the number of lines in the busywork main loop95static final int TOTAL_BUSYWORK_LOOP_LINES = 10;96// The minimum number of lines that we must at some point see in the jstack output97static final int MIN_BUSYWORK_LOOP_LINES = 5;9899static final int MAX_NUMBER_OF_JSTACK_RUNS = 25;100101private static OutputAnalyzer runJstack(String... toolArgs) throws Exception {102JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jhsdb");103launcher.addToolArg("jstack");104if (toolArgs != null) {105for (String toolArg : toolArgs) {106launcher.addToolArg(toolArg);107}108}109110ProcessBuilder processBuilder = SATestUtils.createProcessBuilder(launcher);111System.out.println(processBuilder.command().stream().collect(Collectors.joining(" ")));112OutputAnalyzer output = ProcessTools.executeProcess(processBuilder);113114return output;115}116117public static void runTest(long pid) throws Exception {118// Keep running jstack until the target app is in the "busywork" method.119String output;120int maxRetries = 5;121do {122if (maxRetries-- == 0) {123throw new RuntimeException("Failed: LingeredAppWithBusyWork never entered busywork() method.");124}125OutputAnalyzer jstackOut = runJstack("--pid", Long.toString(pid));126output = jstackOut.getOutput();127System.out.println(output);128} while (!output.contains("busywork"));129130// This is for tracking all the line numbers in busywork() that we've seen.131// Since it is a TreeSet, it will always be sorted and have no duplicates.132TreeSet<Integer> lineNumbersSeen = new TreeSet<Integer>();133134// Keep running jstack until we see a sufficient number of different line135// numbers in the busywork() loop.136for (int x = 0; x < MAX_NUMBER_OF_JSTACK_RUNS; x++) {137OutputAnalyzer jstackOut = runJstack("--pid", Long.toString(pid));138output = jstackOut.getOutput();139// The stack dump will have a line that looks like:140// - LingeredAppWithBusyWork.busywork(int[]) @bci=32, line=74 (Interpreted frame)141// We want to match on the line number, "74" in this example. We also match on the142// full line just so we can print it out.143Pattern LINE_PATTERN = Pattern.compile(144".+(- LingeredAppWithBusyWork.busywork\\(int\\[\\]\\) \\@bci\\=[0-9]+, line\\=([0-9]+) \\(Interpreted frame\\)).+", Pattern.DOTALL);145Matcher matcher = LINE_PATTERN.matcher(output);146if (matcher.matches()) {147System.out.println(matcher.group(1)); // print matching stack trace line148int lineNum = Integer.valueOf(matcher.group(2)); // get matching line number149lineNumbersSeen.add(lineNum);150if (lineNumbersSeen.size() == MIN_BUSYWORK_LOOP_LINES) {151// We're done!152System.out.println("Found needed line numbers after " + (x+1) + " iterations");153break;154}155} else {156System.out.println("failed to match");157System.out.println(output);158continue; // Keep trying. This can happen on rare occasions when the stack cannot be determined.159}160}161System.out.println("Found Line Numbers: " + lineNumbersSeen);162163// Make sure we saw the minimum required number of lines in busywork().164if (lineNumbersSeen.size() < MIN_BUSYWORK_LOOP_LINES) {165throw new RuntimeException("Failed: Didn't find enough line numbers: " + lineNumbersSeen);166}167168// Make sure the distance between the lowest and highest line numbers seen169// is not more than the number of lines in the busywork() loop.170if (lineNumbersSeen.last() - lineNumbersSeen.first() > TOTAL_BUSYWORK_LOOP_LINES) {171throw new RuntimeException("Failed: lowest and highest line numbers are too far apart: " + lineNumbersSeen);172}173174}175176public static void main(String... args) throws Exception {177SATestUtils.skipIfCannotAttach(); // throws SkippedException if attach not expected to work.178179LingeredApp theApp = null;180try {181// Launch the LingeredAppWithBusyWork process with the busywork() loop182theApp = new LingeredAppWithBusyWork();183LingeredApp.startAppExactJvmOpts(theApp, "-Xint");184System.out.println("Started LingeredApp with pid " + theApp.getPid());185186runTest(theApp.getPid());187} finally {188LingeredApp.stopApp(theApp);189System.out.println("LingeredAppWithBusyWork finished");190}191}192}193194195