diff --git a/test/hotspot/jtreg/compiler/codecache/stress/CodecacheMemoryCheck.sh b/test/hotspot/jtreg/compiler/codecache/stress/CodecacheMemoryCheck.sh new file mode 100755 index 00000000000..c4822d29a28 --- /dev/null +++ b/test/hotspot/jtreg/compiler/codecache/stress/CodecacheMemoryCheck.sh @@ -0,0 +1,222 @@ +#!/bin/bash +# Copyright (c) 2024 Alibaba Group Holding Limited. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. + +# @test +# @key stress randomness +# @summary check memory usage of optimizations and deoptimizations +# @library /test/lib / +# @modules java.base/jdk.internal.misc java.management +# @build sun.hotspot.WhiteBox compiler.codecache.stress.Helper compiler.codecache.stress.TestCaseImpl +# @build compiler.codecache.stress.UnexpectedDeoptimizationTest +# @build compiler.codecache.stress.UnexpectedDeoptimizationTestLoop +# @run driver ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission +# @run shell/timeout=7200 CodecacheMemoryCheck.sh + + +# set a few environment variables so that the shell-script can run stand-alone +# in the source directory +if [ "${TESTSRC}" = "" ] ; then + TESTSRC="." +fi +if [ "${TESTJAVA}" = "" ] ; then + echo "TESTJAVA not set. Test cannot execute." + echo "FAILED!!!" + exit 1 +fi +if [ "${COMPILEJAVA}" = "" ]; then + COMPILEJAVA="${TESTJAVA}" +fi + +loopCount=40 +if [[ -n "$1" ]] ; then + loopCount=$1 +fi + +# set platform-dependent variables +OS=`uname -s` +case "$OS" in + SunOS ) + PATHSEP=":" + FILESEP="/" + ;; + Linux ) + PATHSEP=":" + FILESEP="/" + ;; + Darwin ) + PATHSEP=":" + FILESEP="/" + ;; + AIX ) + PATHSEP=":" + FILESEP="/" + ;; + CYGWIN* ) + PATHSEP=";" + FILESEP="/" + ;; + Windows* ) + PATHSEP=";" + FILESEP="\\" + ;; + * ) + echo "Unrecognized system!" + exit 1; + ;; +esac + +useJcmdPrintMemoryUsage() +{ + pid=$1 + javaLog=$2 + i=0 + while ! grep -q "For random generator using seed" ${javaLog} + do + sleep 0.1 #wait util java main function start finish + if [[ $i -ge 100 ]] ; then + echo "The tested java seems work abnormally!" + exit 1 + fi + let i++ + done + i=0 + rm -rf *-native_memory-summary.log + while kill -0 ${pid} 2>/dev/null + do + ${TESTJAVA}${FS}bin${FS}jcmd ${pid} VM.native_memory summary &> ${i}-native_memory-summary.log + if [[ 0 -ne $? ]] ; then + if grep -q "Exception" ${i}-native_memory-summary.log || ! kill -0 ${pid} ; then + #The target java process has been teminated/finished + #java.io.IOException: No such process + #com.sun.tools.attach.AttachNotSupportedException: Unable to parse namespace + #java.io.IOException: Premature EOF + mv ${i}-native_memory-summary.log jcmd-exception.log + break + else + if kill -0 $$ ; then + echo "jcmd command execute fail!" + exit 1 + else + mv ${i}-native_memory-summary.log jcmd-error.log + break + fi + fi + fi + let i++ + sleep 2 + done +} + +getMemoryUsageFromProc() +{ + pid=$1 + javaLog=$2 + i=0 + while ! grep -q "For random generator using seed" ${javaLog} + do + sleep 0.1 #wait util java main function start finish + if [[ $i -ge 100 ]] ; then + echo "The tested java seems work abnormally!" + exit 1 + fi + let i++ + done + mkdir -p plot-data + rm -rf proc-*.csv plot-data/proc-*.txt + echo -n "VmSize" > proc-VmSize.csv + echo -n "VmRSS" > proc-VmRSS.csv + echo -n "PageNum" > proc-PageNum.csv + i=0 + while kill -0 ${pid} 2>/dev/null + do + VmSize=`grep -w VmSize /proc/${pid}/status | awk '{print $2}'` + VmRSS=`grep -w VmRSS /proc/${pid}/status | awk '{print $2}'` + PageNum=`cat /proc/${pid}/statm | awk '{print $1}'` + if kill -0 ${pid} ; then + echo -n ",${VmSize}" >> proc-VmSize.csv + echo -n ",${VmRSS}" >> proc-VmRSS.csv + echo -n ",${PageNum}" >> proc-PageNum.csv + echo "${i} ${VmSize}" >> plot-data/proc-VmSize.txt + echo "${i} ${VmRSS}" >> plot-data/proc-VmRSS.txt + echo "${i} ${PageNum}" >> plot-data/proc-PageNum.txt + let i++; + fi + sleep 2 + done + echo "" >> proc-VmSize.csv + echo "" >> proc-VmRSS.csv + echo "" >> proc-PageNum.csv + cat proc-VmSize.csv proc-VmRSS.csv proc-PageNum.csv > proc.csv +} + +generatePlotPNG() +{ + if [[ ! -d plot-data ]] ; then + echo "echo plot-data directory not exist!" + return + fi + if [[ ! -f ${TESTSRC}/plot.gp ]] ; then + echo "${TESTSRC}/plot.gp not exists!" + return + fi + if ! which gnuplot ; then + echo please install gnuplot command! + return + fi + for file in `ls plot-data | grep "\.txt$"` + do + name=`basename $file .txt` + echo plot ${name} + gnuplot -c ${TESTSRC}/plot.gp "plot-data/${file}" "${name}" "plot-data/${name}.png" + done + if which zip ; then + rm -rf plot-data.zip + zip -rq9 plot-data.zip plot-data + else + tar cf - plot-data | xz -9 -T `nproc` > plot-data.tar.xz + fi +} + +set -x +commonJvmOptions="-Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:-DeoptimizeRandom \ + -XX:CompileCommand=dontinline,compiler.codecache.stress.Helper\$TestCase::method -XX:NativeMemoryTracking=summary" + +rm -rf java.log plot-data +mkdir -p plot-data +${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} ${TESTJAVAOPTS} ${commonJvmOptions} \ + -Dtest.src=${TESTSRC} -cp ${TESTCLASSPATH} compiler.codecache.stress.UnexpectedDeoptimizationTestLoop ${loopCount} &> java.log & +pid=$! +ps -ef | grep java | grep UnexpectedDeoptimizationTestLoop &> ps-java.log +getMemoryUsageFromProc ${pid} java.log 2> proc-detail-stderr.log & +useJcmdPrintMemoryUsage ${pid} java.log 2> jcmd-detail-stderr.log +if ( set +x ; grep -q "Unable to open socket file" *-native_memory-summary.log ) ; then + echo 'jcmd report error: "-native_memory-summary.log"' + exit 1 +fi + +( set +x ; perl -w ${TESTSRC}/get-native-memory-usage.pl 25 "Code-malloc:2.5,Code-mmap:2.8,Compiler-malloc:4.6" `ls *-native_memory-summary.log | sort -n | xargs` ) +exitCode=$? +generatePlotPNG &> generatePlotPNG.log + +( set +x ; mkdir -p native_memory-summary ; mv *-native_memory-summary.log native_memory-summary/ ) + +exit ${exitCode} diff --git a/test/hotspot/jtreg/compiler/codecache/stress/CodecacheMemoryCheckPress.sh b/test/hotspot/jtreg/compiler/codecache/stress/CodecacheMemoryCheckPress.sh new file mode 100755 index 00000000000..272c2009679 --- /dev/null +++ b/test/hotspot/jtreg/compiler/codecache/stress/CodecacheMemoryCheckPress.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# Copyright (c) 2024 Alibaba Group Holding Limited. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. + +# @test +# @key stress randomness +# @summary check memory usage of optimizations and deoptimizations +# @library /test/lib / +# @modules java.base/jdk.internal.misc java.management +# @build sun.hotspot.WhiteBox compiler.codecache.stress.Helper compiler.codecache.stress.TestCaseImpl +# @build compiler.codecache.stress.UnexpectedDeoptimizationTest +# @build compiler.codecache.stress.UnexpectedDeoptimizationTestLoop +# @run driver ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission +# @run shell/manual CodecacheMemoryCheckPress.sh + +bash ${TESTSRC}/CodecacheMemoryCheck.sh 400 diff --git a/test/hotspot/jtreg/compiler/codecache/stress/UnexpectedDeoptimizationTestLoop.java b/test/hotspot/jtreg/compiler/codecache/stress/UnexpectedDeoptimizationTestLoop.java new file mode 100644 index 00000000000..265d9a1cf9a --- /dev/null +++ b/test/hotspot/jtreg/compiler/codecache/stress/UnexpectedDeoptimizationTestLoop.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package compiler.codecache.stress; + +public class UnexpectedDeoptimizationTestLoop extends UnexpectedDeoptimizationTest { + public static void main(String[] args) { + int loop = 25; + if (args.length != 0) { + loop = Integer.valueOf(args[0]); + } + System.out.println("loop count = " + loop); + for (int i = 0; i < loop; i++) { + new CodeCacheStressRunner(new UnexpectedDeoptimizationTest()).runTest(); + } + } +} diff --git a/test/hotspot/jtreg/compiler/codecache/stress/get-native-memory-usage.pl b/test/hotspot/jtreg/compiler/codecache/stress/get-native-memory-usage.pl new file mode 100755 index 00000000000..c7a3cdecd58 --- /dev/null +++ b/test/hotspot/jtreg/compiler/codecache/stress/get-native-memory-usage.pl @@ -0,0 +1,301 @@ +#!/usr/bin/perl -w +# Copyright (c) 2024 Alibaba Group Holding Limited. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +#usage: perl -w ${TESTSRC}/get-native-memory-usage.pl 25 "Code-malloc:2.5,Code-mmap:2.8,Compiler-malloc:4.6" `ls *-native_memory-summary.log | sort -n | xargs` +use strict; +use warnings; +use POSIX; +use File::Path qw(make_path); +my $verbose = 0; + +die "please input split number and more than 3 jcmd native log files" if( @ARGV < 10 ); +my $split = shift(@ARGV); +my $rules = shift(@ARGV); +my $baseline = parserJcmdResult(shift(@ARGV)); +my @nameArray; +my %resultCsv; +my %resultMaxValue; +my %resultMaxIndex; +my %resultMinValue; +my %resultMinIndex; +my %resultQuarterValue; +my %resultThirdValue; +my %resultHalfValue; +my %resultLastValue; +my %isIncreasementalResultHash; +my $memoryLeakNumber = 0; +my $plotDataDir = "plot-data"; +my $lastFile = $ARGV[-1]; +$lastFile =~ /^([0-9]+)-.*?/; +my $lastIndex = $1; +my $quarterIndex = ceil($lastIndex / 4); +my $thirdIndex = ceil($lastIndex / 3); +my $halfIndex = ceil($lastIndex / 2); +die "lastIndex undefine!" if( ! defined $lastIndex ); + +foreach my $key ( sort keys %$baseline ) +{ + my $value = $baseline->{$key}; + print("first line : $key : $value\n") if( $verbose > 1 ); + push @nameArray, $key; + $resultCsv{$key} = "$key" . "," . "$value"; + $resultMaxIndex{$key} = 0; + $resultMaxValue{$key} = $value; + $resultMinIndex{$key} = 0; + $resultMinValue{$key} = $value; +} +foreach my $file ( @ARGV ) +{ + $file =~ /^([0-9]+)-.*?/; + my $index = $1; + die "index undefine!" if( ! defined $index ); + my $data = parserJcmdResult($file); + foreach my $key ( sort @nameArray ) + { + my $value = $data->{$key}; + print("$index : $key : $value\n") if( $verbose > 1 ); + $resultCsv{$key} = $resultCsv{$key} . "," . "$value"; + if( $value > $resultMaxValue{$key} ) + { + $resultMaxIndex{$key} = $index; + $resultMaxValue{$key} = $value; + } + if( $value < $resultMinValue{$key} ) + { + $resultMinIndex{$key} = $index; + $resultMinValue{$key} = $value; + } + if( $index == $quarterIndex ) + { + $resultQuarterValue{$key} = $value; + } + if( $index == $thirdIndex ) + { + $resultThirdValue{$key} = $value; + } + if( $index == $halfIndex ) + { + $resultHalfValue{$key} = $value; + } + if( $index == $lastIndex ) + { + $resultLastValue{$key} = $value; + } + } +} + +if( ! -d $plotDataDir ) +{ + make_path($plotDataDir); +} + +open(my $csvFh, ">native-memory-summary.csv"); +open(my $summaryFh, ">native-memory-summary.txt"); +print $summaryFh ("total $lastIndex files, quarter index is $quarterIndex, third index is $thirdIndex, half index is $halfIndex.\n"); +foreach my $key ( sort @nameArray ) +{ + my @data = split /,/, $resultCsv{$key}; + my $name = shift(@data); + die "key=$key != name=$name" if( $name ne $key ); + + print $csvFh "$resultCsv{$key}\n"; + my $maxMultiple = sprintf("%.1f", $resultMaxValue{$key} / $resultMinValue{$key}); + my $quarterMultiple = sprintf("%.1f", $resultLastValue{$key} / $resultQuarterValue{$key}); + my $thirdMultiple = sprintf("%.1f", $resultLastValue{$key} / $resultThirdValue{$key}); + my $halfMultiple = sprintf("%.1f", $resultLastValue{$key} / $resultHalfValue{$key}); + my $thirdSurprise = ""; + my $isIncreasementalResult = isIncreasemental($name, @data); + $isIncreasementalResultHash{$name} = $isIncreasementalResult; + my $isMemoryLeak = ""; + if( $thirdMultiple >= 2.5 ) + { + $thirdSurprise = "!!"; + if( $isIncreasementalResult == 0 ) + { + $isMemoryLeak = "\tMemoryLeak!!!" + } + } + print $summaryFh "$key\tmax=$resultMaxValue{$key},index=$resultMaxIndex{$key}\tmin=$resultMinValue{$key},index=$resultMinIndex{$key}\tquarter=$resultQuarterValue{$key},third=$resultThirdValue{$key},half=$resultHalfValue{$key}\tmax/min=$maxMultiple,last/quarter=$quarterMultiple,last/half=$halfMultiple,last/third=$thirdMultiple$thirdSurprise\tisIncreasemental=$isIncreasementalResult$isMemoryLeak\n"; + + #write plot data + my $i = 0; + open(my $fh, ">$plotDataDir/$name.txt"); + foreach my $value ( @data ) + { + print $fh "$i $value\n"; + $i++; + } + close($fh); +} +close($csvFh); +close($summaryFh); + + +my $lastIndexResultHash = parserJcmdResult($ARGV[-1]); +my $thirdIndexResultHash = parserJcmdResult($ARGV[ceil(scalar(@ARGV)/3)]); +foreach my $rule ( split /,/, $rules ) +{ + print("rule: $rule\n"); + my($moduleName, $coefficient) = split /:/, $rule; + print("$moduleName: $coefficient\n") if( $verbose > 3 ); + my $lastIndexValue = $lastIndexResultHash->{$moduleName}; + my $thirdIndexValue = $thirdIndexResultHash->{$moduleName}; + die "can't find $moduleName memory usage information!" if( ! defined $lastIndexValue ); + die "can't find $moduleName memory usage information!" if( ! defined $thirdIndexValue ); + my $compareValue = $thirdIndexValue * $coefficient; + if( $lastIndexValue > $compareValue && $isIncreasementalResultHash{$moduleName} == 0 ) + { + warn("$moduleName: $lastIndexValue > $compareValue=$thirdIndexValue*$coefficient"); + $memoryLeakNumber++; + } +} +if( $memoryLeakNumber > 0 ) +{ + die "memoryLeakNumber=$memoryLeakNumber!"; +} + + + +sub parserJcmdResult +{ + my ($filename) = @_; + my %malloc; + my $name; + my $number; + open(my $fh, "<$filename") or die "Can't open file '$filename' $!"; + foreach my $line ( <$fh> ) + { + chomp($line); + if( $line =~ /^-\s*(.*)\s+\(/ ) + { + $name = $1; + $name =~ s/\s+//g; + $number = -1; + next; + } + if( $line =~ /\(malloc=([0-9]+)KB/ ) + { + $number = $1; + die "filename=$filename\tline=$line can't get name!\n" if( length($name) <= 0 ); + my $key = "$name" . "-malloc"; + print("name=$key\t\tnumber=$number\n") if( $verbose == 1 ); + if( $number == 0 ) + { + if( $verbose > 0 ) + { + warn("$key value is 0"); + } + $number = 0.01; + } + $malloc{$key} = $number; + next; + } + if( $line =~ /\(mmap:.*committed=([0-9]+)KB/ ) + { + $number = $1; + die "filename=$filename\tline=$line can't get name!\n" if( length($name) <= 0 ); + my $key = "$name" . "-mmap"; + print("name=$key\t\tnumber=$number\n") if( $verbose == 1 ); + if( $number == 0 ) + { + if( $verbose > 0 ) + { + warn("$key value is 0"); + } + $number = 1; + } + $malloc{$key} = $number; + next; + } + } + close($fh); + return \%malloc; +}; + +sub isIncreasemental +{ + my $name = shift(@_); + my @array = @_; + my $length = scalar(@array); + my $windowLength = floor($length/$split); + warn("$name: windowLength=$windowLength\n") if( $verbose > 0 ); + my $count = $windowLength * $split; + warn("$name: count=$count, $length=$length\n") if( $verbose > 0 );; + my $previousSum = 0; + my $steady = 0; + my $result = 0; + + #calculate the main part data + foreach my $i ( 0..$split-1 ) + { + my $currentSum = 0; + foreach my $j (0..$windowLength-1) + { + my $index = $i*$windowLength+$j; + $currentSum += $array[$i*$windowLength+$j]; + } + $currentSum /= $windowLength; + warn("$name: currentSum=$currentSum, previousSum=$previousSum\n") if( $verbose >= 3 ); + if( $currentSum < $previousSum ) + { + $result++; + warn("$name: currentSum=$currentSum, previousSum=$previousSum\n") if( $verbose >= 1 ); + } + elsif( $currentSum == $previousSum ) + { + $steady++; + } + $previousSum = $currentSum; + } + + # warn("$name: steady=$steady, split=$split\n") if( $verbose >= 0 ); + #calculate the tail data + if( ($length-$count) != 0 ) + { + my $currentSum = 0; + foreach my $i ( $count .. ($length-1) ) + { + $currentSum += $array[$i];; + } + $currentSum /= ($length-$count); + if( $currentSum < $previousSum ) + { + $result++; + warn("$name: currentSum=$currentSum, previousSum=$previousSum\n") if( $verbose >= 1 ); + } + elsif( $currentSum == $previousSum || abs($currentSum-$previousSum) > 0.1 ) + { + $steady++; + } + } + else + { + warn("$name: tail count is zero.\n") if( $verbose >= 1 ); + } + + #statistics the result + warn("$name: steady=$steady, split=$split\n") if( $verbose >= 2 ); + if( $steady == $split ) + { + $result = -1; + } + return $result; +} diff --git a/test/hotspot/jtreg/compiler/codecache/stress/plot.gp b/test/hotspot/jtreg/compiler/codecache/stress/plot.gp new file mode 100755 index 00000000000..90844288249 --- /dev/null +++ b/test/hotspot/jtreg/compiler/codecache/stress/plot.gp @@ -0,0 +1,27 @@ +# plot.gp +if (ARG1 eq "") { + print "Usage: gnuplot plot.gp <outputfile>" + exit +} + +if (ARG2 eq "") { + title = "Default Title" +} else { + title = ARG2 +} + +if (ARG3 eq "") { + output = "output.png" +} else { + output = ARG3 +} + +set terminal png +set output output + +set title title + +set xlabel "X-axis" +set ylabel "Y-axis" + +plot ARG1 using 1:2 with lines title title