mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-26 07:40:09 +00:00
Merge
This commit is contained in:
commit
7feb08a6b4
2
.hgtags
2
.hgtags
@ -288,3 +288,5 @@ b409bc51bc23cfd51f2bd04ea919ec83535af9d0 jdk9-b37
|
||||
6494b13f88a867026ee316b444d9a4fa589dd6bd jdk9-b43
|
||||
abbfccd659b91a7bb815d5e36fed635dcdd40f31 jdk9-b44
|
||||
bfc24ae2b900187585079bb11e66e459d1e525fe jdk9-b45
|
||||
722378bc599e38d9a1dd484de30f10dfd7b21438 jdk9-b46
|
||||
8327024a99559982b848e9c2191da9c0bf8838fd jdk9-b47
|
||||
|
||||
@ -288,3 +288,5 @@ f7c11da0b0481d49cc7a65a453336c108191e821 jdk9-b42
|
||||
02ee8c65622e8bd97496d584e22fc7dcf0edc4ae jdk9-b43
|
||||
8994f5d87b3bb5e8d317d4e8ccb326da1a73684a jdk9-b44
|
||||
3dd628fde2086218d548841022ee8436b6b88185 jdk9-b45
|
||||
12f1e276447bcc81516e85367d53e4f08897049d jdk9-b46
|
||||
b6cca3e6175a69f39e5799b7349ddb0176630291 jdk9-b47
|
||||
|
||||
7
common/autoconf/configure
vendored
7
common/autoconf/configure
vendored
@ -36,6 +36,13 @@ else
|
||||
shift
|
||||
fi
|
||||
|
||||
if test "x$BASH" = x; then
|
||||
echo "Error: This script must be run using bash." 1>&2
|
||||
exit 1
|
||||
fi
|
||||
# Force autoconf to use bash
|
||||
export CONFIG_SHELL=$BASH
|
||||
|
||||
conf_script_dir="$TOPDIR/common/autoconf"
|
||||
|
||||
if [ "$CUSTOM_CONFIG_DIR" = "" ]; then
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
# questions.
|
||||
#
|
||||
|
||||
# This script is processed by configure before it's usable. It is run from
|
||||
# This script is processed by configure before it's usable. It is run from
|
||||
# the root of the build directory.
|
||||
|
||||
|
||||
@ -76,10 +76,13 @@ diff_text() {
|
||||
TMP=1
|
||||
|
||||
if [[ "$THIS_FILE" = *"META-INF/MANIFEST.MF" ]]; then
|
||||
# Filter out date string, ant version and java version differences.
|
||||
TMP=$(LC_ALL=C $DIFF $OTHER_FILE $THIS_FILE | \
|
||||
$GREP '^[<>]' | \
|
||||
$SED -e '/[<>] Ant-Version: Apache Ant .*/d' \
|
||||
-e '/[<>] Created-By: .* (Oracle Corporation).*/d')
|
||||
-e '/[<>] Created-By: .* (Oracle [Corpatin)]*/d' \
|
||||
-e '/[<>] [Corpatin]*)/d' \
|
||||
-e '/[<>].*[0-9]\{4\}_[0-9]\{2\}_[0-9]\{2\}_[0-9]\{2\}_[0-9]\{2\}-b[0-9]\{2\}.*/d')
|
||||
fi
|
||||
if test "x$SUFFIX" = "xjava"; then
|
||||
TMP=$(LC_ALL=C $DIFF $OTHER_FILE $THIS_FILE | \
|
||||
@ -92,7 +95,7 @@ diff_text() {
|
||||
-e '/\/\/ java GenerateCharacter.*/d')
|
||||
fi
|
||||
# Ignore date strings in class files.
|
||||
# On Macosx the system sources for generated java classes produce different output on
|
||||
# On Macosx the system sources for generated java classes produce different output on
|
||||
# consequtive invocations seemingly randomly.
|
||||
# For example a method parameter randomly named "thePoint" or "aPoint". Ignore this.
|
||||
# Anonymous lambda classes get randomly assigned counters in their names.
|
||||
@ -100,18 +103,18 @@ diff_text() {
|
||||
# To improve performance when large diffs are found, do a rough filtering of classes
|
||||
# elibeble for these exceptions
|
||||
if $GREP -R -e '[0-9]\{4\}_[0-9]\{2\}_[0-9]\{2\}_[0-9]\{2\}_[0-9]\{2\}-b[0-9]\{2\}' \
|
||||
-e '[0-9]\{2\}/[0-9]\{2\}/[0-9]\{4\}' \
|
||||
-e thePoint -e aPoint -e setItemsPtr \
|
||||
-e '[0-9]\{2\}/[0-9]\{2\}/[0-9]\{4\}' \
|
||||
-e thePoint -e aPoint -e setItemsPtr \
|
||||
-e 'lambda\$[a-zA-Z0-9]*\$[0-9]' ${THIS_FILE} > /dev/null; then
|
||||
$JAVAP -c -constants -l -p "${OTHER_FILE}" > ${OTHER_FILE}.javap
|
||||
$JAVAP -c -constants -l -p "${THIS_FILE}" > ${THIS_FILE}.javap
|
||||
TMP=$($DIFF ${OTHER_FILE}.javap ${THIS_FILE}.javap | \
|
||||
$GREP '^[<>]' | \
|
||||
$SED -e '/[<>].*[0-9]\{4\}_[0-9]\{2\}_[0-9]\{2\}_[0-9]\{2\}_[0-9]\{2\}-b[0-9]\{2\}.*/d' \
|
||||
-e '/[0-9]\{2\}\/[0-9]\{2\}\/[0-9]\{4\}/d' \
|
||||
-e '/[<>].*Point Lcom\/apple\/jobjc\/foundation\/NSPoint;/d' \
|
||||
-e '/[<>].*public com\.apple\.jobjc\.Pointer<com\.apple\.jobjc\..*itemsPtr();/d' \
|
||||
-e '/[<>].*public void setItemsPtr(com\.apple\.jobjc\.Pointer<com\.apple\.jobjc\..*);/d' \
|
||||
-e '/[0-9]\{2\}\/[0-9]\{2\}\/[0-9]\{4\}/d' \
|
||||
-e '/[<>].*Point Lcom\/apple\/jobjc\/foundation\/NSPoint;/d' \
|
||||
-e '/[<>].*public com\.apple\.jobjc\.Pointer<com\.apple\.jobjc\..*itemsPtr();/d' \
|
||||
-e '/[<>].*public void setItemsPtr(com\.apple\.jobjc\.Pointer<com\.apple\.jobjc\..*);/d' \
|
||||
-e '/[<>].*lambda\$[a-zA-Z0-9]*\$[0-9]*/d')
|
||||
fi
|
||||
fi
|
||||
@ -121,20 +124,19 @@ diff_text() {
|
||||
# Disable this exception since we aren't changing the properties cleaning method yet.
|
||||
# $CAT $OTHER_FILE | $SED -e 's/\([^\\]\):/\1\\:/g' -e 's/\([^\\]\)=/\1\\=/g' -e 's/#.*/#/g' \
|
||||
# | $SED -f "$SRC_ROOT/common/makefiles/support/unicode2x.sed" \
|
||||
# | $SED -e '/^#/d' -e '/^$/d' \
|
||||
# | $SED -e '/^#/d' -e '/^$/d' \
|
||||
# -e :a -e '/\\$/N; s/\\\n//; ta' \
|
||||
# -e 's/^[ \t]*//;s/[ \t]*$//' \
|
||||
# -e 's/\\=/=/' | LC_ALL=C $SORT > $OTHER_FILE.cleaned
|
||||
# -e 's/^[ \t]*//;s/[ \t]*$//' \
|
||||
# -e 's/\\=/=/' | LC_ALL=C $SORT > $OTHER_FILE.cleaned
|
||||
# Filter out date string differences.
|
||||
TMP=$(LC_ALL=C $DIFF $OTHER_FILE.cleaned $THIS_FILE | \
|
||||
$GREP '^[<>]' | \
|
||||
$SED -e '/[<>].*[0-9]\{4\}_[0-9]\{2\}_[0-9]\{2\}_[0-9]\{2\}_[0-9]\{2\}-b[0-9]\{2\}.*/d')
|
||||
fi
|
||||
if test "x$SUFFIX" = "xMF"; then
|
||||
# Filter out date string differences.
|
||||
if test "x$SUFFIX" = "xhtml"; then
|
||||
TMP=$(LC_ALL=C $DIFF $OTHER_FILE $THIS_FILE | \
|
||||
$GREP '^[<>]' | \
|
||||
$SED -e '/[<>].*[0-9]\{4\}_[0-9]\{2\}_[0-9]\{2\}_[0-9]\{2\}_[0-9]\{2\}-b[0-9]\{2\}.*/d')
|
||||
$SED -e '/[<>] <!-- Generated by javadoc .* on .* -->/d' )
|
||||
fi
|
||||
if test -n "$TMP"; then
|
||||
echo Files $OTHER_FILE and $THIS_FILE differ
|
||||
@ -158,7 +160,7 @@ compare_dirs() {
|
||||
(cd $THIS_DIR && $FIND . -type d | $SORT > $WORK_DIR/dirs_this)
|
||||
|
||||
$DIFF $WORK_DIR/dirs_other $WORK_DIR/dirs_this > $WORK_DIR/dirs_diff
|
||||
|
||||
|
||||
echo -n Directory structure...
|
||||
if [ -s $WORK_DIR/dirs_diff ]; then
|
||||
echo Differences found.
|
||||
@ -192,7 +194,7 @@ compare_files() {
|
||||
|
||||
(cd $OTHER_DIR && $FIND . ! -type d | $SORT > $WORK_DIR/files_other)
|
||||
(cd $THIS_DIR && $FIND . ! -type d | $SORT > $WORK_DIR/files_this)
|
||||
|
||||
|
||||
$DIFF $WORK_DIR/files_other $WORK_DIR/files_this > $WORK_DIR/files_diff
|
||||
|
||||
echo -n File names...
|
||||
@ -236,11 +238,11 @@ compare_permissions() {
|
||||
TP=`ls -l ${THIS_DIR}/$f | awk '{printf("%.10s\n", $1);}'`
|
||||
if [ "$OP" != "$TP" ]
|
||||
then
|
||||
if [ -z "$found" ]; then echo ; found="yes"; fi
|
||||
$PRINTF "\told: ${OP} new: ${TP}\t$f\n"
|
||||
if [ -z "$found" ]; then echo ; found="yes"; fi
|
||||
$PRINTF "\tother: ${OP} this: ${TP}\t$f\n"
|
||||
fi
|
||||
done
|
||||
if [ -z "$found" ]; then
|
||||
if [ -z "$found" ]; then
|
||||
echo "Identical!"
|
||||
else
|
||||
REGRESSIONS=true
|
||||
@ -265,24 +267,22 @@ compare_file_types() {
|
||||
if [ ! -f ${THIS_DIR}/$f ]; then continue; fi
|
||||
OF=`cd ${OTHER_DIR} && $FILE -h $f | $SED 's/BuildID[^,]*//g'`
|
||||
TF=`cd ${THIS_DIR} && $FILE -h $f | $SED 's/BuildID[^,]*//g'`
|
||||
if [ "$f" = "./src.zip" ] || [[ "$f" = *"/Home/src.zip" ]] || [[ "$f" = *"/lib/JObjC.jar" ]]
|
||||
then
|
||||
if [ "`echo $OF | $GREP -ic zip`" -gt 0 -a "`echo $TF | $GREP -ic zip`" -gt 0 ]
|
||||
then
|
||||
# the way we produces zip-files make it so that directories are stored in old file
|
||||
# but not in new (only files with full-path)
|
||||
# this makes file-5.09 report them as different
|
||||
continue;
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$OF" != "$TF" ]
|
||||
then
|
||||
if [ -z "$found" ]; then echo ; found="yes"; fi
|
||||
$PRINTF "\tother: ${OF}\n\tthis : ${TF}\n"
|
||||
if [ "`echo $OF | $GREP -c 'Zip archive data'`" -gt 0 ] \
|
||||
&& [ "`echo $TF | $GREP -c 'Zip archive data'`" -gt 0 ]
|
||||
then
|
||||
# the way we produce zip-files make it so that directories are stored in
|
||||
# old file but not in new (only files with full-path) this makes file
|
||||
# report them as different
|
||||
continue
|
||||
else
|
||||
if [ -z "$found" ]; then echo ; found="yes"; fi
|
||||
$PRINTF "\tother: ${OF}\n\tthis : ${TF}\n"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
if [ -z "$found" ]; then
|
||||
if [ -z "$found" ]; then
|
||||
echo "Identical!"
|
||||
else
|
||||
REGRESSIONS=true
|
||||
@ -296,12 +296,13 @@ compare_general_files() {
|
||||
THIS_DIR=$1
|
||||
OTHER_DIR=$2
|
||||
WORK_DIR=$3
|
||||
|
||||
|
||||
GENERAL_FILES=$(cd $THIS_DIR && $FIND . -type f ! -name "*.so" ! -name "*.jar" ! -name "*.zip" \
|
||||
! -name "*.debuginfo" ! -name "*.dylib" ! -name "jexec" ! -name "*.jimage" \
|
||||
! -name "ct.sym" ! -name "*.diz" ! -name "*.dll" \
|
||||
! -name "ct.sym" ! -name "*.diz" ! -name "*.dll" ! -name "*.cpl" \
|
||||
! -name "*.pdb" ! -name "*.exp" ! -name "*.ilk" \
|
||||
! -name "*.lib" ! -name "*.war" ! -name "JavaControlPanel" \
|
||||
! -name "*.obj" ! -name "*.o" ! -name "JavaControlPanelHelper" ! -name "JavaUpdater" \
|
||||
| $GREP -v "./bin/" | $SORT | $FILTER)
|
||||
|
||||
echo General files...
|
||||
@ -377,7 +378,7 @@ compare_zip_file() {
|
||||
THIS_SUFFIX="${THIS_ZIP##*.}"
|
||||
OTHER_SUFFIX="${OTHER_ZIP##*.}"
|
||||
if [ "$THIS_SUFFIX" != "$OTHER_SUFFIX" ]; then
|
||||
echo The files do not have the same suffix type!
|
||||
echo "The files do not have the same suffix type! ($THIS_SUFFIX != $OTHER_SUFFIX)"
|
||||
return 2
|
||||
fi
|
||||
|
||||
@ -389,7 +390,7 @@ compare_zip_file() {
|
||||
fi
|
||||
# Not quite identical, the might still contain the same data.
|
||||
# Unpack the jar/zip files in temp dirs
|
||||
|
||||
|
||||
THIS_UNZIPDIR=$WORK_DIR/$ZIP_FILE.this
|
||||
OTHER_UNZIPDIR=$WORK_DIR/$ZIP_FILE.other
|
||||
$RM -rf $THIS_UNZIPDIR $OTHER_UNZIPDIR
|
||||
@ -464,9 +465,9 @@ compare_zip_file() {
|
||||
|
||||
$RM -f $WORK_DIR/$ZIP_FILE.diffs
|
||||
for file in $DIFFING_FILES; do
|
||||
if [[ "$ACCEPTED_JARZIP_CONTENTS $EXCEPTIONS" != *"$file"* ]]; then
|
||||
if [[ "$ACCEPTED_JARZIP_CONTENTS $EXCEPTIONS" != *"$file"* ]]; then
|
||||
diff_text $OTHER_UNZIPDIR/$file $THIS_UNZIPDIR/$file >> $WORK_DIR/$ZIP_FILE.diffs
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -s "$WORK_DIR/$ZIP_FILE.diffs" ]; then
|
||||
@ -573,6 +574,10 @@ compare_bin_file() {
|
||||
|
||||
$MKDIR -p $FILE_WORK_DIR
|
||||
|
||||
# Make soft links to original files from work dir to facilitate debugging
|
||||
$LN -f -s $THIS_FILE $WORK_FILE_BASE.this
|
||||
$LN -f -s $OTHER_FILE $WORK_FILE_BASE.other
|
||||
|
||||
ORIG_THIS_FILE="$THIS_FILE"
|
||||
ORIG_OTHER_FILE="$OTHER_FILE"
|
||||
|
||||
@ -589,50 +594,51 @@ compare_bin_file() {
|
||||
fi
|
||||
|
||||
if [ "$OPENJDK_TARGET_OS" = "windows" ]; then
|
||||
unset _NT_SYMBOL_PATH
|
||||
# On windows we need to unzip the debug symbols, if present
|
||||
OTHER_FILE_BASE=${OTHER_FILE/.dll/}
|
||||
OTHER_FILE_BASE=${OTHER_FILE_BASE/.exe/}
|
||||
DIZ_NAME=$(basename $OTHER_FILE_BASE).diz
|
||||
unset _NT_SYMBOL_PATH
|
||||
# On windows we need to unzip the debug symbols, if present
|
||||
OTHER_FILE_BASE=${OTHER_FILE/.dll/}
|
||||
OTHER_FILE_BASE=${OTHER_FILE_BASE/.exe/}
|
||||
OTHER_FILE_BASE=${OTHER_FILE_BASE/.cpl/}
|
||||
DIZ_NAME=$(basename $OTHER_FILE_BASE).diz
|
||||
# java.exe and java.dll diz files will have the same name. Have to
|
||||
# make sure java.exe gets the right one. This is only needed for
|
||||
# OTHER since in the new build, all pdb files are left around.
|
||||
if [ "$NAME" = "java.exe" ] && [ -f "$OTHER/tmp/java/java/obj64/java.diz" ]; then
|
||||
OTHER_DIZ_FILE="$OTHER/tmp/java/java/obj64/java.diz"
|
||||
elif [ -f "${OTHER_FILE_BASE}.diz" ]; then
|
||||
OTHER_DIZ_FILE=${OTHER_FILE_BASE}.diz
|
||||
else
|
||||
# make sure java.exe gets the right one. This is only needed for
|
||||
# OTHER since in the new build, all pdb files are left around.
|
||||
if [ "$NAME" = "java.exe" ] && [ -f "$OTHER/tmp/java/java/obj64/java.diz" ]; then
|
||||
OTHER_DIZ_FILE="$OTHER/tmp/java/java/obj64/java.diz"
|
||||
elif [ -f "${OTHER_FILE_BASE}.diz" ]; then
|
||||
OTHER_DIZ_FILE=${OTHER_FILE_BASE}.diz
|
||||
else
|
||||
# Some files, jli.dll, appears twice in the image but only one of
|
||||
# thme has a diz file next to it.
|
||||
OTHER_DIZ_FILE="$($FIND $OTHER_DIR -name $DIZ_NAME | $SED 1q)"
|
||||
if [ ! -f "$OTHER_DIZ_FILE" ]; then
|
||||
# As a last resort, look for diz file in the whole build output
|
||||
# dir.
|
||||
OTHER_DIZ_FILE="$($FIND $OTHER -name $DIZ_NAME | $SED 1q)"
|
||||
fi
|
||||
fi
|
||||
if [ -n "$OTHER_DIZ_FILE" ]; then
|
||||
$MKDIR -p $FILE_WORK_DIR/other
|
||||
(cd $FILE_WORK_DIR/other ; $UNARCHIVE -o $OTHER_DIZ_FILE)
|
||||
export _NT_SYMBOL_PATH="$FILE_WORK_DIR/other"
|
||||
fi
|
||||
THIS_FILE_BASE=${THIS_FILE/.dll/}
|
||||
THIS_FILE_BASE=${THIS_FILE_BASE/.exe/}
|
||||
if [ -f "${THIS_FILE/.dll/}.diz" ]; then
|
||||
THIS_DIZ_FILE=${THIS_FILE/.dll/}.diz
|
||||
else
|
||||
THIS_DIZ_FILE="$($FIND $THIS_DIR -name $DIZ_NAME | $SED 1q)"
|
||||
if [ ! -f "$THIS_DIZ_FILE" ]; then
|
||||
# As a last resort, look for diz file in the whole build output
|
||||
# dir.
|
||||
THIS_DIZ_FILE="$($FIND $THIS -name $DIZ_NAME | $SED 1q)"
|
||||
fi
|
||||
fi
|
||||
if [ -n "$THIS_DIZ_FILE" ]; then
|
||||
$MKDIR -p $FILE_WORK_DIR/this
|
||||
(cd $FILE_WORK_DIR/this ; $UNARCHIVE -o $THIS_DIZ_FILE)
|
||||
export _NT_SYMBOL_PATH="$_NT_SYMBOL_PATH;$FILE_WORK_DIR/this"
|
||||
fi
|
||||
# thme has a diz file next to it.
|
||||
OTHER_DIZ_FILE="$($FIND $OTHER_DIR -name $DIZ_NAME | $SED 1q)"
|
||||
if [ ! -f "$OTHER_DIZ_FILE" ]; then
|
||||
# As a last resort, look for diz file in the whole build output
|
||||
# dir.
|
||||
OTHER_DIZ_FILE="$($FIND $OTHER -name $DIZ_NAME | $SED 1q)"
|
||||
fi
|
||||
fi
|
||||
if [ -n "$OTHER_DIZ_FILE" ]; then
|
||||
$MKDIR -p $FILE_WORK_DIR/other
|
||||
(cd $FILE_WORK_DIR/other ; $UNARCHIVE -o $OTHER_DIZ_FILE)
|
||||
export _NT_SYMBOL_PATH="$FILE_WORK_DIR/other"
|
||||
fi
|
||||
THIS_FILE_BASE=${THIS_FILE/.dll/}
|
||||
THIS_FILE_BASE=${THIS_FILE_BASE/.exe/}
|
||||
if [ -f "${THIS_FILE/.dll/}.diz" ]; then
|
||||
THIS_DIZ_FILE=${THIS_FILE/.dll/}.diz
|
||||
else
|
||||
THIS_DIZ_FILE="$($FIND $THIS_DIR -name $DIZ_NAME | $SED 1q)"
|
||||
if [ ! -f "$THIS_DIZ_FILE" ]; then
|
||||
# As a last resort, look for diz file in the whole build output
|
||||
# dir.
|
||||
THIS_DIZ_FILE="$($FIND $THIS -name $DIZ_NAME | $SED 1q)"
|
||||
fi
|
||||
fi
|
||||
if [ -n "$THIS_DIZ_FILE" ]; then
|
||||
$MKDIR -p $FILE_WORK_DIR/this
|
||||
(cd $FILE_WORK_DIR/this ; $UNARCHIVE -o $THIS_DIZ_FILE)
|
||||
export _NT_SYMBOL_PATH="$_NT_SYMBOL_PATH;$FILE_WORK_DIR/this"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$SKIP_BIN_DIFF" ]; then
|
||||
@ -670,19 +676,19 @@ compare_bin_file() {
|
||||
DIFF_SIZE_REL=$($EXPR $THIS_SIZE \* 100 / $OTHER_SIZE)
|
||||
SIZE_MSG=$($PRINTF "%3d%% %4d" $DIFF_SIZE_REL $DIFF_SIZE_NUM)
|
||||
if [[ "$ACCEPTED_SMALL_SIZE_DIFF" = *"$BIN_FILE"* ]] && [ "$DIFF_SIZE_REL" -gt 98 ] \
|
||||
&& [ "$DIFF_SIZE_REL" -lt 102 ]; then
|
||||
&& [ "$DIFF_SIZE_REL" -lt 102 ]; then
|
||||
SIZE_MSG="($SIZE_MSG)"
|
||||
DIFF_SIZE=
|
||||
elif [ "$OPENJDK_TARGET_OS" = "windows" ] \
|
||||
&& [[ "$ACCEPTED_SMALL_SIZE_DIFF" = *"$BIN_FILE"* ]] \
|
||||
&& [ "$DIFF_SIZE_NUM" = 512 ]; then
|
||||
# On windows, size of binaries increase in 512 increments.
|
||||
&& [[ "$ACCEPTED_SMALL_SIZE_DIFF" = *"$BIN_FILE"* ]] \
|
||||
&& [ "$DIFF_SIZE_NUM" = 512 ]; then
|
||||
# On windows, size of binaries increase in 512 increments.
|
||||
SIZE_MSG="($SIZE_MSG)"
|
||||
DIFF_SIZE=
|
||||
elif [ "$OPENJDK_TARGET_OS" = "windows" ] \
|
||||
&& [[ "$ACCEPTED_SMALL_SIZE_DIFF" = *"$BIN_FILE"* ]] \
|
||||
&& [ "$DIFF_SIZE_NUM" = -512 ]; then
|
||||
# On windows, size of binaries increase in 512 increments.
|
||||
&& [[ "$ACCEPTED_SMALL_SIZE_DIFF" = *"$BIN_FILE"* ]] \
|
||||
&& [ "$DIFF_SIZE_NUM" = -512 ]; then
|
||||
# On windows, size of binaries increase in 512 increments.
|
||||
SIZE_MSG="($SIZE_MSG)"
|
||||
DIFF_SIZE=
|
||||
else
|
||||
@ -717,18 +723,18 @@ compare_bin_file() {
|
||||
if [ "$OPENJDK_TARGET_OS" = "windows" ]; then
|
||||
# The output from dumpbin on windows differs depending on if the debug symbol
|
||||
# files are still around at the location the binary is pointing too. Need
|
||||
# to filter out that extra information.
|
||||
$DUMPBIN -exports $OTHER_FILE | $GREP -E '^ +[0-9A-F]+ +[0-9A-F]+ [0-9A-F]+' | sed 's/ = .*//g' | cut -c27- | $SYM_SORT_CMD > $WORK_FILE_BASE.symbols.other
|
||||
$DUMPBIN -exports $THIS_FILE | $GREP -E '^ +[0-9A-F]+ +[0-9A-F]+ [0-9A-F]+' | sed 's/ = .*//g' | cut -c27- | $SYM_SORT_CMD > $WORK_FILE_BASE.symbols.this
|
||||
# to filter out that extra information.
|
||||
$DUMPBIN -exports $OTHER_FILE | $GREP -E '^ +[0-9A-F]+ +[0-9A-F]+ [0-9A-F]+' | sed 's/ = .*//g' | cut -c27- | $SYM_SORT_CMD > $WORK_FILE_BASE.symbols.other
|
||||
$DUMPBIN -exports $THIS_FILE | $GREP -E '^ +[0-9A-F]+ +[0-9A-F]+ [0-9A-F]+' | sed 's/ = .*//g' | cut -c27- | $SYM_SORT_CMD > $WORK_FILE_BASE.symbols.this
|
||||
elif [ "$OPENJDK_TARGET_OS" = "solaris" ]; then
|
||||
# Some symbols get seemingly random 15 character prefixes. Filter them out.
|
||||
$NM -a $ORIG_OTHER_FILE 2> /dev/null | $GREP -v $NAME | $AWK '{print $2, $3, $4, $5}' | $SED 's/^\([a-zA-Z] [\.\$]\)[a-zA-Z0-9_\$]\{15,15\}\./\1./g' | $SYM_SORT_CMD > $WORK_FILE_BASE.symbols.other
|
||||
$NM -a $ORIG_THIS_FILE 2> /dev/null | $GREP -v $NAME | $AWK '{print $2, $3, $4, $5}' | $SED 's/^\([a-zA-Z] [\.\$]\)[a-zA-Z0-9_\$]\{15,15\}\./\1./g' | $SYM_SORT_CMD > $WORK_FILE_BASE.symbols.this
|
||||
$NM -a $ORIG_THIS_FILE 2> /dev/null | $GREP -v $NAME | $AWK '{print $2, $3, $4, $5}' | $SED 's/^\([a-zA-Z] [\.\$]\)[a-zA-Z0-9_\$]\{15,15\}\./\1./g' | $SYM_SORT_CMD > $WORK_FILE_BASE.symbols.this
|
||||
else
|
||||
$NM -a $ORIG_OTHER_FILE 2> /dev/null | $GREP -v $NAME | $AWK '{print $2, $3, $4, $5}' | $SYM_SORT_CMD > $WORK_FILE_BASE.symbols.other
|
||||
$NM -a $ORIG_THIS_FILE 2> /dev/null | $GREP -v $NAME | $AWK '{print $2, $3, $4, $5}' | $SYM_SORT_CMD > $WORK_FILE_BASE.symbols.this
|
||||
$NM -a $ORIG_OTHER_FILE 2> /dev/null | $GREP -v $NAME | $AWK '{print $2, $3, $4, $5}' | $SYM_SORT_CMD > $WORK_FILE_BASE.symbols.other
|
||||
$NM -a $ORIG_THIS_FILE 2> /dev/null | $GREP -v $NAME | $AWK '{print $2, $3, $4, $5}' | $SYM_SORT_CMD > $WORK_FILE_BASE.symbols.this
|
||||
fi
|
||||
|
||||
|
||||
LC_ALL=C $DIFF $WORK_FILE_BASE.symbols.other $WORK_FILE_BASE.symbols.this > $WORK_FILE_BASE.symbols.diff
|
||||
if [ -s $WORK_FILE_BASE.symbols.diff ]; then
|
||||
SYM_MSG=" diff "
|
||||
@ -741,7 +747,7 @@ compare_bin_file() {
|
||||
SYM_MSG=" $SYM_MSG "
|
||||
fi
|
||||
else
|
||||
SYM_MSG="($SYM_MSG)"
|
||||
SYM_MSG="($SYM_MSG)"
|
||||
DIFF_SYM=
|
||||
fi
|
||||
else
|
||||
@ -754,48 +760,48 @@ compare_bin_file() {
|
||||
|
||||
# Check dependencies
|
||||
if [ -n "$LDD_CMD" ]; then
|
||||
(cd $FILE_WORK_DIR && $CP $OTHER_FILE . && $LDD_CMD $NAME 2>/dev/null | $AWK '{ print $1;}' | $SORT | $TEE $WORK_FILE_BASE.deps.other | $UNIQ > $WORK_FILE_BASE.deps.other.uniq)
|
||||
(cd $FILE_WORK_DIR && $CP $THIS_FILE . && $LDD_CMD $NAME 2</dev/null | $AWK '{ print $1;}' | $SORT | $TEE $WORK_FILE_BASE.deps.this | $UNIQ > $WORK_FILE_BASE.deps.this.uniq)
|
||||
(cd $FILE_WORK_DIR && $RM -f $NAME)
|
||||
|
||||
LC_ALL=C $DIFF $WORK_FILE_BASE.deps.other $WORK_FILE_BASE.deps.this > $WORK_FILE_BASE.deps.diff
|
||||
LC_ALL=C $DIFF $WORK_FILE_BASE.deps.other.uniq $WORK_FILE_BASE.deps.this.uniq > $WORK_FILE_BASE.deps.diff.uniq
|
||||
|
||||
if [ -s $WORK_FILE_BASE.deps.diff ]; then
|
||||
(cd $FILE_WORK_DIR && $CP $OTHER_FILE . && $LDD_CMD $NAME 2>/dev/null | $AWK '{ print $1;}' | $SORT | $TEE $WORK_FILE_BASE.deps.other | $UNIQ > $WORK_FILE_BASE.deps.other.uniq)
|
||||
(cd $FILE_WORK_DIR && $CP $THIS_FILE . && $LDD_CMD $NAME 2</dev/null | $AWK '{ print $1;}' | $SORT | $TEE $WORK_FILE_BASE.deps.this | $UNIQ > $WORK_FILE_BASE.deps.this.uniq)
|
||||
(cd $FILE_WORK_DIR && $RM -f $NAME)
|
||||
|
||||
LC_ALL=C $DIFF $WORK_FILE_BASE.deps.other $WORK_FILE_BASE.deps.this > $WORK_FILE_BASE.deps.diff
|
||||
LC_ALL=C $DIFF $WORK_FILE_BASE.deps.other.uniq $WORK_FILE_BASE.deps.this.uniq > $WORK_FILE_BASE.deps.diff.uniq
|
||||
|
||||
if [ -s $WORK_FILE_BASE.deps.diff ]; then
|
||||
if [ -s $WORK_FILE_BASE.deps.diff.uniq ]; then
|
||||
DEP_MSG=" diff "
|
||||
DEP_MSG=" diff "
|
||||
else
|
||||
DEP_MSG=" redun "
|
||||
DEP_MSG=" redun "
|
||||
fi
|
||||
if [[ "$ACCEPTED_DEP_DIFF" != *"$BIN_FILE"* ]]; then
|
||||
DIFF_DEP=true
|
||||
if [[ "$KNOWN_DEP_DIFF" != *"$BIN_FILE"* ]]; then
|
||||
DIFF_DEP=true
|
||||
if [[ "$KNOWN_DEP_DIFF" != *"$BIN_FILE"* ]]; then
|
||||
DEP_MSG="*$DEP_MSG*"
|
||||
REGRESSIONS=true
|
||||
else
|
||||
else
|
||||
DEP_MSG=" $DEP_MSG "
|
||||
fi
|
||||
fi
|
||||
else
|
||||
DEP_MSG="($DEP_MSG)"
|
||||
DIFF_DEP=
|
||||
DEP_MSG="($DEP_MSG)"
|
||||
DIFF_DEP=
|
||||
fi
|
||||
else
|
||||
DEP_MSG=" "
|
||||
DIFF_DEP=
|
||||
else
|
||||
DEP_MSG=" "
|
||||
DIFF_DEP=
|
||||
if [[ "$KNOWN_DEP_DIFF $ACCEPTED_DEP_DIFF" = *"$BIN_FILE"* ]]; then
|
||||
DEP_MSG=" ! "
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
else
|
||||
DEP_MSG=" - "
|
||||
DEP_MSG=" - "
|
||||
fi
|
||||
|
||||
|
||||
# Compare fulldump output
|
||||
if [ -n "$FULLDUMP_CMD" ] && [ -z "$SKIP_FULLDUMP_DIFF" ]; then
|
||||
$FULLDUMP_CMD $OTHER_FILE > $WORK_FILE_BASE.fulldump.other 2>&1
|
||||
$FULLDUMP_CMD $THIS_FILE > $WORK_FILE_BASE.fulldump.this 2>&1
|
||||
LC_ALL=C $DIFF $WORK_FILE_BASE.fulldump.other $WORK_FILE_BASE.fulldump.this > $WORK_FILE_BASE.fulldump.diff
|
||||
|
||||
|
||||
if [ -s $WORK_FILE_BASE.fulldump.diff ]; then
|
||||
ELF_DIFF_SIZE=$(ls -n $WORK_FILE_BASE.fulldump.diff | awk '{print $5}')
|
||||
ELF_MSG=$($PRINTF "%8d" $ELF_DIFF_SIZE)
|
||||
@ -822,14 +828,17 @@ compare_bin_file() {
|
||||
|
||||
# Compare disassemble output
|
||||
if [ -n "$DIS_CMD" ] && [ -z "$SKIP_DIS_DIFF" ]; then
|
||||
if [ -z "$DIS_DIFF_FILTER" ]; then
|
||||
DIS_DIFF_FILTER="$CAT"
|
||||
fi
|
||||
$DIS_CMD $OTHER_FILE | $GREP -v $NAME | $DIS_DIFF_FILTER > $WORK_FILE_BASE.dis.other 2>&1
|
||||
$DIS_CMD $THIS_FILE | $GREP -v $NAME | $DIS_DIFF_FILTER > $WORK_FILE_BASE.dis.this 2>&1
|
||||
|
||||
# By default we filter out differences that include references to symbols.
|
||||
# To get a raw diff with the complete disassembly, set
|
||||
# DIS_DIFF_FILTER="$CAT"
|
||||
if [ -z "$DIS_DIFF_FILTER" ]; then
|
||||
DIS_DIFF_FILTER="$GREP -v ' # .* <.*>$'"
|
||||
fi
|
||||
$DIS_CMD $OTHER_FILE | $GREP -v $NAME | eval "$DIS_DIFF_FILTER" > $WORK_FILE_BASE.dis.other 2>&1
|
||||
$DIS_CMD $THIS_FILE | $GREP -v $NAME | eval "$DIS_DIFF_FILTER" > $WORK_FILE_BASE.dis.this 2>&1
|
||||
|
||||
LC_ALL=C $DIFF $WORK_FILE_BASE.dis.other $WORK_FILE_BASE.dis.this > $WORK_FILE_BASE.dis.diff
|
||||
|
||||
|
||||
if [ -s $WORK_FILE_BASE.dis.diff ]; then
|
||||
DIS_DIFF_SIZE=$(ls -n $WORK_FILE_BASE.dis.diff | awk '{print $5}')
|
||||
DIS_MSG=$($PRINTF "%8d" $DIS_DIFF_SIZE)
|
||||
@ -907,7 +916,9 @@ compare_all_libs() {
|
||||
OTHER_DIR=$2
|
||||
WORK_DIR=$3
|
||||
|
||||
LIBS=$(cd $THIS_DIR && $FIND . -type f \( -name 'lib*.so' -o -name '*.dylib' -o -name '*.dll' -o -name 'JavaControlPanel' \) | $SORT | $FILTER)
|
||||
LIBS=$(cd $THIS_DIR && $FIND . -type f \( -name 'lib*.so' -o -name '*.dylib' \
|
||||
-o -name '*.dll' -o -name '*.obj' -o -name '*.o' \
|
||||
-o -name '*.cpl' \) | $SORT | $FILTER)
|
||||
|
||||
if [ -n "$LIBS" ]; then
|
||||
echo Libraries...
|
||||
@ -967,7 +978,7 @@ COMPARE_ROOT=/tmp/cimages.$USER
|
||||
$MKDIR -p $COMPARE_ROOT
|
||||
if [ "$OPENJDK_TARGET_OS" = "windows" ]; then
|
||||
if [ "$(uname -o)" = "Cygwin" ]; then
|
||||
COMPARE_ROOT=$(cygpath -msa $COMPARE_ROOT)
|
||||
COMPARE_ROOT=$(cygpath -msa $COMPARE_ROOT)
|
||||
fi
|
||||
fi
|
||||
|
||||
@ -1091,7 +1102,7 @@ while [ -n "$1" ]; do
|
||||
CMP_JARS=true
|
||||
CMP_LIBS=true
|
||||
CMP_EXECS=true
|
||||
|
||||
|
||||
if [ -z "$FILTER" ]; then
|
||||
FILTER="$GREP"
|
||||
fi
|
||||
@ -1177,8 +1188,8 @@ if [ "$SKIP_DEFAULT" != "true" ]; then
|
||||
OTHER_J2RE="$OTHER/images/jre"
|
||||
echo "Selecting jdk images for compare"
|
||||
else
|
||||
echo "No common images found."
|
||||
exit 1
|
||||
echo "No common images found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -d "$THIS/images/jdk-bundle" ] && [ -d "$OTHER/images/jdk-bundle" ]; then
|
||||
@ -1189,6 +1200,17 @@ if [ "$SKIP_DEFAULT" != "true" ]; then
|
||||
echo "Also comparing macosx bundles"
|
||||
fi
|
||||
|
||||
if [ -d "$THIS/deploy" ] && [ -d "$OTHER/deploy" ]; then
|
||||
THIS_DEPLOY_BUNDLE_DIR="$THIS/deploy/dist/installer/bundles"
|
||||
OTHER_DEPLOY_BUNDLE_DIR="$OTHER/deploy/bundles"
|
||||
echo "Also comparing deploy/bundles"
|
||||
if [ "$OPENJDK_TARGET_OS" = "macosx" ]; then
|
||||
THIS_DEPLOY_APPLET_PLUGIN_DIR="$THIS/deploy/JavaAppletPlugin.plugin"
|
||||
OTHER_DEPLOY_APPLET_PLUGIN_DIR="$OTHER/deploy/JavaAppletPlugin.plugin"
|
||||
echo "Also comparing JavaAppletPlugin"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -d "$OTHER/images" ]; then
|
||||
OTHER_SEC_DIR="$OTHER/images"
|
||||
else
|
||||
@ -1212,7 +1234,7 @@ if [ "$SKIP_DEFAULT" != "true" ]; then
|
||||
if [ -d "$THIS/docs" ] && [ -d "$OTHER/docs" ]; then
|
||||
THIS_DOCS="$THIS/docs"
|
||||
OTHER_DOCS="$OTHER/docs"
|
||||
echo "Also comparing docs"
|
||||
echo "Also comparing docs"
|
||||
else
|
||||
echo "WARNING! Docs haven't been built and won't be compared."
|
||||
fi
|
||||
@ -1227,7 +1249,7 @@ if [ "$CMP_NAMES" = "true" ]; then
|
||||
compare_dirs $THIS_J2SDK $OTHER_J2SDK $COMPARE_ROOT/j2sdk
|
||||
echo -n "J2RE "
|
||||
compare_dirs $THIS_J2RE $OTHER_J2RE $COMPARE_ROOT/j2re
|
||||
|
||||
|
||||
echo -n "J2SDK "
|
||||
compare_files $THIS_J2SDK $OTHER_J2SDK $COMPARE_ROOT/j2sdk
|
||||
echo -n "J2RE "
|
||||
@ -1238,7 +1260,7 @@ if [ "$CMP_NAMES" = "true" ]; then
|
||||
compare_dirs $THIS_J2SDK_BUNDLE $OTHER_J2SDK_BUNDLE $COMPARE_ROOT/jdk-bundle
|
||||
echo -n "J2RE Bundle "
|
||||
compare_dirs $THIS_J2RE_BUNDLE $OTHER_J2RE_BUNDLE $COMPARE_ROOT/jre-bundle
|
||||
|
||||
|
||||
echo -n "J2SDK Bundle "
|
||||
compare_files $THIS_J2SDK_BUNDLE $OTHER_J2SDK_BUNDLE $COMPARE_ROOT/jdk-bundle
|
||||
echo -n "J2RE Bundle "
|
||||
@ -1254,6 +1276,12 @@ if [ "$CMP_NAMES" = "true" ]; then
|
||||
compare_dirs $THIS_BASE_DIR $OTHER_BASE_DIR $COMPARE_ROOT/base_dir
|
||||
compare_files $THIS_BASE_DIR $OTHER_BASE_DIR $COMPARE_ROOT/base_dir
|
||||
fi
|
||||
if [ -n "$THIS_DEPLOY_APPLET_PLUGIN_DIR" ] && [ -n "$OTHER_DEPLOY_APPLET_PLUGIN_DIR" ]; then
|
||||
echo -n "JavaAppletPlugin "
|
||||
compare_dirs $THIS_DEPLOY_APPLET_PLUGIN_DIR $OTHER_DEPLOY_APPLET_PLUGIN_DIR $COMPARE_ROOT/plugin
|
||||
echo -n "JavaAppletPlugin "
|
||||
compare_files $THIS_DEPLOY_APPLET_PLUGIN_DIR $OTHER_DEPLOY_APPLET_PLUGIN_DIR $COMPARE_ROOT/plugin
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$CMP_PERMS" = "true" ]; then
|
||||
@ -1266,6 +1294,10 @@ if [ "$CMP_PERMS" = "true" ]; then
|
||||
if [ -n "$THIS_BASE_DIR" ] && [ -n "$OTHER_BASE_DIR" ]; then
|
||||
compare_permissions $THIS_BASE_DIR $OTHER_BASE_DIR $COMPARE_ROOT/base_dir
|
||||
fi
|
||||
if [ -n "$THIS_DEPLOY_APPLET_PLUGIN_DIR" ] && [ -n "$OTHER_DEPLOY_APPLET_PLUGIN_DIR" ]; then
|
||||
echo -n "JavaAppletPlugin "
|
||||
compare_permissions $THIS_DEPLOY_APPLET_PLUGIN_DIR $OTHER_DEPLOY_APPLET_PLUGIN_DIR $COMPARE_ROOT/plugin
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$CMP_TYPES" = "true" ]; then
|
||||
@ -1284,6 +1316,10 @@ if [ "$CMP_TYPES" = "true" ]; then
|
||||
if [ -n "$THIS_BASE_DIR" ] && [ -n "$OTHER_BASE_DIR" ]; then
|
||||
compare_file_types $THIS_BASE_DIR $OTHER_BASE_DIR $COMPARE_ROOT/base_dir
|
||||
fi
|
||||
if [ -n "$THIS_DEPLOY_APPLET_PLUGIN_DIR" ] && [ -n "$OTHER_DEPLOY_APPLET_PLUGIN_DIR" ]; then
|
||||
echo -n "JavaAppletPlugin "
|
||||
compare_file_types $THIS_DEPLOY_APPLET_PLUGIN_DIR $OTHER_DEPLOY_APPLET_PLUGIN_DIR $COMPARE_ROOT/plugin
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$CMP_GENERAL" = "true" ]; then
|
||||
@ -1306,6 +1342,10 @@ if [ "$CMP_GENERAL" = "true" ]; then
|
||||
if [ -n "$THIS_BASE_DIR" ] && [ -n "$OTHER_BASE_DIR" ]; then
|
||||
compare_general_files $THIS_BASE_DIR $OTHER_BASE_DIR $COMPARE_ROOT/base_dir
|
||||
fi
|
||||
if [ -n "$THIS_DEPLOY_APPLET_PLUGIN_DIR" ] && [ -n "$OTHER_DEPLOY_APPLET_PLUGIN_DIR" ]; then
|
||||
echo -n "JavaAppletPlugin "
|
||||
compare_general_files $THIS_DEPLOY_APPLET_PLUGIN_DIR $OTHER_DEPLOY_APPLET_PLUGIN_DIR $COMPARE_ROOT/plugin
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$CMP_ZIPS" = "true" ]; then
|
||||
@ -1333,6 +1373,12 @@ if [ "$CMP_ZIPS" = "true" ]; then
|
||||
if [ -n "$THIS_BASE_DIR" ] && [ -n "$OTHER_BASE_DIR" ]; then
|
||||
compare_all_zip_files $THIS_BASE_DIR $OTHER_BASE_DIR $COMPARE_ROOT/base_dir
|
||||
fi
|
||||
if [ -n "$THIS_DEPLOY_BUNDLE_DIR" ] && [ -n "$OTHER_DEPLOY_BUNDLE_DIR" ]; then
|
||||
compare_all_zip_files $THIS_DEPLOY_BUNDLE_DIR $OTHER_DEPLOY_BUNDLE_DIR $COMPARE_ROOT/deploy-bundle
|
||||
fi
|
||||
if [ -n "$THIS_DEPLOY_APPLET_PLUGIN_DIR" ] && [ -n "$OTHER_DEPLOY_APPLET_PLUGIN_DIR" ]; then
|
||||
compare_all_zip_files $THIS_DEPLOY_APPLET_PLUGIN_DIR $OTHER_DEPLOY_APPLET_PLUGIN_DIR $COMPARE_ROOT/plugin
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$CMP_JARS" = "true" ]; then
|
||||
@ -1342,6 +1388,9 @@ if [ "$CMP_JARS" = "true" ]; then
|
||||
if [ -n "$THIS_BASE_DIR" ] && [ -n "$OTHER_BASE_DIR" ]; then
|
||||
compare_all_jar_files $THIS_BASE_DIR $OTHER_BASE_DIR $COMPARE_ROOT/base_dir
|
||||
fi
|
||||
if [ -n "$THIS_DEPLOY_APPLET_PLUGIN_DIR" ] && [ -n "$OTHER_DEPLOY_APPLET_PLUGIN_DIR" ]; then
|
||||
compare_all_jar_files $THIS_DEPLOY_APPLET_PLUGIN_DIR $OTHER_DEPLOY_APPLET_PLUGIN_DIR $COMPARE_ROOT/plugin
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$CMP_LIBS" = "true" ]; then
|
||||
@ -1356,15 +1405,27 @@ if [ "$CMP_LIBS" = "true" ]; then
|
||||
if [ -n "$THIS_BASE_DIR" ] && [ -n "$OTHER_BASE_DIR" ]; then
|
||||
compare_all_libs $THIS_BASE_DIR $OTHER_BASE_DIR $COMPARE_ROOT/base_dir
|
||||
fi
|
||||
if [ -n "$THIS_DEPLOY_APPLET_PLUGIN_DIR" ] && [ -n "$OTHER_DEPLOY_APPLET_PLUGIN_DIR" ]; then
|
||||
echo -n "JavaAppletPlugin "
|
||||
compare_all_libs $THIS_DEPLOY_APPLET_PLUGIN_DIR $OTHER_DEPLOY_APPLET_PLUGIN_DIR $COMPARE_ROOT/plugin
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$CMP_EXECS" = "true" ]; then
|
||||
if [ -n "$THIS_J2SDK" ] && [ -n "$OTHER_J2SDK" ]; then
|
||||
compare_all_execs $THIS_J2SDK $OTHER_J2SDK $COMPARE_ROOT/j2sdk
|
||||
if [ "$OPENJDK_TARGET_OS" = "macosx" ]; then
|
||||
echo -n "J2RE "
|
||||
compare_all_execs $THIS_J2RE $OTHER_J2RE $COMPARE_ROOT/j2re
|
||||
fi
|
||||
fi
|
||||
if [ -n "$THIS_BASE_DIR" ] && [ -n "$OTHER_BASE_DIR" ]; then
|
||||
compare_all_execs $THIS_BASE_DIR $OTHER_BASE_DIR $COMPARE_ROOT/base_dir
|
||||
fi
|
||||
if [ -n "$THIS_DEPLOY_APPLET_PLUGIN_DIR" ] && [ -n "$OTHER_DEPLOY_APPLET_PLUGIN_DIR" ]; then
|
||||
echo -n "JavaAppletPlugin "
|
||||
compare_all_execs $THIS_DEPLOY_APPLET_PLUGIN_DIR $OTHER_DEPLOY_APPLET_PLUGIN_DIR $COMPARE_ROOT/plugin
|
||||
fi
|
||||
fi
|
||||
|
||||
echo
|
||||
|
||||
@ -288,3 +288,5 @@ e27c725d6c9d155667b35255f442d4ceb8c3c084 jdk9-b40
|
||||
9645e35616b60c5c07b4fdf11a132afc8081dfa8 jdk9-b43
|
||||
1f57bd728c9e6865ccb9d43ccd80a1c11230a32f jdk9-b44
|
||||
9e3f2bed80c0e5a84a256ce41f1d10c5ade48466 jdk9-b45
|
||||
326f2068b4a4c05e2fa27d6acf93eba7b54b090d jdk9-b46
|
||||
ee8447ca632e1d39180b4767c749db101bff7314 jdk9-b47
|
||||
|
||||
@ -448,3 +448,5 @@ c363a8b87e477ee45d6d3cb2a36cb365141bc596 jdk9-b38
|
||||
65a9747147b8090037541040ba67156ec914db6a jdk9-b43
|
||||
43a44b56dca61a4d766a20f0528fdd8b5ceff873 jdk9-b44
|
||||
5dc8184af1e2bb30b0103113d1f1a58a21a80c37 jdk9-b45
|
||||
a184ee1d717297bd35b7c3e35393e137921a3ed2 jdk9-b46
|
||||
3b241fb72b8925b75941d612db762a6d5da66d02 jdk9-b47
|
||||
|
||||
@ -58,15 +58,19 @@ sun.jvm.hotspot.debugger.cdbg.basic.x86 \
|
||||
sun.jvm.hotspot.debugger.dummy \
|
||||
sun.jvm.hotspot.debugger.linux \
|
||||
sun.jvm.hotspot.debugger.linux.amd64 \
|
||||
sun.jvm.hotspot.debugger.linux.ppc64 \
|
||||
sun.jvm.hotspot.debugger.linux.x86 \
|
||||
sun.jvm.hotspot.debugger.posix \
|
||||
sun.jvm.hotspot.debugger.posix.elf \
|
||||
sun.jvm.hotspot.debugger.ppc64 \
|
||||
sun.jvm.hotspot.debugger.proc \
|
||||
sun.jvm.hotspot.debugger.proc.amd64 \
|
||||
sun.jvm.hotspot.debugger.proc.ppc64 \
|
||||
sun.jvm.hotspot.debugger.proc.sparc \
|
||||
sun.jvm.hotspot.debugger.proc.x86 \
|
||||
sun.jvm.hotspot.debugger.remote \
|
||||
sun.jvm.hotspot.debugger.remote.amd64 \
|
||||
sun.jvm.hotspot.debugger.remote.ppc64 \
|
||||
sun.jvm.hotspot.debugger.remote.sparc \
|
||||
sun.jvm.hotspot.debugger.remote.x86 \
|
||||
sun.jvm.hotspot.debugger.sparc \
|
||||
@ -93,9 +97,11 @@ sun.jvm.hotspot.runtime.bsd_amd64 \
|
||||
sun.jvm.hotspot.runtime.bsd_x86 \
|
||||
sun.jvm.hotspot.runtime.linux \
|
||||
sun.jvm.hotspot.runtime.linux_amd64 \
|
||||
sun.jvm.hotspot.runtime.linux_ppc64 \
|
||||
sun.jvm.hotspot.runtime.linux_sparc \
|
||||
sun.jvm.hotspot.runtime.linux_x86 \
|
||||
sun.jvm.hotspot.runtime.posix \
|
||||
sun.jvm.hotspot.runtime.ppc64 \
|
||||
sun.jvm.hotspot.runtime.solaris_amd64 \
|
||||
sun.jvm.hotspot.runtime.solaris_sparc \
|
||||
sun.jvm.hotspot.runtime.solaris_x86 \
|
||||
@ -142,15 +148,19 @@ sun/jvm/hotspot/debugger/cdbg/basic/amd64/*.java \
|
||||
sun/jvm/hotspot/debugger/cdbg/basic/x86/*.java \
|
||||
sun/jvm/hotspot/debugger/dummy/*.java \
|
||||
sun/jvm/hotspot/debugger/linux/*.java \
|
||||
sun/jvm/hotspot/debugger/linux/ppc64/*.java \
|
||||
sun/jvm/hotspot/debugger/linux/x86/*.java \
|
||||
sun/jvm/hotspot/debugger/posix/*.java \
|
||||
sun/jvm/hotspot/debugger/posix/elf/*.java \
|
||||
sun/jvm/hotspot/debugger/ppc64/*.java \
|
||||
sun/jvm/hotspot/debugger/proc/*.java \
|
||||
sun/jvm/hotspot/debugger/proc/amd64/*.java \
|
||||
sun/jvm/hotspot/debugger/proc/ppc64/*.java \
|
||||
sun/jvm/hotspot/debugger/proc/sparc/*.java \
|
||||
sun/jvm/hotspot/debugger/proc/x86/*.java \
|
||||
sun/jvm/hotspot/debugger/remote/*.java \
|
||||
sun/jvm/hotspot/debugger/remote/amd64/*.java \
|
||||
sun/jvm/hotspot/debugger/remote/ppc64/*.java \
|
||||
sun/jvm/hotspot/debugger/remote/sparc/*.java \
|
||||
sun/jvm/hotspot/debugger/remote/x86/*.java \
|
||||
sun/jvm/hotspot/debugger/sparc/*.java \
|
||||
@ -174,9 +184,11 @@ sun/jvm/hotspot/runtime/bsd_amd64/*.java \
|
||||
sun/jvm/hotspot/runtime/bsd_x86/*.java \
|
||||
sun/jvm/hotspot/runtime/linux/*.java \
|
||||
sun/jvm/hotspot/runtime/linux_amd64/*.java \
|
||||
sun/jvm/hotspot/runtime/linux_ppc64/*.java \
|
||||
sun/jvm/hotspot/runtime/linux_sparc/*.java \
|
||||
sun/jvm/hotspot/runtime/linux_x86/*.java \
|
||||
sun/jvm/hotspot/runtime/posix/*.java \
|
||||
sun/jvm/hotspot/runtime/ppc64/*.java \
|
||||
sun/jvm/hotspot/runtime/solaris_amd64/*.java \
|
||||
sun/jvm/hotspot/runtime/solaris_sparc/*.java \
|
||||
sun/jvm/hotspot/runtime/solaris_x86/*.java \
|
||||
|
||||
@ -49,6 +49,10 @@
|
||||
#include "sun_jvm_hotspot_debugger_sparc_SPARCThreadContext.h"
|
||||
#endif
|
||||
|
||||
#ifdef ppc64
|
||||
#include "sun_jvm_hotspot_debugger_ppc64_PPC64ThreadContext.h"
|
||||
#endif
|
||||
|
||||
static jfieldID p_ps_prochandle_ID = 0;
|
||||
static jfieldID threadList_ID = 0;
|
||||
static jfieldID loadObjectList_ID = 0;
|
||||
@ -341,7 +345,7 @@ JNIEXPORT jbyteArray JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLo
|
||||
return (err == PS_OK)? array : 0;
|
||||
}
|
||||
|
||||
#if defined(i386) || defined(amd64) || defined(sparc) || defined(sparcv9)
|
||||
#if defined(i386) || defined(amd64) || defined(sparc) || defined(sparcv9) | defined(ppc64)
|
||||
JNIEXPORT jlongArray JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_getThreadIntegerRegisterSet0
|
||||
(JNIEnv *env, jobject this_obj, jint lwp_id) {
|
||||
|
||||
@ -366,6 +370,10 @@ JNIEXPORT jlongArray JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLo
|
||||
#if defined(sparc) || defined(sparcv9)
|
||||
#define NPRGREG sun_jvm_hotspot_debugger_sparc_SPARCThreadContext_NPRGREG
|
||||
#endif
|
||||
#ifdef ppc64
|
||||
#define NPRGREG sun_jvm_hotspot_debugger_ppc64_PPC64ThreadContext_NPRGREG
|
||||
#endif
|
||||
|
||||
|
||||
array = (*env)->NewLongArray(env, NPRGREG);
|
||||
CHECK_EXCEPTION_(0);
|
||||
@ -458,6 +466,45 @@ JNIEXPORT jlongArray JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLo
|
||||
regs[REG_INDEX(R_O7)] = gregs.u_regs[14];
|
||||
#endif /* sparc */
|
||||
|
||||
#ifdef ppc64
|
||||
#define REG_INDEX(reg) sun_jvm_hotspot_debugger_ppc64_PPC64ThreadContext_##reg
|
||||
|
||||
regs[REG_INDEX(LR)] = gregs.link;
|
||||
regs[REG_INDEX(NIP)] = gregs.nip;
|
||||
regs[REG_INDEX(R0)] = gregs.gpr[0];
|
||||
regs[REG_INDEX(R1)] = gregs.gpr[1];
|
||||
regs[REG_INDEX(R2)] = gregs.gpr[2];
|
||||
regs[REG_INDEX(R3)] = gregs.gpr[3];
|
||||
regs[REG_INDEX(R4)] = gregs.gpr[4];
|
||||
regs[REG_INDEX(R5)] = gregs.gpr[5];
|
||||
regs[REG_INDEX(R6)] = gregs.gpr[6];
|
||||
regs[REG_INDEX(R7)] = gregs.gpr[7];
|
||||
regs[REG_INDEX(R8)] = gregs.gpr[8];
|
||||
regs[REG_INDEX(R9)] = gregs.gpr[9];
|
||||
regs[REG_INDEX(R10)] = gregs.gpr[10];
|
||||
regs[REG_INDEX(R11)] = gregs.gpr[11];
|
||||
regs[REG_INDEX(R12)] = gregs.gpr[12];
|
||||
regs[REG_INDEX(R13)] = gregs.gpr[13];
|
||||
regs[REG_INDEX(R14)] = gregs.gpr[14];
|
||||
regs[REG_INDEX(R15)] = gregs.gpr[15];
|
||||
regs[REG_INDEX(R16)] = gregs.gpr[16];
|
||||
regs[REG_INDEX(R17)] = gregs.gpr[17];
|
||||
regs[REG_INDEX(R18)] = gregs.gpr[18];
|
||||
regs[REG_INDEX(R19)] = gregs.gpr[19];
|
||||
regs[REG_INDEX(R20)] = gregs.gpr[20];
|
||||
regs[REG_INDEX(R21)] = gregs.gpr[21];
|
||||
regs[REG_INDEX(R22)] = gregs.gpr[22];
|
||||
regs[REG_INDEX(R23)] = gregs.gpr[23];
|
||||
regs[REG_INDEX(R24)] = gregs.gpr[24];
|
||||
regs[REG_INDEX(R25)] = gregs.gpr[25];
|
||||
regs[REG_INDEX(R26)] = gregs.gpr[26];
|
||||
regs[REG_INDEX(R27)] = gregs.gpr[27];
|
||||
regs[REG_INDEX(R28)] = gregs.gpr[28];
|
||||
regs[REG_INDEX(R29)] = gregs.gpr[29];
|
||||
regs[REG_INDEX(R30)] = gregs.gpr[30];
|
||||
regs[REG_INDEX(R31)] = gregs.gpr[31];
|
||||
|
||||
#endif
|
||||
|
||||
(*env)->ReleaseLongArrayElements(env, array, regs, JNI_COMMIT);
|
||||
return array;
|
||||
|
||||
@ -325,6 +325,12 @@ static struct symtab* build_symtab_internal(int fd, const char *filename, bool t
|
||||
|
||||
// Reading of elf header
|
||||
struct elf_section *scn_cache = NULL;
|
||||
#if defined(ppc64) && !defined(ABI_ELFv2)
|
||||
// Only big endian ppc64 (i.e. ABI_ELFv1) has 'official procedure descriptors' in ELF files
|
||||
// see: http://refspecs.linuxfoundation.org/LSB_3.1.1/LSB-Core-PPC64/LSB-Core-PPC64/specialsections.html
|
||||
struct elf_section *opd_sect = NULL;
|
||||
ELF_SHDR *opd = NULL;
|
||||
#endif
|
||||
int cnt = 0;
|
||||
ELF_SHDR* shbuf = NULL;
|
||||
ELF_SHDR* cursct = NULL;
|
||||
@ -368,6 +374,14 @@ static struct symtab* build_symtab_internal(int fd, const char *filename, bool t
|
||||
cursct++;
|
||||
}
|
||||
|
||||
#if defined(ppc64) && !defined(ABI_ELFv2)
|
||||
opd_sect = find_section_by_name(".opd", fd, &ehdr, scn_cache);
|
||||
if (opd_sect != NULL && opd_sect->c_data != NULL && opd_sect->c_shdr != NULL) {
|
||||
// plausibility check
|
||||
opd = opd_sect->c_shdr;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (cnt = 1; cnt < ehdr.e_shnum; cnt++) {
|
||||
ELF_SHDR *shdr = scn_cache[cnt].c_shdr;
|
||||
|
||||
@ -412,6 +426,7 @@ static struct symtab* build_symtab_internal(int fd, const char *filename, bool t
|
||||
// copy symbols info our symtab and enter them info the hash table
|
||||
for (j = 0; j < n; j++, syms++) {
|
||||
ENTRY item, *ret;
|
||||
uintptr_t sym_value;
|
||||
char *sym_name = symtab->strs + syms->st_name;
|
||||
|
||||
// skip non-object and non-function symbols
|
||||
@ -422,9 +437,19 @@ static struct symtab* build_symtab_internal(int fd, const char *filename, bool t
|
||||
if (*sym_name == '\0' || syms->st_shndx == SHN_UNDEF) continue;
|
||||
|
||||
symtab->symbols[j].name = sym_name;
|
||||
symtab->symbols[j].offset = syms->st_value - baseaddr;
|
||||
symtab->symbols[j].size = syms->st_size;
|
||||
sym_value = syms->st_value;
|
||||
|
||||
#if defined(ppc64) && !defined(ABI_ELFv2)
|
||||
// see hotspot/src/share/vm/utilities/elfFuncDescTable.hpp for a detailed description
|
||||
// of why we have to go this extra way via the '.opd' section on big endian ppc64
|
||||
if (opd != NULL && *sym_name != '.' &&
|
||||
(opd->sh_addr <= sym_value && sym_value <= opd->sh_addr + opd->sh_size)) {
|
||||
sym_value = ((ELF_ADDR*)opd_sect->c_data)[(sym_value - opd->sh_addr) / sizeof(ELF_ADDR*)];
|
||||
}
|
||||
#endif
|
||||
|
||||
symtab->symbols[j].offset = sym_value - baseaddr;
|
||||
item.key = sym_name;
|
||||
item.data = (void *)&(symtab->symbols[j]);
|
||||
|
||||
@ -433,9 +458,17 @@ static struct symtab* build_symtab_internal(int fd, const char *filename, bool t
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(ppc64) && !defined(ABI_ELFv2)
|
||||
// On Linux/PPC64 the debuginfo files contain an empty function descriptor
|
||||
// section (i.e. '.opd' section) which makes the resolution of symbols
|
||||
// with the above algorithm impossible (we would need the have both, the
|
||||
// .opd section from the library and the symbol table from the debuginfo
|
||||
// file which doesn't match with the current workflow.)
|
||||
goto quit;
|
||||
#endif
|
||||
|
||||
// Look for a separate debuginfo file.
|
||||
if (try_debuginfo) {
|
||||
|
||||
// We prefer a debug symtab to an object's own symtab, so look in
|
||||
// the debuginfo file. We stash a copy of the old symtab in case
|
||||
// there is no debuginfo.
|
||||
|
||||
@ -34,6 +34,6 @@ public class MachineDescriptionPPC64 extends MachineDescriptionTwosComplement im
|
||||
}
|
||||
|
||||
public boolean isBigEndian() {
|
||||
return true;
|
||||
return "big".equals(System.getProperty("sun.cpu.endian"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,14 +26,17 @@ package sun.jvm.hotspot.debugger.linux;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import sun.jvm.hotspot.debugger.*;
|
||||
import sun.jvm.hotspot.debugger.cdbg.*;
|
||||
import sun.jvm.hotspot.debugger.x86.*;
|
||||
import sun.jvm.hotspot.debugger.amd64.*;
|
||||
import sun.jvm.hotspot.debugger.sparc.*;
|
||||
import sun.jvm.hotspot.debugger.ppc64.*;
|
||||
import sun.jvm.hotspot.debugger.linux.x86.*;
|
||||
import sun.jvm.hotspot.debugger.linux.amd64.*;
|
||||
import sun.jvm.hotspot.debugger.linux.sparc.*;
|
||||
import sun.jvm.hotspot.debugger.linux.ppc64.*;
|
||||
import sun.jvm.hotspot.utilities.*;
|
||||
|
||||
class LinuxCDebugger implements CDebugger {
|
||||
@ -96,7 +99,14 @@ class LinuxCDebugger implements CDebugger {
|
||||
Address pc = context.getRegisterAsAddress(SPARCThreadContext.R_O7);
|
||||
if (pc == null) return null;
|
||||
return new LinuxSPARCCFrame(dbg, sp, pc, LinuxDebuggerLocal.getAddressSize());
|
||||
} else {
|
||||
} else if (cpu.equals("ppc64")) {
|
||||
PPC64ThreadContext context = (PPC64ThreadContext) thread.getContext();
|
||||
Address sp = context.getRegisterAsAddress(PPC64ThreadContext.SP);
|
||||
if (sp == null) return null;
|
||||
Address pc = context.getRegisterAsAddress(PPC64ThreadContext.PC);
|
||||
if (pc == null) return null;
|
||||
return new LinuxPPC64CFrame(dbg, sp, pc, LinuxDebuggerLocal.getAddressSize());
|
||||
} else {
|
||||
// Runtime exception thrown by LinuxThreadContextFactory if unknown cpu
|
||||
ThreadContext context = (ThreadContext) thread.getContext();
|
||||
return context.getTopFrame(dbg);
|
||||
|
||||
@ -29,6 +29,7 @@ import sun.jvm.hotspot.debugger.*;
|
||||
import sun.jvm.hotspot.debugger.linux.amd64.*;
|
||||
import sun.jvm.hotspot.debugger.linux.ia64.*;
|
||||
import sun.jvm.hotspot.debugger.linux.x86.*;
|
||||
import sun.jvm.hotspot.debugger.linux.ppc64.*;
|
||||
import sun.jvm.hotspot.debugger.linux.sparc.*;
|
||||
|
||||
class LinuxThreadContextFactory {
|
||||
@ -42,6 +43,8 @@ class LinuxThreadContextFactory {
|
||||
return new LinuxIA64ThreadContext(dbg);
|
||||
} else if (cpu.equals("sparc")) {
|
||||
return new LinuxSPARCThreadContext(dbg);
|
||||
} else if (cpu.equals("ppc64")) {
|
||||
return new LinuxPPC64ThreadContext(dbg);
|
||||
} else {
|
||||
try {
|
||||
Class tcc = Class.forName("sun.jvm.hotspot.debugger.linux." +
|
||||
|
||||
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. 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 sun.jvm.hotspot.debugger.linux.ppc64;
|
||||
|
||||
import sun.jvm.hotspot.debugger.*;
|
||||
import sun.jvm.hotspot.debugger.ppc64.*;
|
||||
import sun.jvm.hotspot.debugger.linux.*;
|
||||
import sun.jvm.hotspot.debugger.cdbg.*;
|
||||
import sun.jvm.hotspot.debugger.cdbg.basic.*;
|
||||
|
||||
final public class LinuxPPC64CFrame extends BasicCFrame {
|
||||
// package/class internals only
|
||||
|
||||
public LinuxPPC64CFrame(LinuxDebugger dbg, Address sp, Address pc, int address_size) {
|
||||
super(dbg.getCDebugger());
|
||||
this.sp = sp;
|
||||
this.pc = pc;
|
||||
this.dbg = dbg;
|
||||
this.address_size = address_size;
|
||||
}
|
||||
|
||||
// override base class impl to avoid ELF parsing
|
||||
public ClosestSymbol closestSymbolToPC() {
|
||||
// try native lookup in debugger.
|
||||
return dbg.lookup(dbg.getAddressValue(pc()));
|
||||
}
|
||||
|
||||
public Address pc() {
|
||||
return pc;
|
||||
}
|
||||
|
||||
public Address localVariableBase() {
|
||||
return sp;
|
||||
}
|
||||
|
||||
public CFrame sender(ThreadProxy thread) {
|
||||
if (sp == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Address nextSP = sp.getAddressAt(0);
|
||||
if (nextSP == null) {
|
||||
return null;
|
||||
}
|
||||
Address nextPC = sp.getAddressAt(2 * address_size);
|
||||
if (nextPC == null) {
|
||||
return null;
|
||||
}
|
||||
return new LinuxPPC64CFrame(dbg, nextSP, nextPC, address_size);
|
||||
}
|
||||
|
||||
public static int PPC64_STACK_BIAS = 0;
|
||||
private static int address_size;
|
||||
private Address pc;
|
||||
private Address sp;
|
||||
private LinuxDebugger dbg;
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. 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 sun.jvm.hotspot.debugger.linux.ppc64;
|
||||
|
||||
import sun.jvm.hotspot.debugger.*;
|
||||
import sun.jvm.hotspot.debugger.ppc64.*;
|
||||
import sun.jvm.hotspot.debugger.linux.*;
|
||||
|
||||
public class LinuxPPC64ThreadContext extends PPC64ThreadContext {
|
||||
private LinuxDebugger debugger;
|
||||
|
||||
public LinuxPPC64ThreadContext(LinuxDebugger debugger) {
|
||||
super();
|
||||
this.debugger = debugger;
|
||||
}
|
||||
|
||||
public void setRegisterAsAddress(int index, Address value) {
|
||||
setRegister(index, debugger.getAddressValue(value));
|
||||
}
|
||||
|
||||
public Address getRegisterAsAddress(int index) {
|
||||
return debugger.newAddress(getRegister(index));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. 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 sun.jvm.hotspot.debugger.ppc64;
|
||||
|
||||
import sun.jvm.hotspot.debugger.*;
|
||||
import sun.jvm.hotspot.debugger.cdbg.*;
|
||||
|
||||
/** Specifies the thread context on ppc64 platforms; only a sub-portion
|
||||
* of the context is guaranteed to be present on all operating
|
||||
* systems. */
|
||||
|
||||
public abstract class PPC64ThreadContext implements ThreadContext {
|
||||
|
||||
// NOTE: The indices for the various registers must be maintained as
|
||||
// listed across various operating systems. However, only a small
|
||||
// subset of the registers' values are guaranteed to be present (and
|
||||
// must be present for the SA's stack walking to work).
|
||||
|
||||
public static final int R31 = 0;
|
||||
public static final int R30 = 1;
|
||||
public static final int R29 = 2;
|
||||
public static final int R28 = 3;
|
||||
public static final int R27 = 4;
|
||||
public static final int R26 = 5;
|
||||
public static final int R25 = 6;
|
||||
public static final int R24 = 7;
|
||||
public static final int R23 = 8;
|
||||
public static final int R22 = 9;
|
||||
public static final int R21 = 10;
|
||||
public static final int R20 = 11;
|
||||
public static final int R19 = 12;
|
||||
public static final int R18 = 13;
|
||||
public static final int R17 = 14;
|
||||
public static final int R16 = 15;
|
||||
public static final int R15 = 16;
|
||||
public static final int R14 = 17;
|
||||
public static final int R13 = 18;
|
||||
public static final int R12 = 19;
|
||||
public static final int R11 = 20;
|
||||
public static final int R10 = 21;
|
||||
public static final int R9 = 22;
|
||||
public static final int R8 = 23;
|
||||
public static final int R7 = 24;
|
||||
public static final int R6 = 25;
|
||||
public static final int R5 = 26;
|
||||
public static final int R4 = 27;
|
||||
public static final int R3 = 28;
|
||||
public static final int R2 = 29;
|
||||
public static final int R1 = 30;
|
||||
public static final int R0 = 31;
|
||||
public static final int NIP = 32;
|
||||
public static final int LR = 33;
|
||||
|
||||
public static final int NPRGREG = 34;
|
||||
|
||||
private static final String[] regNames = {
|
||||
"r31", "r30", "r29", "r28", "r27", "r26", "r25", "r24",
|
||||
"r23", "r22", "r21", "r20", "r19", "r18", "r17", "r16",
|
||||
"r15", "r14", "r13", "r12", "r11", "r10", "r9", "r8",
|
||||
"r7", "r6", "r5", "r4", "r3", "r2", "r1", "r0",
|
||||
"nip", "link"
|
||||
};
|
||||
|
||||
public static final int PC = NIP;
|
||||
public static final int SP = R1;
|
||||
|
||||
private long[] data;
|
||||
|
||||
public PPC64ThreadContext() {
|
||||
data = new long[NPRGREG];
|
||||
}
|
||||
|
||||
public int getNumRegisters() {
|
||||
return NPRGREG;
|
||||
}
|
||||
|
||||
public String getRegisterName(int index) {
|
||||
return regNames[index];
|
||||
}
|
||||
|
||||
public void setRegister(int index, long value) {
|
||||
data[index] = value;
|
||||
}
|
||||
|
||||
public long getRegister(int index) {
|
||||
return data[index];
|
||||
}
|
||||
|
||||
public CFrame getTopFrame(Debugger dbg) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** This can't be implemented in this class since we would have to
|
||||
* tie the implementation to, for example, the debugging system */
|
||||
public abstract void setRegisterAsAddress(int index, Address value);
|
||||
|
||||
/** This can't be implemented in this class since we would have to
|
||||
* tie the implementation to, for example, the debugging system */
|
||||
public abstract Address getRegisterAsAddress(int index);
|
||||
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. 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
|
||||
@ -32,7 +32,9 @@ import sun.jvm.hotspot.debugger.*;
|
||||
import sun.jvm.hotspot.debugger.cdbg.*;
|
||||
import sun.jvm.hotspot.debugger.proc.amd64.*;
|
||||
import sun.jvm.hotspot.debugger.proc.sparc.*;
|
||||
import sun.jvm.hotspot.debugger.proc.ppc64.*;
|
||||
import sun.jvm.hotspot.debugger.proc.x86.*;
|
||||
import sun.jvm.hotspot.debugger.ppc64.*;
|
||||
import sun.jvm.hotspot.debugger.amd64.*;
|
||||
import sun.jvm.hotspot.debugger.sparc.*;
|
||||
import sun.jvm.hotspot.debugger.x86.*;
|
||||
@ -86,6 +88,10 @@ public class ProcDebuggerLocal extends DebuggerBase implements ProcDebugger {
|
||||
threadFactory = new ProcAMD64ThreadFactory(this);
|
||||
pcRegIndex = AMD64ThreadContext.RIP;
|
||||
fpRegIndex = AMD64ThreadContext.RBP;
|
||||
} else if (cpu.equals("ppc64")) {
|
||||
threadFactory = new ProcPPC64ThreadFactory(this);
|
||||
pcRegIndex = PPC64ThreadContext.PC;
|
||||
fpRegIndex = PPC64ThreadContext.SP;
|
||||
} else {
|
||||
try {
|
||||
Class tfc = Class.forName("sun.jvm.hotspot.debugger.proc." +
|
||||
|
||||
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. 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 sun.jvm.hotspot.debugger.proc.ppc64;
|
||||
|
||||
import sun.jvm.hotspot.debugger.*;
|
||||
import sun.jvm.hotspot.debugger.ppc64.*;
|
||||
import sun.jvm.hotspot.debugger.proc.*;
|
||||
import sun.jvm.hotspot.utilities.*;
|
||||
|
||||
public class ProcPPC64Thread implements ThreadProxy {
|
||||
private ProcDebugger debugger;
|
||||
private int id;
|
||||
|
||||
public ProcPPC64Thread(ProcDebugger debugger, Address addr) {
|
||||
this.debugger = debugger;
|
||||
|
||||
// FIXME: the size here should be configurable. However, making it
|
||||
// so would produce a dependency on the "types" package from the
|
||||
// debugger package, which is not desired.
|
||||
this.id = (int) addr.getCIntegerAt(0, 4, true);
|
||||
}
|
||||
|
||||
public ProcPPC64Thread(ProcDebugger debugger, long id) {
|
||||
this.debugger = debugger;
|
||||
this.id = (int) id;
|
||||
}
|
||||
|
||||
public ThreadContext getContext() throws IllegalThreadStateException {
|
||||
ProcPPC64ThreadContext context = new ProcPPC64ThreadContext(debugger);
|
||||
long[] regs = debugger.getThreadIntegerRegisterSet(id);
|
||||
if (Assert.ASSERTS_ENABLED) {
|
||||
Assert.that(regs.length <= PPC64ThreadContext.NPRGREG, "size of register set is greater than " + PPC64ThreadContext.NPRGREG);
|
||||
}
|
||||
for (int i = 0; i < regs.length; i++) {
|
||||
context.setRegister(i, regs[i]);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
public boolean canSetContext() throws DebuggerException {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setContext(ThreadContext context)
|
||||
throws IllegalThreadStateException, DebuggerException {
|
||||
throw new DebuggerException("Unimplemented");
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "t@" + id;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if ((obj == null) || !(obj instanceof ProcPPC64Thread)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (((ProcPPC64Thread) obj).id == id);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. 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 sun.jvm.hotspot.debugger.proc.ppc64;
|
||||
|
||||
import sun.jvm.hotspot.debugger.*;
|
||||
import sun.jvm.hotspot.debugger.ppc64.*;
|
||||
import sun.jvm.hotspot.debugger.proc.*;
|
||||
|
||||
public class ProcPPC64ThreadContext extends PPC64ThreadContext {
|
||||
private ProcDebugger debugger;
|
||||
|
||||
public ProcPPC64ThreadContext(ProcDebugger debugger) {
|
||||
super();
|
||||
this.debugger = debugger;
|
||||
}
|
||||
|
||||
public void setRegisterAsAddress(int index, Address value) {
|
||||
setRegister(index, debugger.getAddressValue(value));
|
||||
}
|
||||
|
||||
public Address getRegisterAsAddress(int index) {
|
||||
return debugger.newAddress(getRegister(index));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. 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 sun.jvm.hotspot.debugger.proc.ppc64;
|
||||
|
||||
import sun.jvm.hotspot.debugger.*;
|
||||
import sun.jvm.hotspot.debugger.proc.*;
|
||||
|
||||
public class ProcPPC64ThreadFactory implements ProcThreadFactory {
|
||||
private ProcDebugger debugger;
|
||||
|
||||
public ProcPPC64ThreadFactory(ProcDebugger debugger) {
|
||||
this.debugger = debugger;
|
||||
}
|
||||
|
||||
public ThreadProxy createThreadWrapper(Address threadIdentifierAddr) {
|
||||
return new ProcPPC64Thread(debugger, threadIdentifierAddr);
|
||||
}
|
||||
|
||||
public ThreadProxy createThreadWrapper(long id) {
|
||||
return new ProcPPC64Thread(debugger, id);
|
||||
}
|
||||
}
|
||||
@ -33,6 +33,7 @@ import sun.jvm.hotspot.debugger.cdbg.*;
|
||||
import sun.jvm.hotspot.debugger.remote.sparc.*;
|
||||
import sun.jvm.hotspot.debugger.remote.x86.*;
|
||||
import sun.jvm.hotspot.debugger.remote.amd64.*;
|
||||
import sun.jvm.hotspot.debugger.remote.ppc64.*;
|
||||
|
||||
/** An implementation of Debugger which wraps a
|
||||
RemoteDebugger, providing remote debugging via RMI.
|
||||
@ -70,6 +71,11 @@ public class RemoteDebuggerClient extends DebuggerBase implements JVMDebugger {
|
||||
cachePageSize = 4096;
|
||||
cacheNumPages = parseCacheNumPagesProperty(cacheSize / cachePageSize);
|
||||
unalignedAccessesOkay = true;
|
||||
} else if (cpu.equals("ppc64")) {
|
||||
threadFactory = new RemotePPC64ThreadFactory(this);
|
||||
cachePageSize = 4096;
|
||||
cacheNumPages = parseCacheNumPagesProperty(cacheSize / cachePageSize);
|
||||
unalignedAccessesOkay = true;
|
||||
} else {
|
||||
try {
|
||||
Class tf = Class.forName("sun.jvm.hotspot.debugger.remote." +
|
||||
|
||||
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. 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 sun.jvm.hotspot.debugger.remote.ppc64;
|
||||
|
||||
import sun.jvm.hotspot.debugger.*;
|
||||
import sun.jvm.hotspot.debugger.ppc64.*;
|
||||
import sun.jvm.hotspot.debugger.remote.*;
|
||||
import sun.jvm.hotspot.utilities.*;
|
||||
|
||||
public class RemotePPC64Thread extends RemoteThread {
|
||||
public RemotePPC64Thread(RemoteDebuggerClient debugger, Address addr) {
|
||||
super(debugger, addr);
|
||||
}
|
||||
|
||||
public RemotePPC64Thread(RemoteDebuggerClient debugger, long id) {
|
||||
super(debugger, id);
|
||||
}
|
||||
|
||||
public ThreadContext getContext() throws IllegalThreadStateException {
|
||||
RemotePPC64ThreadContext context = new RemotePPC64ThreadContext(debugger);
|
||||
long[] regs = (addr != null)? debugger.getThreadIntegerRegisterSet(addr) :
|
||||
debugger.getThreadIntegerRegisterSet(id);
|
||||
if (Assert.ASSERTS_ENABLED) {
|
||||
Assert.that(regs.length == PPC64ThreadContext.NPRGREG, "size of register set must match");
|
||||
}
|
||||
for (int i = 0; i < regs.length; i++) {
|
||||
context.setRegister(i, regs[i]);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. 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 sun.jvm.hotspot.debugger.remote.ppc64;
|
||||
|
||||
import sun.jvm.hotspot.debugger.*;
|
||||
import sun.jvm.hotspot.debugger.ppc64.*;
|
||||
import sun.jvm.hotspot.debugger.remote.*;
|
||||
|
||||
public class RemotePPC64ThreadContext extends PPC64ThreadContext {
|
||||
private RemoteDebuggerClient debugger;
|
||||
|
||||
public RemotePPC64ThreadContext(RemoteDebuggerClient debugger) {
|
||||
super();
|
||||
this.debugger = debugger;
|
||||
}
|
||||
|
||||
/** This can't be implemented in this class since we would have to
|
||||
tie the implementation to, for example, the debugging system */
|
||||
public void setRegisterAsAddress(int index, Address value) {
|
||||
setRegister(index, debugger.getAddressValue(value));
|
||||
}
|
||||
|
||||
/** This can't be implemented in this class since we would have to
|
||||
tie the implementation to, for example, the debugging system */
|
||||
public Address getRegisterAsAddress(int index) {
|
||||
return debugger.newAddress(getRegister(index));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. 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 sun.jvm.hotspot.debugger.remote.ppc64;
|
||||
|
||||
import sun.jvm.hotspot.debugger.*;
|
||||
import sun.jvm.hotspot.debugger.remote.*;
|
||||
|
||||
public class RemotePPC64ThreadFactory implements RemoteThreadFactory {
|
||||
private RemoteDebuggerClient debugger;
|
||||
|
||||
public RemotePPC64ThreadFactory(RemoteDebuggerClient debugger) {
|
||||
this.debugger = debugger;
|
||||
}
|
||||
|
||||
public ThreadProxy createThreadWrapper(Address threadIdentifierAddr) {
|
||||
return new RemotePPC64Thread(debugger, threadIdentifierAddr);
|
||||
}
|
||||
|
||||
public ThreadProxy createThreadWrapper(long id) {
|
||||
return new RemotePPC64Thread(debugger, id);
|
||||
}
|
||||
}
|
||||
@ -25,6 +25,7 @@
|
||||
package sun.jvm.hotspot.runtime;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import sun.jvm.hotspot.debugger.*;
|
||||
import sun.jvm.hotspot.types.*;
|
||||
import sun.jvm.hotspot.runtime.solaris_sparc.SolarisSPARCJavaThreadPDAccess;
|
||||
@ -34,6 +35,7 @@ import sun.jvm.hotspot.runtime.win32_amd64.Win32AMD64JavaThreadPDAccess;
|
||||
import sun.jvm.hotspot.runtime.win32_x86.Win32X86JavaThreadPDAccess;
|
||||
import sun.jvm.hotspot.runtime.linux_x86.LinuxX86JavaThreadPDAccess;
|
||||
import sun.jvm.hotspot.runtime.linux_amd64.LinuxAMD64JavaThreadPDAccess;
|
||||
import sun.jvm.hotspot.runtime.linux_ppc64.LinuxPPC64JavaThreadPDAccess;
|
||||
import sun.jvm.hotspot.runtime.linux_sparc.LinuxSPARCJavaThreadPDAccess;
|
||||
import sun.jvm.hotspot.runtime.bsd_x86.BsdX86JavaThreadPDAccess;
|
||||
import sun.jvm.hotspot.runtime.bsd_amd64.BsdAMD64JavaThreadPDAccess;
|
||||
@ -87,6 +89,8 @@ public class Threads {
|
||||
access = new LinuxAMD64JavaThreadPDAccess();
|
||||
} else if (cpu.equals("sparc")) {
|
||||
access = new LinuxSPARCJavaThreadPDAccess();
|
||||
} else if (cpu.equals("ppc64")) {
|
||||
access = new LinuxPPC64JavaThreadPDAccess();
|
||||
} else {
|
||||
try {
|
||||
access = (JavaThreadPDAccess)
|
||||
|
||||
@ -78,7 +78,7 @@ public class VFrame {
|
||||
}
|
||||
|
||||
if (f.isRuntimeFrame()) {
|
||||
// This is a conversion frame. Skip this frame and try again.
|
||||
// This is a conversion frame or a Stub routine. Skip this frame and try again.
|
||||
RegisterMap tempMap = regMap.copy();
|
||||
Frame s = f.sender(tempMap);
|
||||
return newVFrame(s, tempMap, thread, unsafe, false);
|
||||
|
||||
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. 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 sun.jvm.hotspot.runtime.linux_ppc64;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import sun.jvm.hotspot.debugger.*;
|
||||
import sun.jvm.hotspot.debugger.ppc64.*;
|
||||
import sun.jvm.hotspot.runtime.*;
|
||||
import sun.jvm.hotspot.runtime.ppc64.*;
|
||||
import sun.jvm.hotspot.types.*;
|
||||
import sun.jvm.hotspot.utilities.*;
|
||||
|
||||
public class LinuxPPC64JavaThreadPDAccess implements JavaThreadPDAccess {
|
||||
private static AddressField osThreadField;
|
||||
|
||||
// Field from OSThread
|
||||
private static CIntegerField osThreadThreadIDField;
|
||||
|
||||
// This is currently unneeded but is being kept in case we change
|
||||
// the currentFrameGuess algorithm
|
||||
private static final long GUESS_SCAN_RANGE = 128 * 1024;
|
||||
|
||||
static {
|
||||
VM.registerVMInitializedObserver(new Observer() {
|
||||
public void update(Observable o, Object data) {
|
||||
initialize(VM.getVM().getTypeDataBase());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static synchronized void initialize(TypeDataBase db) {
|
||||
Type type = db.lookupType("JavaThread");
|
||||
osThreadField = type.getAddressField("_osthread");
|
||||
|
||||
Type osThreadType = db.lookupType("OSThread");
|
||||
osThreadThreadIDField = osThreadType.getCIntegerField("_thread_id");
|
||||
}
|
||||
|
||||
public Address getLastJavaFP(Address addr) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Address getLastJavaPC(Address addr) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Address getBaseOfStackPointer(Address addr) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Frame getLastFramePD(JavaThread thread, Address addr) {
|
||||
Address fp = thread.getLastJavaFP();
|
||||
if (fp == null) {
|
||||
return null; // no information
|
||||
}
|
||||
return new PPC64Frame(thread.getLastJavaSP(), fp);
|
||||
}
|
||||
|
||||
public RegisterMap newRegisterMap(JavaThread thread, boolean updateMap) {
|
||||
return new PPC64RegisterMap(thread, updateMap);
|
||||
}
|
||||
|
||||
public Frame getCurrentFrameGuess(JavaThread thread, Address addr) {
|
||||
ThreadProxy t = getThreadProxy(addr);
|
||||
PPC64ThreadContext context = (PPC64ThreadContext) t.getContext();
|
||||
PPC64CurrentFrameGuess guesser = new PPC64CurrentFrameGuess(context, thread);
|
||||
if (!guesser.run(GUESS_SCAN_RANGE)) {
|
||||
return null;
|
||||
}
|
||||
if (guesser.getPC() == null) {
|
||||
return new PPC64Frame(guesser.getSP(), guesser.getFP());
|
||||
} else {
|
||||
return new PPC64Frame(guesser.getSP(), guesser.getFP(), guesser.getPC());
|
||||
}
|
||||
}
|
||||
|
||||
public void printThreadIDOn(Address addr, PrintStream tty) {
|
||||
tty.print(getThreadProxy(addr));
|
||||
}
|
||||
|
||||
public void printInfoOn(Address threadAddr, PrintStream tty) {
|
||||
tty.print("Thread id: ");
|
||||
printThreadIDOn(threadAddr, tty);
|
||||
// tty.println("\nPostJavaState: " + getPostJavaState(threadAddr));
|
||||
}
|
||||
|
||||
public Address getLastSP(Address addr) {
|
||||
ThreadProxy t = getThreadProxy(addr);
|
||||
PPC64ThreadContext context = (PPC64ThreadContext) t.getContext();
|
||||
return context.getRegisterAsAddress(PPC64ThreadContext.SP);
|
||||
}
|
||||
|
||||
public Address getLastFP(Address addr) {
|
||||
return getLastSP(addr).getAddressAt(0);
|
||||
}
|
||||
|
||||
public ThreadProxy getThreadProxy(Address addr) {
|
||||
// Addr is the address of the JavaThread.
|
||||
// Fetch the OSThread (for now and for simplicity, not making a
|
||||
// separate "OSThread" class in this package)
|
||||
Address osThreadAddr = osThreadField.getValue(addr);
|
||||
// Get the address of the _thread_id from the OSThread
|
||||
Address threadIdAddr = osThreadAddr.addOffsetTo(osThreadThreadIDField.getOffset());
|
||||
|
||||
JVMDebugger debugger = VM.getVM().getDebugger();
|
||||
return debugger.getThreadForIdentifierAddress(threadIdAddr);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. 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 sun.jvm.hotspot.runtime.ppc64;
|
||||
|
||||
import sun.jvm.hotspot.debugger.*;
|
||||
import sun.jvm.hotspot.debugger.ppc64.*;
|
||||
import sun.jvm.hotspot.code.*;
|
||||
import sun.jvm.hotspot.interpreter.*;
|
||||
import sun.jvm.hotspot.runtime.*;
|
||||
import sun.jvm.hotspot.runtime.ppc64.*;
|
||||
|
||||
/** <P> Should be able to be used on all ppc64 platforms we support
|
||||
(Linux/ppc64) to implement JavaThread's "currentFrameGuess()"
|
||||
functionality. Input is a PPC64ThreadContext; output is SP, FP,
|
||||
and PC for an PPC64Frame. Instantiation of the PPC64Frame is left
|
||||
to the caller, since we may need to subclass PPC64Frame to support
|
||||
signal handler frames on Unix platforms. </P>
|
||||
*/
|
||||
|
||||
public class PPC64CurrentFrameGuess {
|
||||
private PPC64ThreadContext context;
|
||||
private JavaThread thread;
|
||||
private Address spFound;
|
||||
private Address fpFound;
|
||||
private Address pcFound;
|
||||
|
||||
private static final boolean DEBUG;
|
||||
static {
|
||||
DEBUG = System.getProperty("sun.jvm.hotspot.runtime.ppc64.PPC64Frame.DEBUG") != null;
|
||||
}
|
||||
|
||||
public PPC64CurrentFrameGuess(PPC64ThreadContext context,
|
||||
JavaThread thread) {
|
||||
this.context = context;
|
||||
this.thread = thread;
|
||||
}
|
||||
|
||||
/** Returns false if not able to find a frame within a reasonable range. */
|
||||
public boolean run(long regionInBytesToSearch) {
|
||||
Address sp = context.getRegisterAsAddress(PPC64ThreadContext.SP);
|
||||
Address pc = context.getRegisterAsAddress(PPC64ThreadContext.PC);
|
||||
if (sp == null) {
|
||||
// Bail out if no last java frame either
|
||||
if (thread.getLastJavaSP() != null) {
|
||||
Address javaSP = thread.getLastJavaSP();
|
||||
Address javaFP = javaSP.getAddressAt(0);
|
||||
setValues(javaSP, javaFP, null);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/* There is no frame pointer per se for the ppc64 architecture. To mirror
|
||||
* the behavior of the VM frame manager, we set fp to be the caller's (i.e., "sender's")
|
||||
* stack pointer, which is the back chain value contained in our sp.
|
||||
*/
|
||||
Address fp = sp.getAddressAt(0);
|
||||
setValues(null, null, null); // Assume we're not going to find anything
|
||||
|
||||
VM vm = VM.getVM();
|
||||
if (vm.isJavaPCDbg(pc)) {
|
||||
if (vm.isClientCompiler()) {
|
||||
// Topmost frame is a Java frame.
|
||||
if (DEBUG) {
|
||||
System.out.println("CurrentFrameGuess: choosing compiler frame: sp = " +
|
||||
sp + ", fp = " + fp + ", pc = " + pc);
|
||||
}
|
||||
setValues(sp, fp, pc);
|
||||
return true;
|
||||
} else {
|
||||
if (vm.getInterpreter().contains(pc)) {
|
||||
if (DEBUG) {
|
||||
System.out.println("CurrentFrameGuess: choosing interpreter frame: sp = " +
|
||||
sp + ", fp = " + fp + ", pc = " + pc);
|
||||
}
|
||||
setValues(sp, fp, pc);
|
||||
return true;
|
||||
}
|
||||
|
||||
// This algorithm takes the current PC as a given and tries to
|
||||
// find the correct corresponding SP by walking up the stack
|
||||
// and repeatedly performing stackwalks (very inefficient).
|
||||
for (long offset = 0;
|
||||
offset < regionInBytesToSearch;
|
||||
offset += vm.getAddressSize()) {
|
||||
try {
|
||||
Address curSP = sp.addOffsetTo(offset);
|
||||
fp = curSP.getAddressAt(0);
|
||||
Frame frame = new PPC64Frame(curSP, fp, pc);
|
||||
RegisterMap map = thread.newRegisterMap(false);
|
||||
while (frame != null) {
|
||||
if (frame.isEntryFrame() && frame.entryFrameIsFirst()) {
|
||||
// We were able to traverse all the way to the
|
||||
// bottommost Java frame.
|
||||
// This sp looks good. Keep it.
|
||||
if (DEBUG) {
|
||||
System.out.println("CurrentFrameGuess: Choosing sp = " + curSP + ", pc = " + pc);
|
||||
}
|
||||
setValues(curSP, fp, pc);
|
||||
return true;
|
||||
}
|
||||
frame = frame.sender(map);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (DEBUG) {
|
||||
System.out.println("CurrentFrameGuess: Exception " + e + " at offset " + offset);
|
||||
}
|
||||
// Bad SP. Try another.
|
||||
}
|
||||
}
|
||||
|
||||
// Were not able to find a plausible SP to go with this PC.
|
||||
// Bail out.
|
||||
return false;
|
||||
|
||||
}
|
||||
} else {
|
||||
// If the current program counter was not known to us as a Java
|
||||
// PC, we currently assume that we are in the run-time system
|
||||
// and attempt to look to thread-local storage for saved java SP.
|
||||
// Note that if this is null (because we were, in fact,
|
||||
// in Java code, i.e., vtable stubs or similar, and the SA
|
||||
// didn't have enough insight into the target VM to understand
|
||||
// that) then we are going to lose the entire stack trace for
|
||||
// the thread, which is sub-optimal. FIXME.
|
||||
|
||||
if (thread.getLastJavaSP() == null) {
|
||||
if (DEBUG) {
|
||||
System.out.println("CurrentFrameGuess: last java sp is null");
|
||||
}
|
||||
return false; // No known Java frames on stack
|
||||
}
|
||||
|
||||
Address javaSP = thread.getLastJavaSP();
|
||||
Address javaFP = javaSP.getAddressAt(0);
|
||||
Address javaPC = thread.getLastJavaPC();
|
||||
if (DEBUG) {
|
||||
System.out.println("CurrentFrameGuess: choosing last Java frame: sp = " +
|
||||
javaSP + ", fp = " + javaFP + ", pc = " + javaPC);
|
||||
}
|
||||
setValues(javaSP, javaFP, javaPC);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public Address getSP() { return spFound; }
|
||||
public Address getFP() { return fpFound; }
|
||||
/** May be null if getting values from thread-local storage; take
|
||||
care to call the correct PPC64Frame constructor to recover this if
|
||||
necessary */
|
||||
public Address getPC() { return pcFound; }
|
||||
|
||||
private void setValues(Address sp, Address fp, Address pc) {
|
||||
spFound = sp;
|
||||
fpFound = fp;
|
||||
pcFound = pc;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,513 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. 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 sun.jvm.hotspot.runtime.ppc64;
|
||||
|
||||
import java.util.*;
|
||||
import sun.jvm.hotspot.code.*;
|
||||
import sun.jvm.hotspot.compiler.*;
|
||||
import sun.jvm.hotspot.debugger.*;
|
||||
import sun.jvm.hotspot.oops.*;
|
||||
import sun.jvm.hotspot.runtime.*;
|
||||
import sun.jvm.hotspot.types.*;
|
||||
import sun.jvm.hotspot.utilities.*;
|
||||
|
||||
/** Specialization of and implementation of abstract methods of the
|
||||
Frame class for the ppc64 family of CPUs. */
|
||||
|
||||
public class PPC64Frame extends Frame {
|
||||
private static final boolean DEBUG;
|
||||
static {
|
||||
DEBUG = System.getProperty("sun.jvm.hotspot.runtime.ppc64.PPC64Frame.DEBUG") != null;
|
||||
}
|
||||
|
||||
// All frames
|
||||
private static final int SENDER_SP_OFFSET = 0;
|
||||
|
||||
// Interpreter frames
|
||||
private static final int INTERPRETER_FRAME_MIRROR_OFFSET = -3; // for native calls only
|
||||
private static final int INTERPRETER_FRAME_SENDER_SP_OFFSET = -4;
|
||||
private static final int INTERPRETER_FRAME_LAST_SP_OFFSET = INTERPRETER_FRAME_SENDER_SP_OFFSET - 1;
|
||||
private static final int INTERPRETER_FRAME_MDX_OFFSET = INTERPRETER_FRAME_LAST_SP_OFFSET -1;
|
||||
private static final int INTERPRETER_FRAME_ESP_OFFSET = INTERPRETER_FRAME_MDX_OFFSET - 1;
|
||||
private static final int INTERPRETER_FRAME_BCX_OFFSET = INTERPRETER_FRAME_ESP_OFFSET - 1;
|
||||
private static final int INTERPRETER_FRAME_CACHE_OFFSET =INTERPRETER_FRAME_BCX_OFFSET - 1;
|
||||
private static final int INTERPRETER_FRAME_MONITORS_OFFSET = INTERPRETER_FRAME_CACHE_OFFSET - 1;
|
||||
private static final int INTERPRETER_FRAME_LOCALS_OFFSET = INTERPRETER_FRAME_MONITORS_OFFSET - 1;
|
||||
private static final int INTERPRETER_FRAME_METHOD_OFFSET = INTERPRETER_FRAME_LOCALS_OFFSET - 1;
|
||||
private static final int INTERPRETER_FRAME_INITIAL_SP_OFFSET = INTERPRETER_FRAME_BCX_OFFSET - 1; // FIXME: probably wrong, but unused anyway
|
||||
private static final int INTERPRETER_FRAME_MONITOR_BLOCK_TOP_OFFSET = INTERPRETER_FRAME_INITIAL_SP_OFFSET;
|
||||
private static final int INTERPRETER_FRAME_MONITOR_BLOCK_BOTTOM_OFFSET = INTERPRETER_FRAME_INITIAL_SP_OFFSET;
|
||||
|
||||
// Entry frames
|
||||
private static int ENTRY_FRAME_CALL_WRAPPER_OFFSET;
|
||||
|
||||
// Native frames
|
||||
private static int NATIVE_FRAME_INITIAL_PARAM_OFFSET;
|
||||
|
||||
|
||||
static {
|
||||
VM.registerVMInitializedObserver(new Observer() {
|
||||
public void update(Observable o, Object data) {
|
||||
initialize(VM.getVM().getTypeDataBase());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static synchronized void initialize(TypeDataBase db) {
|
||||
int abi_minframe_size = db.lookupIntConstant("frame::abi_minframe_size").intValue();
|
||||
int entry_frame_locals_size = db.lookupIntConstant("frame::entry_frame_locals_size").intValue();
|
||||
int wordLength = (int) VM.getVM().getAddressSize();
|
||||
NATIVE_FRAME_INITIAL_PARAM_OFFSET = -abi_minframe_size/wordLength;
|
||||
ENTRY_FRAME_CALL_WRAPPER_OFFSET = -entry_frame_locals_size/wordLength;
|
||||
}
|
||||
|
||||
|
||||
// an additional field beyond sp and pc:
|
||||
Address raw_fp; // frame pointer
|
||||
private Address raw_unextendedSP;
|
||||
|
||||
private PPC64Frame() {
|
||||
}
|
||||
|
||||
private void adjustForDeopt() {
|
||||
if ( pc != null) {
|
||||
// Look for a deopt pc and if it is deopted convert to original pc
|
||||
CodeBlob cb = VM.getVM().getCodeCache().findBlob(pc);
|
||||
if (cb != null && cb.isJavaMethod()) {
|
||||
NMethod nm = (NMethod) cb;
|
||||
if (pc.equals(nm.deoptHandlerBegin())) {
|
||||
if (Assert.ASSERTS_ENABLED) {
|
||||
Assert.that(this.getUnextendedSP() != null, "null SP in Java frame");
|
||||
}
|
||||
// adjust pc if frame is deoptimized.
|
||||
pc = this.getUnextendedSP().getAddressAt(nm.origPCOffset());
|
||||
deoptimized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public PPC64Frame(Address raw_sp, Address raw_fp, Address pc) {
|
||||
this.raw_sp = raw_sp;
|
||||
this.raw_unextendedSP = raw_sp;
|
||||
if (raw_fp == null) {
|
||||
this.raw_fp = raw_sp.getAddressAt(0);
|
||||
} else {
|
||||
this.raw_fp = raw_fp;
|
||||
}
|
||||
if (pc == null) {
|
||||
this.pc = raw_sp.getAddressAt(2 * VM.getVM().getAddressSize());
|
||||
} else {
|
||||
this.pc = pc;
|
||||
}
|
||||
adjustUnextendedSP();
|
||||
|
||||
// Frame must be fully constructed before this call
|
||||
adjustForDeopt();
|
||||
|
||||
if (DEBUG) {
|
||||
System.out.println("PPC64Frame(sp, fp, pc): " + this);
|
||||
dumpStack();
|
||||
}
|
||||
}
|
||||
|
||||
public PPC64Frame(Address raw_sp, Address raw_fp) {
|
||||
this.raw_sp = raw_sp;
|
||||
this.raw_unextendedSP = raw_sp;
|
||||
if (raw_fp == null) {
|
||||
this.raw_fp = raw_sp.getAddressAt(0);
|
||||
} else {
|
||||
this.raw_fp = raw_fp;
|
||||
}
|
||||
this.pc = raw_sp.getAddressAt(2 * VM.getVM().getAddressSize());
|
||||
adjustUnextendedSP();
|
||||
|
||||
// Frame must be fully constructed before this call
|
||||
adjustForDeopt();
|
||||
|
||||
if (DEBUG) {
|
||||
System.out.println("PPC64Frame(sp, fp): " + this);
|
||||
dumpStack();
|
||||
}
|
||||
}
|
||||
|
||||
public PPC64Frame(Address raw_sp, Address raw_unextendedSp, Address raw_fp, Address pc) {
|
||||
this.raw_sp = raw_sp;
|
||||
this.raw_unextendedSP = raw_unextendedSp;
|
||||
if (raw_fp == null) {
|
||||
this.raw_fp = raw_sp.getAddressAt(0);
|
||||
} else {
|
||||
this.raw_fp = raw_fp;
|
||||
}
|
||||
if (pc == null) {
|
||||
this.pc = raw_sp.getAddressAt(2 * VM.getVM().getAddressSize());
|
||||
} else {
|
||||
this.pc = pc;
|
||||
}
|
||||
adjustUnextendedSP();
|
||||
|
||||
// Frame must be fully constructed before this call
|
||||
adjustForDeopt();
|
||||
|
||||
if (DEBUG) {
|
||||
System.out.println("PPC64Frame(sp, unextendedSP, fp, pc): " + this);
|
||||
dumpStack();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Object clone() {
|
||||
PPC64Frame frame = new PPC64Frame();
|
||||
frame.raw_sp = raw_sp;
|
||||
frame.raw_unextendedSP = raw_unextendedSP;
|
||||
frame.raw_fp = raw_fp;
|
||||
frame.pc = pc;
|
||||
frame.deoptimized = deoptimized;
|
||||
return frame;
|
||||
}
|
||||
|
||||
public boolean equals(Object arg) {
|
||||
if (arg == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(arg instanceof PPC64Frame)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PPC64Frame other = (PPC64Frame) arg;
|
||||
|
||||
return (AddressOps.equal(getSP(), other.getSP()) &&
|
||||
AddressOps.equal(getUnextendedSP(), other.getUnextendedSP()) &&
|
||||
AddressOps.equal(getFP(), other.getFP()) &&
|
||||
AddressOps.equal(getPC(), other.getPC()));
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
if (raw_sp == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return raw_sp.hashCode();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "sp: " + (getSP() == null ? "null" : getSP().toString()) +
|
||||
", unextendedSP: " + (getUnextendedSP() == null ? "null" : getUnextendedSP().toString()) +
|
||||
", fp: " + (getFP() == null ? "null" : getFP().toString()) +
|
||||
", pc: " + (pc == null ? "null" : pc.toString());
|
||||
}
|
||||
|
||||
// accessors for the instance variables
|
||||
public Address getFP() { return raw_fp; }
|
||||
public Address getSP() { return raw_sp; }
|
||||
public Address getID() { return raw_sp; }
|
||||
|
||||
// FIXME: not implemented yet (should be done for Solaris/PPC64)
|
||||
public boolean isSignalHandlerFrameDbg() { return false; }
|
||||
public int getSignalNumberDbg() { return 0; }
|
||||
public String getSignalNameDbg() { return null; }
|
||||
|
||||
public boolean isInterpretedFrameValid() {
|
||||
if (Assert.ASSERTS_ENABLED) {
|
||||
Assert.that(isInterpretedFrame(), "Not an interpreted frame");
|
||||
}
|
||||
|
||||
// These are reasonable sanity checks
|
||||
if (getFP() == null || getFP().andWithMask(0x3) != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (getSP() == null || getSP().andWithMask(0x3) != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// These are hacks to keep us out of trouble.
|
||||
// The problem with these is that they mask other problems
|
||||
if (getFP().lessThanOrEqual(getSP())) {
|
||||
// this attempts to deal with unsigned comparison above
|
||||
return false;
|
||||
}
|
||||
|
||||
if (getFP().minus(getSP()) > 4096 * VM.getVM().getAddressSize()) {
|
||||
// stack frames shouldn't be large.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// FIXME: not applicable in current system
|
||||
// void patch_pc(Thread* thread, address pc);
|
||||
|
||||
public Frame sender(RegisterMap regMap, CodeBlob cb) {
|
||||
PPC64RegisterMap map = (PPC64RegisterMap) regMap;
|
||||
|
||||
if (Assert.ASSERTS_ENABLED) {
|
||||
Assert.that(map != null, "map must be set");
|
||||
}
|
||||
|
||||
// Default is we done have to follow them. The sender_for_xxx will
|
||||
// update it accordingly
|
||||
map.setIncludeArgumentOops(false);
|
||||
|
||||
if (isEntryFrame()) return senderForEntryFrame(map);
|
||||
if (isInterpretedFrame()) return senderForInterpreterFrame(map);
|
||||
|
||||
if(cb == null) {
|
||||
cb = VM.getVM().getCodeCache().findBlob(getPC());
|
||||
} else {
|
||||
if (Assert.ASSERTS_ENABLED) {
|
||||
Assert.that(cb.equals(VM.getVM().getCodeCache().findBlob(getPC())), "Must be the same");
|
||||
}
|
||||
}
|
||||
|
||||
if (cb != null) {
|
||||
return senderForCompiledFrame(map, cb);
|
||||
}
|
||||
|
||||
// Must be native-compiled frame, i.e. the marshaling code for native
|
||||
// methods that exists in the core system.
|
||||
return new PPC64Frame(getSenderSP(), getLink(), getSenderPC());
|
||||
}
|
||||
|
||||
private Frame senderForEntryFrame(PPC64RegisterMap map) {
|
||||
if (DEBUG) {
|
||||
System.out.println("senderForEntryFrame");
|
||||
}
|
||||
if (Assert.ASSERTS_ENABLED) {
|
||||
Assert.that(map != null, "map must be set");
|
||||
}
|
||||
// Java frame called from C; skip all C frames and return top C
|
||||
// frame of that chunk as the sender
|
||||
PPC64JavaCallWrapper jcw = (PPC64JavaCallWrapper) getEntryFrameCallWrapper();
|
||||
if (Assert.ASSERTS_ENABLED) {
|
||||
Assert.that(!entryFrameIsFirst(), "next Java fp must be non zero");
|
||||
Assert.that(jcw.getLastJavaSP().greaterThan(getSP()), "must be above this frame on stack");
|
||||
}
|
||||
PPC64Frame fr;
|
||||
if (jcw.getLastJavaPC() != null) {
|
||||
fr = new PPC64Frame(jcw.getLastJavaSP(), jcw.getLastJavaFP(), jcw.getLastJavaPC());
|
||||
} else {
|
||||
fr = new PPC64Frame(jcw.getLastJavaSP(), jcw.getLastJavaFP());
|
||||
}
|
||||
map.clear();
|
||||
if (Assert.ASSERTS_ENABLED) {
|
||||
Assert.that(map.getIncludeArgumentOops(), "should be set by clear");
|
||||
}
|
||||
return fr;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// frame::adjust_unextended_sp
|
||||
private void adjustUnextendedSP() {
|
||||
raw_unextendedSP = getFP();
|
||||
}
|
||||
private Frame senderForInterpreterFrame(PPC64RegisterMap map) {
|
||||
if (DEBUG) {
|
||||
System.out.println("senderForInterpreterFrame");
|
||||
}
|
||||
Address unextendedSP = addressOfStackSlot(INTERPRETER_FRAME_SENDER_SP_OFFSET).getAddressAt(0);
|
||||
Address sp = getSenderSP();
|
||||
|
||||
return new PPC64Frame(sp, unextendedSP, getLink(), getSenderPC());
|
||||
}
|
||||
|
||||
|
||||
private Frame senderForCompiledFrame(PPC64RegisterMap map, CodeBlob cb) {
|
||||
if (DEBUG) {
|
||||
System.out.println("senderForCompiledFrame");
|
||||
}
|
||||
|
||||
//
|
||||
// NOTE: some of this code is (unfortunately) duplicated in PPC64CurrentFrameGuess
|
||||
//
|
||||
|
||||
if (Assert.ASSERTS_ENABLED) {
|
||||
Assert.that(map != null, "map must be set");
|
||||
}
|
||||
|
||||
// frame owned by optimizing compiler
|
||||
if (Assert.ASSERTS_ENABLED) {
|
||||
Assert.that(cb.getFrameSize() >= 0, "must have non-zero frame size");
|
||||
}
|
||||
Address senderSP = getSenderSP();
|
||||
|
||||
Address senderPC = getSenderPC();
|
||||
|
||||
if (map.getUpdateMap()) {
|
||||
// Tell GC to use argument oopmaps for some runtime stubs that need it.
|
||||
// For C1, the runtime stub might not have oop maps, so set this flag
|
||||
// outside of update_register_map.
|
||||
map.setIncludeArgumentOops(cb.callerMustGCArguments());
|
||||
|
||||
if (cb.getOopMaps() != null) {
|
||||
OopMapSet.updateRegisterMap(this, cb, map, true);
|
||||
}
|
||||
}
|
||||
|
||||
return new PPC64Frame(senderSP, getLink(), senderPC);
|
||||
}
|
||||
|
||||
protected boolean hasSenderPD() {
|
||||
// FIXME
|
||||
return true;
|
||||
}
|
||||
|
||||
public long frameSize() {
|
||||
return (getSenderSP().minus(getSP()) / VM.getVM().getAddressSize());
|
||||
}
|
||||
|
||||
public Address getLink() {
|
||||
return getSenderSP().getAddressAt(0);
|
||||
}
|
||||
|
||||
public Address getUnextendedSP() { return raw_unextendedSP; }
|
||||
|
||||
// Return address:
|
||||
public Address getSenderPC() { return getSenderSP().getAddressAt(2 * VM.getVM().getAddressSize()); }
|
||||
|
||||
// return address of param, zero origin index.
|
||||
// MPJ note: Appears to be unused.
|
||||
public Address getNativeParamAddr(int idx) {
|
||||
return null;
|
||||
// return addressOfStackSlot(NATIVE_FRAME_INITIAL_PARAM_OFFSET + idx);
|
||||
}
|
||||
|
||||
public Address getSenderSP() { return getFP(); }
|
||||
public Address addressOfInterpreterFrameLocals() {
|
||||
return addressOfStackSlot(INTERPRETER_FRAME_LOCALS_OFFSET);
|
||||
}
|
||||
|
||||
private Address addressOfInterpreterFrameBCX() {
|
||||
return addressOfStackSlot(INTERPRETER_FRAME_BCX_OFFSET);
|
||||
}
|
||||
|
||||
public int getInterpreterFrameBCI() {
|
||||
// FIXME: this is not atomic with respect to GC and is unsuitable
|
||||
// for use in a non-debugging, or reflective, system. Need to
|
||||
// figure out how to express this.
|
||||
Address bcp = addressOfInterpreterFrameBCX().getAddressAt(0);
|
||||
Address methodHandle = addressOfInterpreterFrameMethod().getAddressAt(0);
|
||||
Method method = (Method)Metadata.instantiateWrapperFor(methodHandle);
|
||||
return bcpToBci(bcp, method);
|
||||
}
|
||||
|
||||
public Address addressOfInterpreterFrameMDX() {
|
||||
return addressOfStackSlot(INTERPRETER_FRAME_MDX_OFFSET);
|
||||
}
|
||||
|
||||
// FIXME
|
||||
//inline int frame::interpreter_frame_monitor_size() {
|
||||
// return BasicObjectLock::size();
|
||||
//}
|
||||
|
||||
// expression stack
|
||||
// (the max_stack arguments are used by the GC; see class FrameClosure)
|
||||
|
||||
public Address addressOfInterpreterFrameExpressionStack() {
|
||||
Address monitorEnd = interpreterFrameMonitorEnd().address();
|
||||
return monitorEnd.addOffsetTo(-1 * VM.getVM().getAddressSize());
|
||||
}
|
||||
|
||||
public int getInterpreterFrameExpressionStackDirection() { return -1; }
|
||||
|
||||
// top of expression stack
|
||||
public Address addressOfInterpreterFrameTOS() {
|
||||
return getSP();
|
||||
}
|
||||
|
||||
/** Expression stack from top down */
|
||||
public Address addressOfInterpreterFrameTOSAt(int slot) {
|
||||
return addressOfInterpreterFrameTOS().addOffsetTo(slot * VM.getVM().getAddressSize());
|
||||
}
|
||||
|
||||
public Address getInterpreterFrameSenderSP() {
|
||||
if (Assert.ASSERTS_ENABLED) {
|
||||
Assert.that(isInterpretedFrame(), "interpreted frame expected");
|
||||
}
|
||||
return addressOfStackSlot(INTERPRETER_FRAME_SENDER_SP_OFFSET).getAddressAt(0);
|
||||
}
|
||||
|
||||
// Monitors
|
||||
public BasicObjectLock interpreterFrameMonitorBegin() {
|
||||
return new BasicObjectLock(addressOfStackSlot(INTERPRETER_FRAME_MONITOR_BLOCK_BOTTOM_OFFSET));
|
||||
}
|
||||
|
||||
public BasicObjectLock interpreterFrameMonitorEnd() {
|
||||
Address result = addressOfStackSlot(INTERPRETER_FRAME_MONITOR_BLOCK_TOP_OFFSET).getAddressAt(0);
|
||||
if (Assert.ASSERTS_ENABLED) {
|
||||
// make sure the pointer points inside the frame
|
||||
Assert.that(AddressOps.gt(getFP(), result), "result must < than frame pointer");
|
||||
Assert.that(AddressOps.lte(getSP(), result), "result must >= than stack pointer");
|
||||
}
|
||||
return new BasicObjectLock(result);
|
||||
}
|
||||
|
||||
public int interpreterFrameMonitorSize() {
|
||||
return BasicObjectLock.size();
|
||||
}
|
||||
|
||||
// Method
|
||||
public Address addressOfInterpreterFrameMethod() {
|
||||
return addressOfStackSlot(INTERPRETER_FRAME_METHOD_OFFSET);
|
||||
}
|
||||
|
||||
// Constant pool cache
|
||||
public Address addressOfInterpreterFrameCPCache() {
|
||||
return addressOfStackSlot(INTERPRETER_FRAME_CACHE_OFFSET);
|
||||
}
|
||||
|
||||
// Entry frames
|
||||
public JavaCallWrapper getEntryFrameCallWrapper() {
|
||||
return new PPC64JavaCallWrapper(addressOfStackSlot(ENTRY_FRAME_CALL_WRAPPER_OFFSET).getAddressAt(0));
|
||||
}
|
||||
|
||||
protected Address addressOfSavedOopResult() {
|
||||
// offset is 2 for compiler2 and 3 for compiler1
|
||||
return getSP().addOffsetTo((VM.getVM().isClientCompiler() ? 2 : 3) *
|
||||
VM.getVM().getAddressSize());
|
||||
}
|
||||
|
||||
protected Address addressOfSavedReceiver() {
|
||||
return getSP().addOffsetTo(-4 * VM.getVM().getAddressSize());
|
||||
}
|
||||
|
||||
private void dumpStack() {
|
||||
if (getFP() != null) {
|
||||
for (Address addr = getSP().addOffsetTo(-5 * VM.getVM().getAddressSize());
|
||||
AddressOps.lte(addr, getFP().addOffsetTo(5 * VM.getVM().getAddressSize()));
|
||||
addr = addr.addOffsetTo(VM.getVM().getAddressSize())) {
|
||||
System.out.println(addr + ": " + addr.getAddressAt(0));
|
||||
}
|
||||
} else {
|
||||
for (Address addr = getSP().addOffsetTo(-5 * VM.getVM().getAddressSize());
|
||||
AddressOps.lte(addr, getSP().addOffsetTo(20 * VM.getVM().getAddressSize()));
|
||||
addr = addr.addOffsetTo(VM.getVM().getAddressSize())) {
|
||||
System.out.println(addr + ": " + addr.getAddressAt(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. 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 sun.jvm.hotspot.runtime.ppc64;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import sun.jvm.hotspot.debugger.*;
|
||||
import sun.jvm.hotspot.types.*;
|
||||
import sun.jvm.hotspot.runtime.*;
|
||||
|
||||
public class PPC64JavaCallWrapper extends JavaCallWrapper {
|
||||
|
||||
public PPC64JavaCallWrapper(Address addr) {
|
||||
super(addr);
|
||||
}
|
||||
|
||||
public Address getLastJavaFP() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (c) 20014, Oracle and/or its affiliates. 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 sun.jvm.hotspot.runtime.ppc64;
|
||||
|
||||
import sun.jvm.hotspot.debugger.*;
|
||||
import sun.jvm.hotspot.runtime.*;
|
||||
|
||||
public class PPC64RegisterMap extends RegisterMap {
|
||||
|
||||
/** This is the only public constructor */
|
||||
public PPC64RegisterMap(JavaThread thread, boolean updateMap) {
|
||||
super(thread, updateMap);
|
||||
}
|
||||
|
||||
protected PPC64RegisterMap(RegisterMap map) {
|
||||
super(map);
|
||||
}
|
||||
|
||||
public Object clone() {
|
||||
PPC64RegisterMap retval = new PPC64RegisterMap(this);
|
||||
return retval;
|
||||
}
|
||||
|
||||
// no PD state to clear or copy:
|
||||
protected void clearPD() {}
|
||||
protected void initializePD() {}
|
||||
protected void initializeFromPD(RegisterMap map) {}
|
||||
protected Address getLocationPD(VMReg reg) { return null; }
|
||||
}
|
||||
@ -46,7 +46,9 @@ CFLAGS += -qsuppress=1540-0198
|
||||
# - 1540-1090 (I) The destructor of "..." might not be called.
|
||||
# - 1500-010: (W) WARNING in ...: Infinite loop. Program may not stop.
|
||||
# There are several infinite loops in the vm, suppress.
|
||||
CFLAGS += -qsuppress=1540-1090 -qsuppress=1500-010
|
||||
# - 1540-1639 (I) The behavior of long type bit fields has changed ...
|
||||
# ... long type bit fields now default to long, not int.
|
||||
CFLAGS += -qsuppress=1540-1090 -qsuppress=1500-010 -qsuppress=1540-1639
|
||||
|
||||
# Suppress
|
||||
# - 540-1088 (W) The exception specification is being ignored.
|
||||
|
||||
@ -124,7 +124,7 @@ STATIC_STDCXX = -Wl,-lC_r
|
||||
# MAPFLAG = -Xlinker --version-script=FILENAME
|
||||
|
||||
# Build shared library
|
||||
SHARED_FLAG = -q64 -b64 -bexpall -G -bnoentry -qmkshrobj -brtl -bnolibpath
|
||||
SHARED_FLAG = -q64 -b64 -bexpall -G -bnoentry -qmkshrobj -brtl -bnolibpath -bernotok
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
# Debug flags
|
||||
|
||||
@ -109,6 +109,7 @@ $(GENERATED)/sa-jdi.jar:: $(AGENT_FILES)
|
||||
$(QUIETLY) $(REMOTE) $(RUN.JAVAH) -classpath $(SA_CLASSDIR) -d $(GENERATED) -jni sun.jvm.hotspot.debugger.x86.X86ThreadContext
|
||||
$(QUIETLY) $(REMOTE) $(RUN.JAVAH) -classpath $(SA_CLASSDIR) -d $(GENERATED) -jni sun.jvm.hotspot.debugger.amd64.AMD64ThreadContext
|
||||
$(QUIETLY) $(REMOTE) $(RUN.JAVAH) -classpath $(SA_CLASSDIR) -d $(GENERATED) -jni sun.jvm.hotspot.debugger.sparc.SPARCThreadContext
|
||||
$(QUIETLY) $(REMOTE) $(RUN.JAVAH) -classpath $(SA_CLASSDIR) -d $(GENERATED) -jni sun.jvm.hotspot.debugger.ppc64.PPC64ThreadContext
|
||||
$(QUIETLY) $(REMOTE) $(RUN.JAVAH) -classpath $(SA_CLASSDIR) -d $(GENERATED) -jni sun.jvm.hotspot.asm.Disassembler
|
||||
|
||||
clean:
|
||||
|
||||
@ -51,16 +51,20 @@ $(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/cdbg/basic/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/dummy/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/linux/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/linux/amd64/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/linux/ppc64/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/linux/x86/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/linux/sparc/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/posix/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/posix/elf/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/ppc64/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/proc/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/proc/amd64/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/proc/ppc64/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/proc/sparc/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/proc/x86/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/remote/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/remote/amd64/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/remote/ppc64/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/remote/sparc/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/remote/x86/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/sparc/*.java \
|
||||
@ -90,12 +94,14 @@ $(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/linux/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/linux_amd64/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/linux_x86/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/linux_sparc/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/linux_ppc64/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/posix/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/solaris_amd64/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/solaris_sparc/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/solaris_x86/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/sparc/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/x86/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/ppc64/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/tools/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/tools/jcore/*.java \
|
||||
$(AGENT_SRC_DIR)/sun/jvm/hotspot/tools/soql/*.java \
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright 2012, 2013 SAP AG. All rights reserved.
|
||||
* Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright 2012, 2015 SAP AG. 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
|
||||
@ -284,19 +284,20 @@ class Assembler : public AbstractAssembler {
|
||||
MTCTR_OPCODE = (MTSPR_OPCODE | 9 << SPR_0_4_SHIFT),
|
||||
MFCTR_OPCODE = (MFSPR_OPCODE | 9 << SPR_0_4_SHIFT),
|
||||
|
||||
MTTFHAR_OPCODE = (MTSPR_OPCODE | 128 << SPR_0_4_SHIFT),
|
||||
MFTFHAR_OPCODE = (MFSPR_OPCODE | 128 << SPR_0_4_SHIFT),
|
||||
MTTFIAR_OPCODE = (MTSPR_OPCODE | 129 << SPR_0_4_SHIFT),
|
||||
MFTFIAR_OPCODE = (MFSPR_OPCODE | 129 << SPR_0_4_SHIFT),
|
||||
MTTEXASR_OPCODE = (MTSPR_OPCODE | 130 << SPR_0_4_SHIFT),
|
||||
MFTEXASR_OPCODE = (MFSPR_OPCODE | 130 << SPR_0_4_SHIFT),
|
||||
MTTEXASRU_OPCODE = (MTSPR_OPCODE | 131 << SPR_0_4_SHIFT),
|
||||
MFTEXASRU_OPCODE = (MFSPR_OPCODE | 131 << SPR_0_4_SHIFT),
|
||||
// Attention: Higher and lower half are inserted in reversed order.
|
||||
MTTFHAR_OPCODE = (MTSPR_OPCODE | 4 << SPR_5_9_SHIFT | 0 << SPR_0_4_SHIFT),
|
||||
MFTFHAR_OPCODE = (MFSPR_OPCODE | 4 << SPR_5_9_SHIFT | 0 << SPR_0_4_SHIFT),
|
||||
MTTFIAR_OPCODE = (MTSPR_OPCODE | 4 << SPR_5_9_SHIFT | 1 << SPR_0_4_SHIFT),
|
||||
MFTFIAR_OPCODE = (MFSPR_OPCODE | 4 << SPR_5_9_SHIFT | 1 << SPR_0_4_SHIFT),
|
||||
MTTEXASR_OPCODE = (MTSPR_OPCODE | 4 << SPR_5_9_SHIFT | 2 << SPR_0_4_SHIFT),
|
||||
MFTEXASR_OPCODE = (MFSPR_OPCODE | 4 << SPR_5_9_SHIFT | 2 << SPR_0_4_SHIFT),
|
||||
MTTEXASRU_OPCODE = (MTSPR_OPCODE | 4 << SPR_5_9_SHIFT | 3 << SPR_0_4_SHIFT),
|
||||
MFTEXASRU_OPCODE = (MFSPR_OPCODE | 4 << SPR_5_9_SHIFT | 3 << SPR_0_4_SHIFT),
|
||||
|
||||
MTVRSAVE_OPCODE = (MTSPR_OPCODE | 256 << SPR_0_4_SHIFT),
|
||||
MFVRSAVE_OPCODE = (MFSPR_OPCODE | 256 << SPR_0_4_SHIFT),
|
||||
MTVRSAVE_OPCODE = (MTSPR_OPCODE | 8 << SPR_5_9_SHIFT | 0 << SPR_0_4_SHIFT),
|
||||
MFVRSAVE_OPCODE = (MFSPR_OPCODE | 8 << SPR_5_9_SHIFT | 0 << SPR_0_4_SHIFT),
|
||||
|
||||
MFTB_OPCODE = (MFSPR_OPCODE | 268 << SPR_0_4_SHIFT),
|
||||
MFTB_OPCODE = (MFSPR_OPCODE | 8 << SPR_5_9_SHIFT | 12 << SPR_0_4_SHIFT),
|
||||
|
||||
MTCRF_OPCODE = (31u << OPCODE_SHIFT | 144u << 1),
|
||||
MFCR_OPCODE = (31u << OPCODE_SHIFT | 19u << 1),
|
||||
@ -1494,6 +1495,26 @@ class Assembler : public AbstractAssembler {
|
||||
inline void mftexasr(Register d);
|
||||
inline void mftexasru(Register d);
|
||||
|
||||
// TEXASR bit description
|
||||
enum transaction_failure_reason {
|
||||
// Upper half (TEXASRU):
|
||||
tm_failure_persistent = 7, // The failure is likely to recur on each execution.
|
||||
tm_disallowed = 8, // The instruction is not permitted.
|
||||
tm_nesting_of = 9, // The maximum transaction level was exceeded.
|
||||
tm_footprint_of = 10, // The tracking limit for transactional storage accesses was exceeded.
|
||||
tm_self_induced_cf = 11, // A self-induced conflict occurred in Suspended state.
|
||||
tm_non_trans_cf = 12, // A conflict occurred with a non-transactional access by another processor.
|
||||
tm_trans_cf = 13, // A conflict occurred with another transaction.
|
||||
tm_translation_cf = 14, // A conflict occurred with a TLB invalidation.
|
||||
tm_inst_fetch_cf = 16, // An instruction fetch was performed from a block that was previously written transactionally.
|
||||
tm_tabort = 31, // Termination was caused by the execution of an abort instruction.
|
||||
// Lower half:
|
||||
tm_suspended = 32, // Failure was recorded in Suspended state.
|
||||
tm_failure_summary = 36, // Failure has been detected and recorded.
|
||||
tm_tfiar_exact = 37, // Value in the TFIAR is exact.
|
||||
tm_rot = 38, // Rollback-only transaction.
|
||||
};
|
||||
|
||||
// PPC 1, section 2.4.1 Branch Instructions
|
||||
inline void b( address a, relocInfo::relocType rt = relocInfo::none);
|
||||
inline void b( Label& L);
|
||||
@ -1581,6 +1602,7 @@ class Assembler : public AbstractAssembler {
|
||||
inline void bnectrl(ConditionRegister crx, relocInfo::relocType rt = relocInfo::none);
|
||||
|
||||
// condition register logic instructions
|
||||
// NOTE: There's a preferred form: d and s2 should point into the same condition register.
|
||||
inline void crand( int d, int s1, int s2);
|
||||
inline void crnand(int d, int s1, int s2);
|
||||
inline void cror( int d, int s1, int s2);
|
||||
@ -1590,6 +1612,19 @@ class Assembler : public AbstractAssembler {
|
||||
inline void crandc(int d, int s1, int s2);
|
||||
inline void crorc( int d, int s1, int s2);
|
||||
|
||||
// More convenient version.
|
||||
int condition_register_bit(ConditionRegister cr, Condition c) {
|
||||
return 4 * (int)(intptr_t)cr + c;
|
||||
}
|
||||
void crand( ConditionRegister crdst, Condition cdst, ConditionRegister crsrc, Condition csrc);
|
||||
void crnand(ConditionRegister crdst, Condition cdst, ConditionRegister crsrc, Condition csrc);
|
||||
void cror( ConditionRegister crdst, Condition cdst, ConditionRegister crsrc, Condition csrc);
|
||||
void crxor( ConditionRegister crdst, Condition cdst, ConditionRegister crsrc, Condition csrc);
|
||||
void crnor( ConditionRegister crdst, Condition cdst, ConditionRegister crsrc, Condition csrc);
|
||||
void creqv( ConditionRegister crdst, Condition cdst, ConditionRegister crsrc, Condition csrc);
|
||||
void crandc(ConditionRegister crdst, Condition cdst, ConditionRegister crsrc, Condition csrc);
|
||||
void crorc( ConditionRegister crdst, Condition cdst, ConditionRegister crsrc, Condition csrc);
|
||||
|
||||
// icache and dcache related instructions
|
||||
inline void icbi( Register s1, Register s2);
|
||||
//inline void dcba(Register s1, Register s2); // Instruction for embedded processor only.
|
||||
@ -1673,6 +1708,10 @@ class Assembler : public AbstractAssembler {
|
||||
inline void smt_prio_low();
|
||||
inline void smt_prio_medium_low();
|
||||
inline void smt_prio_medium();
|
||||
// >= Power7
|
||||
inline void smt_yield();
|
||||
inline void smt_mdoio();
|
||||
inline void smt_mdoom();
|
||||
|
||||
// trap instructions
|
||||
inline void twi_0(Register a); // for load with acquire semantics use load+twi_0+isync (trap can't occur)
|
||||
@ -1958,6 +1997,7 @@ class Assembler : public AbstractAssembler {
|
||||
inline void tbeginrot_(); // R=1 Rollback-Only Transaction
|
||||
inline void tend_(); // A=0
|
||||
inline void tendall_(); // A=1
|
||||
inline void tabort_();
|
||||
inline void tabort_(Register a);
|
||||
inline void tabortwc_(int t, Register a, Register b);
|
||||
inline void tabortwci_(int t, Register a, int si);
|
||||
@ -1967,6 +2007,10 @@ class Assembler : public AbstractAssembler {
|
||||
inline void tresume_(); // tsr with L=1
|
||||
inline void tcheck(int f);
|
||||
|
||||
static bool is_tbegin(int x) {
|
||||
return TBEGIN_OPCODE == (x & (0x3f << OPCODE_SHIFT | 0x3ff << 1));
|
||||
}
|
||||
|
||||
// The following encoders use r0 as second operand. These instructions
|
||||
// read r0 as '0'.
|
||||
inline void lwzx( Register d, Register s2);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright 2012, 2014 SAP AG. All rights reserved.
|
||||
* Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright 2012, 2015 SAP AG. 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
|
||||
@ -453,6 +453,48 @@ inline void Assembler::creqv( int d, int s1, int s2) { emit_int32(CREQV_OPCODE
|
||||
inline void Assembler::crandc(int d, int s1, int s2) { emit_int32(CRANDC_OPCODE | bt(d) | ba(s1) | bb(s2)); }
|
||||
inline void Assembler::crorc( int d, int s1, int s2) { emit_int32(CRORC_OPCODE | bt(d) | ba(s1) | bb(s2)); }
|
||||
|
||||
// More convenient version.
|
||||
inline void Assembler::crand( ConditionRegister crdst, Condition cdst, ConditionRegister crsrc, Condition csrc) {
|
||||
int dst_bit = condition_register_bit(crdst, cdst),
|
||||
src_bit = condition_register_bit(crsrc, csrc);
|
||||
crand(dst_bit, src_bit, dst_bit);
|
||||
}
|
||||
inline void Assembler::crnand(ConditionRegister crdst, Condition cdst, ConditionRegister crsrc, Condition csrc) {
|
||||
int dst_bit = condition_register_bit(crdst, cdst),
|
||||
src_bit = condition_register_bit(crsrc, csrc);
|
||||
crnand(dst_bit, src_bit, dst_bit);
|
||||
}
|
||||
inline void Assembler::cror( ConditionRegister crdst, Condition cdst, ConditionRegister crsrc, Condition csrc) {
|
||||
int dst_bit = condition_register_bit(crdst, cdst),
|
||||
src_bit = condition_register_bit(crsrc, csrc);
|
||||
cror(dst_bit, src_bit, dst_bit);
|
||||
}
|
||||
inline void Assembler::crxor( ConditionRegister crdst, Condition cdst, ConditionRegister crsrc, Condition csrc) {
|
||||
int dst_bit = condition_register_bit(crdst, cdst),
|
||||
src_bit = condition_register_bit(crsrc, csrc);
|
||||
crxor(dst_bit, src_bit, dst_bit);
|
||||
}
|
||||
inline void Assembler::crnor( ConditionRegister crdst, Condition cdst, ConditionRegister crsrc, Condition csrc) {
|
||||
int dst_bit = condition_register_bit(crdst, cdst),
|
||||
src_bit = condition_register_bit(crsrc, csrc);
|
||||
crnor(dst_bit, src_bit, dst_bit);
|
||||
}
|
||||
inline void Assembler::creqv( ConditionRegister crdst, Condition cdst, ConditionRegister crsrc, Condition csrc) {
|
||||
int dst_bit = condition_register_bit(crdst, cdst),
|
||||
src_bit = condition_register_bit(crsrc, csrc);
|
||||
creqv(dst_bit, src_bit, dst_bit);
|
||||
}
|
||||
inline void Assembler::crandc(ConditionRegister crdst, Condition cdst, ConditionRegister crsrc, Condition csrc) {
|
||||
int dst_bit = condition_register_bit(crdst, cdst),
|
||||
src_bit = condition_register_bit(crsrc, csrc);
|
||||
crandc(dst_bit, src_bit, dst_bit);
|
||||
}
|
||||
inline void Assembler::crorc( ConditionRegister crdst, Condition cdst, ConditionRegister crsrc, Condition csrc) {
|
||||
int dst_bit = condition_register_bit(crdst, cdst),
|
||||
src_bit = condition_register_bit(crsrc, csrc);
|
||||
crorc(dst_bit, src_bit, dst_bit);
|
||||
}
|
||||
|
||||
// Conditional move (>= Power7)
|
||||
inline void Assembler::isel(Register d, ConditionRegister cr, Condition cc, bool inv, Register a, Register b) {
|
||||
if (b == noreg) {
|
||||
@ -516,6 +558,10 @@ inline void Assembler::smt_prio_medium_low() { Assembler::or_unchecked(R6, R6,
|
||||
inline void Assembler::smt_prio_medium() { Assembler::or_unchecked(R2, R2, R2); }
|
||||
inline void Assembler::smt_prio_medium_high() { Assembler::or_unchecked(R5, R5, R5); }
|
||||
inline void Assembler::smt_prio_high() { Assembler::or_unchecked(R3, R3, R3); }
|
||||
// >= Power7
|
||||
inline void Assembler::smt_yield() { Assembler::or_unchecked(R27, R27, R27); }
|
||||
inline void Assembler::smt_mdoio() { Assembler::or_unchecked(R29, R29, R29); }
|
||||
inline void Assembler::smt_mdoom() { Assembler::or_unchecked(R30, R30, R30); }
|
||||
|
||||
inline void Assembler::twi_0(Register a) { twi_unchecked(0, a, 0);}
|
||||
|
||||
@ -778,7 +824,8 @@ inline void Assembler::tbegin_() { emit_int32( TB
|
||||
inline void Assembler::tbeginrot_() { emit_int32( TBEGIN_OPCODE | /*R=1*/ 1u << (31-10) | rc(1)); }
|
||||
inline void Assembler::tend_() { emit_int32( TEND_OPCODE | rc(1)); }
|
||||
inline void Assembler::tendall_() { emit_int32( TEND_OPCODE | /*A=1*/ 1u << (31-6) | rc(1)); }
|
||||
inline void Assembler::tabort_(Register a) { emit_int32( TABORT_OPCODE | ra(a) | rc(1)); }
|
||||
inline void Assembler::tabort_() { emit_int32( TABORT_OPCODE | rc(1)); }
|
||||
inline void Assembler::tabort_(Register a) { assert(a != R0, "r0 not allowed"); emit_int32( TABORT_OPCODE | ra(a) | rc(1)); }
|
||||
inline void Assembler::tabortwc_(int t, Register a, Register b) { emit_int32( TABORTWC_OPCODE | to(t) | ra(a) | rb(b) | rc(1)); }
|
||||
inline void Assembler::tabortwci_(int t, Register a, int si) { emit_int32( TABORTWCI_OPCODE | to(t) | ra(a) | sh1620(si) | rc(1)); }
|
||||
inline void Assembler::tabortdc_(int t, Register a, Register b) { emit_int32( TABORTDC_OPCODE | to(t) | ra(a) | rb(b) | rc(1)); }
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright 2012, 2014 SAP AG. All rights reserved.
|
||||
* Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright 2012, 2015 SAP AG. 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
|
||||
@ -1712,7 +1712,7 @@ void InterpreterMacroAssembler::profile_obj_type(Register obj, Register mdo_addr
|
||||
andi_(R0, klass, TypeEntries::type_unknown);
|
||||
// Already unknown. Nothing to do anymore.
|
||||
//bne(CCR0, do_nothing);
|
||||
crorc(/*CCR0 eq*/2, /*CCR1 eq*/4+2, /*CCR0 eq*/2); // cr0 eq = cr1 eq or cr0 ne
|
||||
crorc(CCR0, Assembler::equal, CCR1, Assembler::equal); // cr0 eq = cr1 eq or cr0 ne
|
||||
beq(CCR0, do_nothing);
|
||||
|
||||
clrrdi_(R0, tmp, exact_log2(-TypeEntries::type_mask));
|
||||
@ -1826,9 +1826,9 @@ void InterpreterMacroAssembler::profile_return_type(Register ret, Register tmp1,
|
||||
lbz(tmp2, Method::intrinsic_id_offset_in_bytes(), R19_method);
|
||||
cmpwi(CCR0, tmp1, Bytecodes::_invokedynamic);
|
||||
cmpwi(CCR1, tmp1, Bytecodes::_invokehandle);
|
||||
cror(/*CR0 eq*/2, /*CR1 eq*/4+2, /*CR0 eq*/2);
|
||||
cror(CCR0, Assembler::equal, CCR1, Assembler::equal);
|
||||
cmpwi(CCR1, tmp2, vmIntrinsics::_compiledLambdaForm);
|
||||
cror(/*CR0 eq*/2, /*CR1 eq*/4+2, /*CR0 eq*/2);
|
||||
cror(CCR0, Assembler::equal, CCR1, Assembler::equal);
|
||||
bne(CCR0, profile_continue);
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright 2012, 2014 SAP AG. All rights reserved.
|
||||
* Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright 2012, 2015 SAP AG. 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
|
||||
@ -1079,7 +1079,7 @@ class StubGenerator: public StubCodeGenerator {
|
||||
__ sldi(tmp2, R5_ARG3, log2_elem_size); // size in bytes
|
||||
__ cmpld(CCR0, R3_ARG1, R4_ARG2); // Use unsigned comparison!
|
||||
__ cmpld(CCR1, tmp1, tmp2);
|
||||
__ crand(/*CCR0 lt*/0, /*CCR1 lt*/4+0, /*CCR0 lt*/0);
|
||||
__ crand(CCR0, Assembler::less, CCR1, Assembler::less);
|
||||
__ blt(CCR0, l_overlap); // Src before dst and distance smaller than size.
|
||||
|
||||
// need to copy forwards
|
||||
|
||||
@ -264,11 +264,11 @@ void TemplateInterpreterGenerator::generate_counter_incr(Label* overflow, Label*
|
||||
__ cmpdi(CCR0, Rmdo, 0);
|
||||
__ beq(CCR0, no_mdo);
|
||||
|
||||
// Increment backedge counter in the MDO.
|
||||
const int mdo_bc_offs = in_bytes(MethodData::backedge_counter_offset()) + in_bytes(InvocationCounter::counter_offset());
|
||||
__ lwz(Rscratch2, mdo_bc_offs, Rmdo);
|
||||
// Increment invocation counter in the MDO.
|
||||
const int mdo_ic_offs = in_bytes(MethodData::invocation_counter_offset()) + in_bytes(InvocationCounter::counter_offset());
|
||||
__ lwz(Rscratch2, mdo_ic_offs, Rmdo);
|
||||
__ addi(Rscratch2, Rscratch2, increment);
|
||||
__ stw(Rscratch2, mdo_bc_offs, Rmdo);
|
||||
__ stw(Rscratch2, mdo_ic_offs, Rmdo);
|
||||
__ load_const_optimized(Rscratch1, mask, R0);
|
||||
__ and_(Rscratch1, Rscratch2, Rscratch1);
|
||||
__ bne(CCR0, done);
|
||||
@ -276,12 +276,12 @@ void TemplateInterpreterGenerator::generate_counter_incr(Label* overflow, Label*
|
||||
}
|
||||
|
||||
// Increment counter in MethodCounters*.
|
||||
const int mo_bc_offs = in_bytes(MethodCounters::backedge_counter_offset()) + in_bytes(InvocationCounter::counter_offset());
|
||||
const int mo_ic_offs = in_bytes(MethodCounters::invocation_counter_offset()) + in_bytes(InvocationCounter::counter_offset());
|
||||
__ bind(no_mdo);
|
||||
__ get_method_counters(R19_method, R3_counters, done);
|
||||
__ lwz(Rscratch2, mo_bc_offs, R3_counters);
|
||||
__ lwz(Rscratch2, mo_ic_offs, R3_counters);
|
||||
__ addi(Rscratch2, Rscratch2, increment);
|
||||
__ stw(Rscratch2, mo_bc_offs, R3_counters);
|
||||
__ stw(Rscratch2, mo_ic_offs, R3_counters);
|
||||
__ load_const_optimized(Rscratch1, mask, R0);
|
||||
__ and_(Rscratch1, Rscratch2, Rscratch1);
|
||||
__ beq(CCR0, *overflow);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright 2013, 2014 SAP AG. All rights reserved.
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright 2013, 2015 SAP AG. 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
|
||||
@ -335,11 +335,11 @@ void TemplateTable::ldc(bool wide) {
|
||||
|
||||
__ cmpwi(CCR0, Rscratch2, JVM_CONSTANT_UnresolvedClass); // Unresolved class?
|
||||
__ cmpwi(CCR1, Rscratch2, JVM_CONSTANT_UnresolvedClassInError); // Unresolved class in error state?
|
||||
__ cror(/*CR0 eq*/2, /*CR1 eq*/4+2, /*CR0 eq*/2);
|
||||
__ cror(CCR0, Assembler::equal, CCR1, Assembler::equal);
|
||||
|
||||
// Resolved class - need to call vm to get java mirror of the class.
|
||||
__ cmpwi(CCR1, Rscratch2, JVM_CONSTANT_Class);
|
||||
__ crnor(/*CR0 eq*/2, /*CR1 eq*/4+2, /*CR0 eq*/2); // Neither resolved class nor unresolved case from above?
|
||||
__ crnor(CCR0, Assembler::equal, CCR1, Assembler::equal); // Neither resolved class nor unresolved case from above?
|
||||
__ beq(CCR0, notClass);
|
||||
|
||||
__ li(R4, wide ? 1 : 0);
|
||||
@ -2611,7 +2611,7 @@ void TemplateTable::jvmti_post_field_mod(Register Rcache, Register Rscratch, boo
|
||||
__ cmpwi(CCR0, Rflags, ltos);
|
||||
__ cmpwi(CCR1, Rflags, dtos);
|
||||
__ addi(base, R15_esp, Interpreter::expr_offset_in_bytes(1));
|
||||
__ crnor(/*CR0 eq*/2, /*CR1 eq*/4+2, /*CR0 eq*/2);
|
||||
__ crnor(CCR0, Assembler::equal, CCR1, Assembler::equal);
|
||||
__ beq(CCR0, is_one_slot);
|
||||
__ addi(base, R15_esp, Interpreter::expr_offset_in_bytes(2));
|
||||
__ bind(is_one_slot);
|
||||
@ -3563,7 +3563,7 @@ void TemplateTable::_new() {
|
||||
// Make sure klass does not have has_finalizer, or is abstract, or interface or java/lang/Class.
|
||||
__ andi_(R0, Rinstance_size, Klass::_lh_instance_slow_path_bit); // slow path bit equals 0?
|
||||
|
||||
__ crnand(/*CR0 eq*/2, /*CR1 eq*/4+2, /*CR0 eq*/2); // slow path bit set or not fully initialized?
|
||||
__ crnand(CCR0, Assembler::equal, CCR1, Assembler::equal); // slow path bit set or not fully initialized?
|
||||
__ beq(CCR0, Lslow_case);
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
@ -3184,7 +3184,24 @@ void MacroAssembler::pow_or_exp(bool is_exp, int num_fpu_regs_in_use) {
|
||||
jmp(done);
|
||||
} else {
|
||||
// Stack: X Y
|
||||
Label x_negative, y_odd;
|
||||
Label x_negative, y_not_2;
|
||||
|
||||
static double two = 2.0;
|
||||
ExternalAddress two_addr((address)&two);
|
||||
|
||||
// constant maybe too far on 64 bit
|
||||
lea(tmp2, two_addr);
|
||||
fld_d(Address(tmp2, 0)); // Stack: 2 X Y
|
||||
fcmp(tmp, 2, true, false); // Stack: X Y
|
||||
jcc(Assembler::parity, y_not_2);
|
||||
jcc(Assembler::notEqual, y_not_2);
|
||||
|
||||
fxch(); fpop(); // Stack: X
|
||||
fmul(0); // Stack: X*X
|
||||
|
||||
jmp(done);
|
||||
|
||||
bind(y_not_2);
|
||||
|
||||
fldz(); // Stack: 0 X Y
|
||||
fcmp(tmp, 1, true, false); // Stack: X Y
|
||||
|
||||
@ -197,7 +197,38 @@ static pid_t filename_to_pid(const char* filename) {
|
||||
}
|
||||
|
||||
|
||||
// check if the given path is considered a secure directory for
|
||||
// Check if the given statbuf is considered a secure directory for
|
||||
// the backing store files. Returns true if the directory is considered
|
||||
// a secure location. Returns false if the statbuf is a symbolic link or
|
||||
// if an error occurred.
|
||||
//
|
||||
static bool is_statbuf_secure(struct stat *statp) {
|
||||
if (S_ISLNK(statp->st_mode) || !S_ISDIR(statp->st_mode)) {
|
||||
// The path represents a link or some non-directory file type,
|
||||
// which is not what we expected. Declare it insecure.
|
||||
//
|
||||
return false;
|
||||
}
|
||||
// We have an existing directory, check if the permissions are safe.
|
||||
//
|
||||
if ((statp->st_mode & (S_IWGRP|S_IWOTH)) != 0) {
|
||||
// The directory is open for writing and could be subjected
|
||||
// to a symlink or a hard link attack. Declare it insecure.
|
||||
//
|
||||
return false;
|
||||
}
|
||||
// See if the uid of the directory matches the effective uid of the process.
|
||||
//
|
||||
if (statp->st_uid != geteuid()) {
|
||||
// The directory was not created by this user, declare it insecure.
|
||||
//
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Check if the given path is considered a secure directory for
|
||||
// the backing store files. Returns true if the directory exists
|
||||
// and is considered a secure location. Returns false if the path
|
||||
// is a symbolic link or if an error occurred.
|
||||
@ -211,27 +242,185 @@ static bool is_directory_secure(const char* path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// the path exists, now check it's mode
|
||||
if (S_ISLNK(statbuf.st_mode) || !S_ISDIR(statbuf.st_mode)) {
|
||||
// the path represents a link or some non-directory file type,
|
||||
// which is not what we expected. declare it insecure.
|
||||
//
|
||||
// The path exists, see if it is secure.
|
||||
return is_statbuf_secure(&statbuf);
|
||||
}
|
||||
|
||||
|
||||
// Check if the given directory file descriptor is considered a secure
|
||||
// directory for the backing store files. Returns true if the directory
|
||||
// exists and is considered a secure location. Returns false if the path
|
||||
// is a symbolic link or if an error occurred.
|
||||
//
|
||||
static bool is_dirfd_secure(int dir_fd) {
|
||||
struct stat statbuf;
|
||||
int result = 0;
|
||||
|
||||
RESTARTABLE(::fstat(dir_fd, &statbuf), result);
|
||||
if (result == OS_ERR) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
// we have an existing directory, check if the permissions are safe.
|
||||
//
|
||||
if ((statbuf.st_mode & (S_IWGRP|S_IWOTH)) != 0) {
|
||||
// the directory is open for writing and could be subjected
|
||||
// to a symlnk attack. declare it insecure.
|
||||
//
|
||||
return false;
|
||||
|
||||
// The path exists, now check its mode.
|
||||
return is_statbuf_secure(&statbuf);
|
||||
}
|
||||
|
||||
|
||||
// Check to make sure fd1 and fd2 are referencing the same file system object.
|
||||
//
|
||||
static bool is_same_fsobject(int fd1, int fd2) {
|
||||
struct stat statbuf1;
|
||||
struct stat statbuf2;
|
||||
int result = 0;
|
||||
|
||||
RESTARTABLE(::fstat(fd1, &statbuf1), result);
|
||||
if (result == OS_ERR) {
|
||||
return false;
|
||||
}
|
||||
RESTARTABLE(::fstat(fd2, &statbuf2), result);
|
||||
if (result == OS_ERR) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((statbuf1.st_ino == statbuf2.st_ino) &&
|
||||
(statbuf1.st_dev == statbuf2.st_dev)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Open the directory of the given path and validate it.
|
||||
// Return a DIR * of the open directory.
|
||||
//
|
||||
static DIR *open_directory_secure(const char* dirname) {
|
||||
// Open the directory using open() so that it can be verified
|
||||
// to be secure by calling is_dirfd_secure(), opendir() and then check
|
||||
// to see if they are the same file system object. This method does not
|
||||
// introduce a window of opportunity for the directory to be attacked that
|
||||
// calling opendir() and is_directory_secure() does.
|
||||
int result;
|
||||
DIR *dirp = NULL;
|
||||
RESTARTABLE(::open(dirname, O_RDONLY|O_NOFOLLOW), result);
|
||||
if (result == OS_ERR) {
|
||||
// Directory doesn't exist or is a symlink, so there is nothing to cleanup.
|
||||
if (PrintMiscellaneous && Verbose) {
|
||||
if (errno == ELOOP) {
|
||||
warning("directory %s is a symlink and is not secure\n", dirname);
|
||||
} else {
|
||||
warning("could not open directory %s: %s\n", dirname, strerror(errno));
|
||||
}
|
||||
}
|
||||
return dirp;
|
||||
}
|
||||
int fd = result;
|
||||
|
||||
// Determine if the open directory is secure.
|
||||
if (!is_dirfd_secure(fd)) {
|
||||
// The directory is not a secure directory.
|
||||
os::close(fd);
|
||||
return dirp;
|
||||
}
|
||||
|
||||
// Open the directory.
|
||||
dirp = ::opendir(dirname);
|
||||
if (dirp == NULL) {
|
||||
// The directory doesn't exist, close fd and return.
|
||||
os::close(fd);
|
||||
return dirp;
|
||||
}
|
||||
|
||||
// Check to make sure fd and dirp are referencing the same file system object.
|
||||
if (!is_same_fsobject(fd, dirfd(dirp))) {
|
||||
// The directory is not secure.
|
||||
os::close(fd);
|
||||
os::closedir(dirp);
|
||||
dirp = NULL;
|
||||
return dirp;
|
||||
}
|
||||
|
||||
// Close initial open now that we know directory is secure
|
||||
os::close(fd);
|
||||
|
||||
return dirp;
|
||||
}
|
||||
|
||||
// NOTE: The code below uses fchdir(), open() and unlink() because
|
||||
// fdopendir(), openat() and unlinkat() are not supported on all
|
||||
// versions. Once the support for fdopendir(), openat() and unlinkat()
|
||||
// is available on all supported versions the code can be changed
|
||||
// to use these functions.
|
||||
|
||||
// Open the directory of the given path, validate it and set the
|
||||
// current working directory to it.
|
||||
// Return a DIR * of the open directory and the saved cwd fd.
|
||||
//
|
||||
static DIR *open_directory_secure_cwd(const char* dirname, int *saved_cwd_fd) {
|
||||
|
||||
// Open the directory.
|
||||
DIR* dirp = open_directory_secure(dirname);
|
||||
if (dirp == NULL) {
|
||||
// Directory doesn't exist or is insecure, so there is nothing to cleanup.
|
||||
return dirp;
|
||||
}
|
||||
int fd = dirfd(dirp);
|
||||
|
||||
// Open a fd to the cwd and save it off.
|
||||
int result;
|
||||
RESTARTABLE(::open(".", O_RDONLY), result);
|
||||
if (result == OS_ERR) {
|
||||
*saved_cwd_fd = -1;
|
||||
} else {
|
||||
*saved_cwd_fd = result;
|
||||
}
|
||||
|
||||
// Set the current directory to dirname by using the fd of the directory.
|
||||
result = fchdir(fd);
|
||||
|
||||
return dirp;
|
||||
}
|
||||
|
||||
// Close the directory and restore the current working directory.
|
||||
//
|
||||
static void close_directory_secure_cwd(DIR* dirp, int saved_cwd_fd) {
|
||||
|
||||
int result;
|
||||
// If we have a saved cwd change back to it and close the fd.
|
||||
if (saved_cwd_fd != -1) {
|
||||
result = fchdir(saved_cwd_fd);
|
||||
::close(saved_cwd_fd);
|
||||
}
|
||||
|
||||
// Close the directory.
|
||||
os::closedir(dirp);
|
||||
}
|
||||
|
||||
// Check if the given file descriptor is considered a secure.
|
||||
//
|
||||
static bool is_file_secure(int fd, const char *filename) {
|
||||
|
||||
int result;
|
||||
struct stat statbuf;
|
||||
|
||||
// Determine if the file is secure.
|
||||
RESTARTABLE(::fstat(fd, &statbuf), result);
|
||||
if (result == OS_ERR) {
|
||||
if (PrintMiscellaneous && Verbose) {
|
||||
warning("fstat failed on %s: %s\n", filename, strerror(errno));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (statbuf.st_nlink > 1) {
|
||||
// A file with multiple links is not expected.
|
||||
if (PrintMiscellaneous && Verbose) {
|
||||
warning("file %s has multiple links\n", filename);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// return the user name for the given user id
|
||||
//
|
||||
// the caller is expected to free the allocated memory.
|
||||
@ -317,9 +506,11 @@ static char* get_user_name_slow(int vmid, TRAPS) {
|
||||
|
||||
const char* tmpdirname = os::get_temp_directory();
|
||||
|
||||
// open the temp directory
|
||||
DIR* tmpdirp = os::opendir(tmpdirname);
|
||||
|
||||
if (tmpdirp == NULL) {
|
||||
// Cannot open the directory to get the user name, return.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -344,25 +535,14 @@ static char* get_user_name_slow(int vmid, TRAPS) {
|
||||
strcat(usrdir_name, "/");
|
||||
strcat(usrdir_name, dentry->d_name);
|
||||
|
||||
DIR* subdirp = os::opendir(usrdir_name);
|
||||
// open the user directory
|
||||
DIR* subdirp = open_directory_secure(usrdir_name);
|
||||
|
||||
if (subdirp == NULL) {
|
||||
FREE_C_HEAP_ARRAY(char, usrdir_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Since we don't create the backing store files in directories
|
||||
// pointed to by symbolic links, we also don't follow them when
|
||||
// looking for the files. We check for a symbolic link after the
|
||||
// call to opendir in order to eliminate a small window where the
|
||||
// symlink can be exploited.
|
||||
//
|
||||
if (!is_directory_secure(usrdir_name)) {
|
||||
FREE_C_HEAP_ARRAY(char, usrdir_name);
|
||||
os::closedir(subdirp);
|
||||
continue;
|
||||
}
|
||||
|
||||
struct dirent* udentry;
|
||||
char* udbuf = NEW_C_HEAP_ARRAY(char, os::readdir_buf_size(usrdir_name), mtInternal);
|
||||
errno = 0;
|
||||
@ -465,26 +645,6 @@ static void remove_file(const char* path) {
|
||||
}
|
||||
|
||||
|
||||
// remove file
|
||||
//
|
||||
// this method removes the file with the given file name in the
|
||||
// named directory.
|
||||
//
|
||||
static void remove_file(const char* dirname, const char* filename) {
|
||||
|
||||
size_t nbytes = strlen(dirname) + strlen(filename) + 2;
|
||||
char* path = NEW_C_HEAP_ARRAY(char, nbytes, mtInternal);
|
||||
|
||||
strcpy(path, dirname);
|
||||
strcat(path, "/");
|
||||
strcat(path, filename);
|
||||
|
||||
remove_file(path);
|
||||
|
||||
FREE_C_HEAP_ARRAY(char, path);
|
||||
}
|
||||
|
||||
|
||||
// cleanup stale shared memory resources
|
||||
//
|
||||
// This method attempts to remove all stale shared memory files in
|
||||
@ -496,17 +656,11 @@ static void remove_file(const char* dirname, const char* filename) {
|
||||
//
|
||||
static void cleanup_sharedmem_resources(const char* dirname) {
|
||||
|
||||
// open the user temp directory
|
||||
DIR* dirp = os::opendir(dirname);
|
||||
|
||||
int saved_cwd_fd;
|
||||
// open the directory and set the current working directory to it
|
||||
DIR* dirp = open_directory_secure_cwd(dirname, &saved_cwd_fd);
|
||||
if (dirp == NULL) {
|
||||
// directory doesn't exist, so there is nothing to cleanup
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_directory_secure(dirname)) {
|
||||
// the directory is not a secure directory
|
||||
os::closedir(dirp);
|
||||
// directory doesn't exist or is insecure, so there is nothing to cleanup
|
||||
return;
|
||||
}
|
||||
|
||||
@ -520,6 +674,7 @@ static void cleanup_sharedmem_resources(const char* dirname) {
|
||||
//
|
||||
struct dirent* entry;
|
||||
char* dbuf = NEW_C_HEAP_ARRAY(char, os::readdir_buf_size(dirname), mtInternal);
|
||||
|
||||
errno = 0;
|
||||
while ((entry = os::readdir(dirp, (struct dirent *)dbuf)) != NULL) {
|
||||
|
||||
@ -530,7 +685,7 @@ static void cleanup_sharedmem_resources(const char* dirname) {
|
||||
if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
|
||||
|
||||
// attempt to remove all unexpected files, except "." and ".."
|
||||
remove_file(dirname, entry->d_name);
|
||||
unlink(entry->d_name);
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
@ -553,11 +708,14 @@ static void cleanup_sharedmem_resources(const char* dirname) {
|
||||
if ((pid == os::current_process_id()) ||
|
||||
(kill(pid, 0) == OS_ERR && (errno == ESRCH || errno == EPERM))) {
|
||||
|
||||
remove_file(dirname, entry->d_name);
|
||||
unlink(entry->d_name);
|
||||
}
|
||||
errno = 0;
|
||||
}
|
||||
os::closedir(dirp);
|
||||
|
||||
// close the directory and reset the current working directory
|
||||
close_directory_secure_cwd(dirp, saved_cwd_fd);
|
||||
|
||||
FREE_C_HEAP_ARRAY(char, dbuf);
|
||||
}
|
||||
|
||||
@ -614,19 +772,54 @@ static int create_sharedmem_resources(const char* dirname, const char* filename,
|
||||
return -1;
|
||||
}
|
||||
|
||||
int result;
|
||||
|
||||
RESTARTABLE(::open(filename, O_RDWR|O_CREAT|O_TRUNC, S_IREAD|S_IWRITE), result);
|
||||
if (result == OS_ERR) {
|
||||
if (PrintMiscellaneous && Verbose) {
|
||||
warning("could not create file %s: %s\n", filename, strerror(errno));
|
||||
}
|
||||
int saved_cwd_fd;
|
||||
// open the directory and set the current working directory to it
|
||||
DIR* dirp = open_directory_secure_cwd(dirname, &saved_cwd_fd);
|
||||
if (dirp == NULL) {
|
||||
// Directory doesn't exist or is insecure, so cannot create shared
|
||||
// memory file.
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Open the filename in the current directory.
|
||||
// Cannot use O_TRUNC here; truncation of an existing file has to happen
|
||||
// after the is_file_secure() check below.
|
||||
int result;
|
||||
RESTARTABLE(::open(filename, O_RDWR|O_CREAT|O_NOFOLLOW, S_IREAD|S_IWRITE), result);
|
||||
if (result == OS_ERR) {
|
||||
if (PrintMiscellaneous && Verbose) {
|
||||
if (errno == ELOOP) {
|
||||
warning("file %s is a symlink and is not secure\n", filename);
|
||||
} else {
|
||||
warning("could not create file %s: %s\n", filename, strerror(errno));
|
||||
}
|
||||
}
|
||||
// close the directory and reset the current working directory
|
||||
close_directory_secure_cwd(dirp, saved_cwd_fd);
|
||||
|
||||
return -1;
|
||||
}
|
||||
// close the directory and reset the current working directory
|
||||
close_directory_secure_cwd(dirp, saved_cwd_fd);
|
||||
|
||||
// save the file descriptor
|
||||
int fd = result;
|
||||
|
||||
// check to see if the file is secure
|
||||
if (!is_file_secure(fd, filename)) {
|
||||
::close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// truncate the file to get rid of any existing data
|
||||
RESTARTABLE(::ftruncate(fd, (off_t)0), result);
|
||||
if (result == OS_ERR) {
|
||||
if (PrintMiscellaneous && Verbose) {
|
||||
warning("could not truncate shared memory file: %s\n", strerror(errno));
|
||||
}
|
||||
::close(fd);
|
||||
return -1;
|
||||
}
|
||||
// set the file size
|
||||
RESTARTABLE(::ftruncate(fd, (off_t)size), result);
|
||||
if (result == OS_ERR) {
|
||||
@ -684,8 +877,15 @@ static int open_sharedmem_file(const char* filename, int oflags, TRAPS) {
|
||||
THROW_MSG_(vmSymbols::java_io_IOException(), strerror(errno), OS_ERR);
|
||||
}
|
||||
}
|
||||
int fd = result;
|
||||
|
||||
return result;
|
||||
// check to see if the file is secure
|
||||
if (!is_file_secure(fd, filename)) {
|
||||
::close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
// create a named shared memory region. returns the address of the
|
||||
@ -717,13 +917,21 @@ static char* mmap_create_shared(size_t size) {
|
||||
char* dirname = get_user_tmp_dir(user_name);
|
||||
char* filename = get_sharedmem_filename(dirname, vmid);
|
||||
|
||||
// get the short filename
|
||||
char* short_filename = strrchr(filename, '/');
|
||||
if (short_filename == NULL) {
|
||||
short_filename = filename;
|
||||
} else {
|
||||
short_filename++;
|
||||
}
|
||||
|
||||
// cleanup any stale shared memory files
|
||||
cleanup_sharedmem_resources(dirname);
|
||||
|
||||
assert(((size > 0) && (size % os::vm_page_size() == 0)),
|
||||
"unexpected PerfMemory region size");
|
||||
|
||||
fd = create_sharedmem_resources(dirname, filename, size);
|
||||
fd = create_sharedmem_resources(dirname, short_filename, size);
|
||||
|
||||
FREE_C_HEAP_ARRAY(char, user_name);
|
||||
FREE_C_HEAP_ARRAY(char, dirname);
|
||||
@ -838,12 +1046,12 @@ static void mmap_attach_shared(const char* user, int vmid, PerfMemory::PerfMemor
|
||||
// constructs for the file and the shared memory mapping.
|
||||
if (mode == PerfMemory::PERF_MODE_RO) {
|
||||
mmap_prot = PROT_READ;
|
||||
file_flags = O_RDONLY;
|
||||
file_flags = O_RDONLY | O_NOFOLLOW;
|
||||
}
|
||||
else if (mode == PerfMemory::PERF_MODE_RW) {
|
||||
#ifdef LATER
|
||||
mmap_prot = PROT_READ | PROT_WRITE;
|
||||
file_flags = O_RDWR;
|
||||
file_flags = O_RDWR | O_NOFOLLOW;
|
||||
#else
|
||||
THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(),
|
||||
"Unsupported access mode");
|
||||
|
||||
@ -197,7 +197,38 @@ static pid_t filename_to_pid(const char* filename) {
|
||||
}
|
||||
|
||||
|
||||
// check if the given path is considered a secure directory for
|
||||
// Check if the given statbuf is considered a secure directory for
|
||||
// the backing store files. Returns true if the directory is considered
|
||||
// a secure location. Returns false if the statbuf is a symbolic link or
|
||||
// if an error occurred.
|
||||
//
|
||||
static bool is_statbuf_secure(struct stat *statp) {
|
||||
if (S_ISLNK(statp->st_mode) || !S_ISDIR(statp->st_mode)) {
|
||||
// The path represents a link or some non-directory file type,
|
||||
// which is not what we expected. Declare it insecure.
|
||||
//
|
||||
return false;
|
||||
}
|
||||
// We have an existing directory, check if the permissions are safe.
|
||||
//
|
||||
if ((statp->st_mode & (S_IWGRP|S_IWOTH)) != 0) {
|
||||
// The directory is open for writing and could be subjected
|
||||
// to a symlink or a hard link attack. Declare it insecure.
|
||||
//
|
||||
return false;
|
||||
}
|
||||
// See if the uid of the directory matches the effective uid of the process.
|
||||
//
|
||||
if (statp->st_uid != geteuid()) {
|
||||
// The directory was not created by this user, declare it insecure.
|
||||
//
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Check if the given path is considered a secure directory for
|
||||
// the backing store files. Returns true if the directory exists
|
||||
// and is considered a secure location. Returns false if the path
|
||||
// is a symbolic link or if an error occurred.
|
||||
@ -211,22 +242,180 @@ static bool is_directory_secure(const char* path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// the path exists, now check it's mode
|
||||
if (S_ISLNK(statbuf.st_mode) || !S_ISDIR(statbuf.st_mode)) {
|
||||
// the path represents a link or some non-directory file type,
|
||||
// which is not what we expected. declare it insecure.
|
||||
//
|
||||
// The path exists, see if it is secure.
|
||||
return is_statbuf_secure(&statbuf);
|
||||
}
|
||||
|
||||
|
||||
// Check if the given directory file descriptor is considered a secure
|
||||
// directory for the backing store files. Returns true if the directory
|
||||
// exists and is considered a secure location. Returns false if the path
|
||||
// is a symbolic link or if an error occurred.
|
||||
//
|
||||
static bool is_dirfd_secure(int dir_fd) {
|
||||
struct stat statbuf;
|
||||
int result = 0;
|
||||
|
||||
RESTARTABLE(::fstat(dir_fd, &statbuf), result);
|
||||
if (result == OS_ERR) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
// we have an existing directory, check if the permissions are safe.
|
||||
//
|
||||
if ((statbuf.st_mode & (S_IWGRP|S_IWOTH)) != 0) {
|
||||
// the directory is open for writing and could be subjected
|
||||
// to a symlnk attack. declare it insecure.
|
||||
//
|
||||
return false;
|
||||
|
||||
// The path exists, now check its mode.
|
||||
return is_statbuf_secure(&statbuf);
|
||||
}
|
||||
|
||||
|
||||
// Check to make sure fd1 and fd2 are referencing the same file system object.
|
||||
//
|
||||
static bool is_same_fsobject(int fd1, int fd2) {
|
||||
struct stat statbuf1;
|
||||
struct stat statbuf2;
|
||||
int result = 0;
|
||||
|
||||
RESTARTABLE(::fstat(fd1, &statbuf1), result);
|
||||
if (result == OS_ERR) {
|
||||
return false;
|
||||
}
|
||||
RESTARTABLE(::fstat(fd2, &statbuf2), result);
|
||||
if (result == OS_ERR) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((statbuf1.st_ino == statbuf2.st_ino) &&
|
||||
(statbuf1.st_dev == statbuf2.st_dev)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Open the directory of the given path and validate it.
|
||||
// Return a DIR * of the open directory.
|
||||
//
|
||||
static DIR *open_directory_secure(const char* dirname) {
|
||||
// Open the directory using open() so that it can be verified
|
||||
// to be secure by calling is_dirfd_secure(), opendir() and then check
|
||||
// to see if they are the same file system object. This method does not
|
||||
// introduce a window of opportunity for the directory to be attacked that
|
||||
// calling opendir() and is_directory_secure() does.
|
||||
int result;
|
||||
DIR *dirp = NULL;
|
||||
RESTARTABLE(::open(dirname, O_RDONLY|O_NOFOLLOW), result);
|
||||
if (result == OS_ERR) {
|
||||
if (PrintMiscellaneous && Verbose) {
|
||||
if (errno == ELOOP) {
|
||||
warning("directory %s is a symlink and is not secure\n", dirname);
|
||||
} else {
|
||||
warning("could not open directory %s: %s\n", dirname, strerror(errno));
|
||||
}
|
||||
}
|
||||
return dirp;
|
||||
}
|
||||
int fd = result;
|
||||
|
||||
// Determine if the open directory is secure.
|
||||
if (!is_dirfd_secure(fd)) {
|
||||
// The directory is not a secure directory.
|
||||
os::close(fd);
|
||||
return dirp;
|
||||
}
|
||||
|
||||
// Open the directory.
|
||||
dirp = ::opendir(dirname);
|
||||
if (dirp == NULL) {
|
||||
// The directory doesn't exist, close fd and return.
|
||||
os::close(fd);
|
||||
return dirp;
|
||||
}
|
||||
|
||||
// Check to make sure fd and dirp are referencing the same file system object.
|
||||
if (!is_same_fsobject(fd, dirfd(dirp))) {
|
||||
// The directory is not secure.
|
||||
os::close(fd);
|
||||
os::closedir(dirp);
|
||||
dirp = NULL;
|
||||
return dirp;
|
||||
}
|
||||
|
||||
// Close initial open now that we know directory is secure
|
||||
os::close(fd);
|
||||
|
||||
return dirp;
|
||||
}
|
||||
|
||||
// NOTE: The code below uses fchdir(), open() and unlink() because
|
||||
// fdopendir(), openat() and unlinkat() are not supported on all
|
||||
// versions. Once the support for fdopendir(), openat() and unlinkat()
|
||||
// is available on all supported versions the code can be changed
|
||||
// to use these functions.
|
||||
|
||||
// Open the directory of the given path, validate it and set the
|
||||
// current working directory to it.
|
||||
// Return a DIR * of the open directory and the saved cwd fd.
|
||||
//
|
||||
static DIR *open_directory_secure_cwd(const char* dirname, int *saved_cwd_fd) {
|
||||
|
||||
// Open the directory.
|
||||
DIR* dirp = open_directory_secure(dirname);
|
||||
if (dirp == NULL) {
|
||||
// Directory doesn't exist or is insecure, so there is nothing to cleanup.
|
||||
return dirp;
|
||||
}
|
||||
int fd = dirfd(dirp);
|
||||
|
||||
// Open a fd to the cwd and save it off.
|
||||
int result;
|
||||
RESTARTABLE(::open(".", O_RDONLY), result);
|
||||
if (result == OS_ERR) {
|
||||
*saved_cwd_fd = -1;
|
||||
} else {
|
||||
*saved_cwd_fd = result;
|
||||
}
|
||||
|
||||
// Set the current directory to dirname by using the fd of the directory.
|
||||
result = fchdir(fd);
|
||||
|
||||
return dirp;
|
||||
}
|
||||
|
||||
// Close the directory and restore the current working directory.
|
||||
//
|
||||
static void close_directory_secure_cwd(DIR* dirp, int saved_cwd_fd) {
|
||||
|
||||
int result;
|
||||
// If we have a saved cwd change back to it and close the fd.
|
||||
if (saved_cwd_fd != -1) {
|
||||
result = fchdir(saved_cwd_fd);
|
||||
::close(saved_cwd_fd);
|
||||
}
|
||||
|
||||
// Close the directory.
|
||||
os::closedir(dirp);
|
||||
}
|
||||
|
||||
// Check if the given file descriptor is considered a secure.
|
||||
//
|
||||
static bool is_file_secure(int fd, const char *filename) {
|
||||
|
||||
int result;
|
||||
struct stat statbuf;
|
||||
|
||||
// Determine if the file is secure.
|
||||
RESTARTABLE(::fstat(fd, &statbuf), result);
|
||||
if (result == OS_ERR) {
|
||||
if (PrintMiscellaneous && Verbose) {
|
||||
warning("fstat failed on %s: %s\n", filename, strerror(errno));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (statbuf.st_nlink > 1) {
|
||||
// A file with multiple links is not expected.
|
||||
if (PrintMiscellaneous && Verbose) {
|
||||
warning("file %s has multiple links\n", filename);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -317,9 +506,11 @@ static char* get_user_name_slow(int vmid, TRAPS) {
|
||||
|
||||
const char* tmpdirname = os::get_temp_directory();
|
||||
|
||||
// open the temp directory
|
||||
DIR* tmpdirp = os::opendir(tmpdirname);
|
||||
|
||||
if (tmpdirp == NULL) {
|
||||
// Cannot open the directory to get the user name, return.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -344,7 +535,8 @@ static char* get_user_name_slow(int vmid, TRAPS) {
|
||||
strcat(usrdir_name, "/");
|
||||
strcat(usrdir_name, dentry->d_name);
|
||||
|
||||
DIR* subdirp = os::opendir(usrdir_name);
|
||||
// open the user directory
|
||||
DIR* subdirp = open_directory_secure(usrdir_name);
|
||||
|
||||
if (subdirp == NULL) {
|
||||
FREE_C_HEAP_ARRAY(char, usrdir_name);
|
||||
@ -465,26 +657,6 @@ static void remove_file(const char* path) {
|
||||
}
|
||||
|
||||
|
||||
// remove file
|
||||
//
|
||||
// this method removes the file with the given file name in the
|
||||
// named directory.
|
||||
//
|
||||
static void remove_file(const char* dirname, const char* filename) {
|
||||
|
||||
size_t nbytes = strlen(dirname) + strlen(filename) + 2;
|
||||
char* path = NEW_C_HEAP_ARRAY(char, nbytes, mtInternal);
|
||||
|
||||
strcpy(path, dirname);
|
||||
strcat(path, "/");
|
||||
strcat(path, filename);
|
||||
|
||||
remove_file(path);
|
||||
|
||||
FREE_C_HEAP_ARRAY(char, path);
|
||||
}
|
||||
|
||||
|
||||
// cleanup stale shared memory resources
|
||||
//
|
||||
// This method attempts to remove all stale shared memory files in
|
||||
@ -496,17 +668,11 @@ static void remove_file(const char* dirname, const char* filename) {
|
||||
//
|
||||
static void cleanup_sharedmem_resources(const char* dirname) {
|
||||
|
||||
// open the user temp directory
|
||||
DIR* dirp = os::opendir(dirname);
|
||||
|
||||
int saved_cwd_fd;
|
||||
// open the directory
|
||||
DIR* dirp = open_directory_secure_cwd(dirname, &saved_cwd_fd);
|
||||
if (dirp == NULL) {
|
||||
// directory doesn't exist, so there is nothing to cleanup
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_directory_secure(dirname)) {
|
||||
// the directory is not a secure directory
|
||||
os::closedir(dirp);
|
||||
// directory doesn't exist or is insecure, so there is nothing to cleanup
|
||||
return;
|
||||
}
|
||||
|
||||
@ -520,6 +686,7 @@ static void cleanup_sharedmem_resources(const char* dirname) {
|
||||
//
|
||||
struct dirent* entry;
|
||||
char* dbuf = NEW_C_HEAP_ARRAY(char, os::readdir_buf_size(dirname), mtInternal);
|
||||
|
||||
errno = 0;
|
||||
while ((entry = os::readdir(dirp, (struct dirent *)dbuf)) != NULL) {
|
||||
|
||||
@ -528,9 +695,8 @@ static void cleanup_sharedmem_resources(const char* dirname) {
|
||||
if (pid == 0) {
|
||||
|
||||
if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
|
||||
|
||||
// attempt to remove all unexpected files, except "." and ".."
|
||||
remove_file(dirname, entry->d_name);
|
||||
unlink(entry->d_name);
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
@ -552,12 +718,14 @@ static void cleanup_sharedmem_resources(const char* dirname) {
|
||||
//
|
||||
if ((pid == os::current_process_id()) ||
|
||||
(kill(pid, 0) == OS_ERR && (errno == ESRCH || errno == EPERM))) {
|
||||
|
||||
remove_file(dirname, entry->d_name);
|
||||
unlink(entry->d_name);
|
||||
}
|
||||
errno = 0;
|
||||
}
|
||||
os::closedir(dirp);
|
||||
|
||||
// close the directory and reset the current working directory
|
||||
close_directory_secure_cwd(dirp, saved_cwd_fd);
|
||||
|
||||
FREE_C_HEAP_ARRAY(char, dbuf);
|
||||
}
|
||||
|
||||
@ -614,19 +782,54 @@ static int create_sharedmem_resources(const char* dirname, const char* filename,
|
||||
return -1;
|
||||
}
|
||||
|
||||
int result;
|
||||
|
||||
RESTARTABLE(::open(filename, O_RDWR|O_CREAT|O_TRUNC, S_IREAD|S_IWRITE), result);
|
||||
if (result == OS_ERR) {
|
||||
if (PrintMiscellaneous && Verbose) {
|
||||
warning("could not create file %s: %s\n", filename, strerror(errno));
|
||||
}
|
||||
int saved_cwd_fd;
|
||||
// open the directory and set the current working directory to it
|
||||
DIR* dirp = open_directory_secure_cwd(dirname, &saved_cwd_fd);
|
||||
if (dirp == NULL) {
|
||||
// Directory doesn't exist or is insecure, so cannot create shared
|
||||
// memory file.
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Open the filename in the current directory.
|
||||
// Cannot use O_TRUNC here; truncation of an existing file has to happen
|
||||
// after the is_file_secure() check below.
|
||||
int result;
|
||||
RESTARTABLE(::open(filename, O_RDWR|O_CREAT|O_NOFOLLOW, S_IREAD|S_IWRITE), result);
|
||||
if (result == OS_ERR) {
|
||||
if (PrintMiscellaneous && Verbose) {
|
||||
if (errno == ELOOP) {
|
||||
warning("file %s is a symlink and is not secure\n", filename);
|
||||
} else {
|
||||
warning("could not create file %s: %s\n", filename, strerror(errno));
|
||||
}
|
||||
}
|
||||
// close the directory and reset the current working directory
|
||||
close_directory_secure_cwd(dirp, saved_cwd_fd);
|
||||
|
||||
return -1;
|
||||
}
|
||||
// close the directory and reset the current working directory
|
||||
close_directory_secure_cwd(dirp, saved_cwd_fd);
|
||||
|
||||
// save the file descriptor
|
||||
int fd = result;
|
||||
|
||||
// check to see if the file is secure
|
||||
if (!is_file_secure(fd, filename)) {
|
||||
::close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// truncate the file to get rid of any existing data
|
||||
RESTARTABLE(::ftruncate(fd, (off_t)0), result);
|
||||
if (result == OS_ERR) {
|
||||
if (PrintMiscellaneous && Verbose) {
|
||||
warning("could not truncate shared memory file: %s\n", strerror(errno));
|
||||
}
|
||||
::close(fd);
|
||||
return -1;
|
||||
}
|
||||
// set the file size
|
||||
RESTARTABLE(::ftruncate(fd, (off_t)size), result);
|
||||
if (result == OS_ERR) {
|
||||
@ -684,8 +887,15 @@ static int open_sharedmem_file(const char* filename, int oflags, TRAPS) {
|
||||
THROW_MSG_(vmSymbols::java_io_IOException(), strerror(errno), OS_ERR);
|
||||
}
|
||||
}
|
||||
int fd = result;
|
||||
|
||||
return result;
|
||||
// check to see if the file is secure
|
||||
if (!is_file_secure(fd, filename)) {
|
||||
::close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
// create a named shared memory region. returns the address of the
|
||||
@ -716,6 +926,13 @@ static char* mmap_create_shared(size_t size) {
|
||||
|
||||
char* dirname = get_user_tmp_dir(user_name);
|
||||
char* filename = get_sharedmem_filename(dirname, vmid);
|
||||
// get the short filename
|
||||
char* short_filename = strrchr(filename, '/');
|
||||
if (short_filename == NULL) {
|
||||
short_filename = filename;
|
||||
} else {
|
||||
short_filename++;
|
||||
}
|
||||
|
||||
// cleanup any stale shared memory files
|
||||
cleanup_sharedmem_resources(dirname);
|
||||
@ -723,7 +940,7 @@ static char* mmap_create_shared(size_t size) {
|
||||
assert(((size > 0) && (size % os::vm_page_size() == 0)),
|
||||
"unexpected PerfMemory region size");
|
||||
|
||||
fd = create_sharedmem_resources(dirname, filename, size);
|
||||
fd = create_sharedmem_resources(dirname, short_filename, size);
|
||||
|
||||
FREE_C_HEAP_ARRAY(char, user_name);
|
||||
FREE_C_HEAP_ARRAY(char, dirname);
|
||||
@ -838,12 +1055,12 @@ static void mmap_attach_shared(const char* user, int vmid, PerfMemory::PerfMemor
|
||||
// constructs for the file and the shared memory mapping.
|
||||
if (mode == PerfMemory::PERF_MODE_RO) {
|
||||
mmap_prot = PROT_READ;
|
||||
file_flags = O_RDONLY;
|
||||
file_flags = O_RDONLY | O_NOFOLLOW;
|
||||
}
|
||||
else if (mode == PerfMemory::PERF_MODE_RW) {
|
||||
#ifdef LATER
|
||||
mmap_prot = PROT_READ | PROT_WRITE;
|
||||
file_flags = O_RDWR;
|
||||
file_flags = O_RDWR | O_NOFOLLOW;
|
||||
#else
|
||||
THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(),
|
||||
"Unsupported access mode");
|
||||
|
||||
@ -199,7 +199,38 @@ static pid_t filename_to_pid(const char* filename) {
|
||||
}
|
||||
|
||||
|
||||
// check if the given path is considered a secure directory for
|
||||
// Check if the given statbuf is considered a secure directory for
|
||||
// the backing store files. Returns true if the directory is considered
|
||||
// a secure location. Returns false if the statbuf is a symbolic link or
|
||||
// if an error occurred.
|
||||
//
|
||||
static bool is_statbuf_secure(struct stat *statp) {
|
||||
if (S_ISLNK(statp->st_mode) || !S_ISDIR(statp->st_mode)) {
|
||||
// The path represents a link or some non-directory file type,
|
||||
// which is not what we expected. Declare it insecure.
|
||||
//
|
||||
return false;
|
||||
}
|
||||
// We have an existing directory, check if the permissions are safe.
|
||||
//
|
||||
if ((statp->st_mode & (S_IWGRP|S_IWOTH)) != 0) {
|
||||
// The directory is open for writing and could be subjected
|
||||
// to a symlink or a hard link attack. Declare it insecure.
|
||||
//
|
||||
return false;
|
||||
}
|
||||
// See if the uid of the directory matches the effective uid of the process.
|
||||
//
|
||||
if (statp->st_uid != geteuid()) {
|
||||
// The directory was not created by this user, declare it insecure.
|
||||
//
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Check if the given path is considered a secure directory for
|
||||
// the backing store files. Returns true if the directory exists
|
||||
// and is considered a secure location. Returns false if the path
|
||||
// is a symbolic link or if an error occurred.
|
||||
@ -213,27 +244,185 @@ static bool is_directory_secure(const char* path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// the path exists, now check it's mode
|
||||
if (S_ISLNK(statbuf.st_mode) || !S_ISDIR(statbuf.st_mode)) {
|
||||
// the path represents a link or some non-directory file type,
|
||||
// which is not what we expected. declare it insecure.
|
||||
//
|
||||
// The path exists, see if it is secure.
|
||||
return is_statbuf_secure(&statbuf);
|
||||
}
|
||||
|
||||
|
||||
// Check if the given directory file descriptor is considered a secure
|
||||
// directory for the backing store files. Returns true if the directory
|
||||
// exists and is considered a secure location. Returns false if the path
|
||||
// is a symbolic link or if an error occurred.
|
||||
//
|
||||
static bool is_dirfd_secure(int dir_fd) {
|
||||
struct stat statbuf;
|
||||
int result = 0;
|
||||
|
||||
RESTARTABLE(::fstat(dir_fd, &statbuf), result);
|
||||
if (result == OS_ERR) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
// we have an existing directory, check if the permissions are safe.
|
||||
//
|
||||
if ((statbuf.st_mode & (S_IWGRP|S_IWOTH)) != 0) {
|
||||
// the directory is open for writing and could be subjected
|
||||
// to a symlnk attack. declare it insecure.
|
||||
//
|
||||
return false;
|
||||
|
||||
// The path exists, now check its mode.
|
||||
return is_statbuf_secure(&statbuf);
|
||||
}
|
||||
|
||||
|
||||
// Check to make sure fd1 and fd2 are referencing the same file system object.
|
||||
//
|
||||
static bool is_same_fsobject(int fd1, int fd2) {
|
||||
struct stat statbuf1;
|
||||
struct stat statbuf2;
|
||||
int result = 0;
|
||||
|
||||
RESTARTABLE(::fstat(fd1, &statbuf1), result);
|
||||
if (result == OS_ERR) {
|
||||
return false;
|
||||
}
|
||||
RESTARTABLE(::fstat(fd2, &statbuf2), result);
|
||||
if (result == OS_ERR) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((statbuf1.st_ino == statbuf2.st_ino) &&
|
||||
(statbuf1.st_dev == statbuf2.st_dev)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Open the directory of the given path and validate it.
|
||||
// Return a DIR * of the open directory.
|
||||
//
|
||||
static DIR *open_directory_secure(const char* dirname) {
|
||||
// Open the directory using open() so that it can be verified
|
||||
// to be secure by calling is_dirfd_secure(), opendir() and then check
|
||||
// to see if they are the same file system object. This method does not
|
||||
// introduce a window of opportunity for the directory to be attacked that
|
||||
// calling opendir() and is_directory_secure() does.
|
||||
int result;
|
||||
DIR *dirp = NULL;
|
||||
RESTARTABLE(::open(dirname, O_RDONLY|O_NOFOLLOW), result);
|
||||
if (result == OS_ERR) {
|
||||
// Directory doesn't exist or is a symlink, so there is nothing to cleanup.
|
||||
if (PrintMiscellaneous && Verbose) {
|
||||
if (errno == ELOOP) {
|
||||
warning("directory %s is a symlink and is not secure\n", dirname);
|
||||
} else {
|
||||
warning("could not open directory %s: %s\n", dirname, strerror(errno));
|
||||
}
|
||||
}
|
||||
return dirp;
|
||||
}
|
||||
int fd = result;
|
||||
|
||||
// Determine if the open directory is secure.
|
||||
if (!is_dirfd_secure(fd)) {
|
||||
// The directory is not a secure directory.
|
||||
os::close(fd);
|
||||
return dirp;
|
||||
}
|
||||
|
||||
// Open the directory.
|
||||
dirp = ::opendir(dirname);
|
||||
if (dirp == NULL) {
|
||||
// The directory doesn't exist, close fd and return.
|
||||
os::close(fd);
|
||||
return dirp;
|
||||
}
|
||||
|
||||
// Check to make sure fd and dirp are referencing the same file system object.
|
||||
if (!is_same_fsobject(fd, dirp->dd_fd)) {
|
||||
// The directory is not secure.
|
||||
os::close(fd);
|
||||
os::closedir(dirp);
|
||||
dirp = NULL;
|
||||
return dirp;
|
||||
}
|
||||
|
||||
// Close initial open now that we know directory is secure
|
||||
os::close(fd);
|
||||
|
||||
return dirp;
|
||||
}
|
||||
|
||||
// NOTE: The code below uses fchdir(), open() and unlink() because
|
||||
// fdopendir(), openat() and unlinkat() are not supported on all
|
||||
// versions. Once the support for fdopendir(), openat() and unlinkat()
|
||||
// is available on all supported versions the code can be changed
|
||||
// to use these functions.
|
||||
|
||||
// Open the directory of the given path, validate it and set the
|
||||
// current working directory to it.
|
||||
// Return a DIR * of the open directory and the saved cwd fd.
|
||||
//
|
||||
static DIR *open_directory_secure_cwd(const char* dirname, int *saved_cwd_fd) {
|
||||
|
||||
// Open the directory.
|
||||
DIR* dirp = open_directory_secure(dirname);
|
||||
if (dirp == NULL) {
|
||||
// Directory doesn't exist or is insecure, so there is nothing to cleanup.
|
||||
return dirp;
|
||||
}
|
||||
int fd = dirp->dd_fd;
|
||||
|
||||
// Open a fd to the cwd and save it off.
|
||||
int result;
|
||||
RESTARTABLE(::open(".", O_RDONLY), result);
|
||||
if (result == OS_ERR) {
|
||||
*saved_cwd_fd = -1;
|
||||
} else {
|
||||
*saved_cwd_fd = result;
|
||||
}
|
||||
|
||||
// Set the current directory to dirname by using the fd of the directory.
|
||||
result = fchdir(fd);
|
||||
|
||||
return dirp;
|
||||
}
|
||||
|
||||
// Close the directory and restore the current working directory.
|
||||
//
|
||||
static void close_directory_secure_cwd(DIR* dirp, int saved_cwd_fd) {
|
||||
|
||||
int result;
|
||||
// If we have a saved cwd change back to it and close the fd.
|
||||
if (saved_cwd_fd != -1) {
|
||||
result = fchdir(saved_cwd_fd);
|
||||
::close(saved_cwd_fd);
|
||||
}
|
||||
|
||||
// Close the directory.
|
||||
os::closedir(dirp);
|
||||
}
|
||||
|
||||
// Check if the given file descriptor is considered a secure.
|
||||
//
|
||||
static bool is_file_secure(int fd, const char *filename) {
|
||||
|
||||
int result;
|
||||
struct stat statbuf;
|
||||
|
||||
// Determine if the file is secure.
|
||||
RESTARTABLE(::fstat(fd, &statbuf), result);
|
||||
if (result == OS_ERR) {
|
||||
if (PrintMiscellaneous && Verbose) {
|
||||
warning("fstat failed on %s: %s\n", filename, strerror(errno));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (statbuf.st_nlink > 1) {
|
||||
// A file with multiple links is not expected.
|
||||
if (PrintMiscellaneous && Verbose) {
|
||||
warning("file %s has multiple links\n", filename);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// return the user name for the given user id
|
||||
//
|
||||
// the caller is expected to free the allocated memory.
|
||||
@ -308,9 +497,11 @@ static char* get_user_name_slow(int vmid, TRAPS) {
|
||||
|
||||
const char* tmpdirname = os::get_temp_directory();
|
||||
|
||||
// open the temp directory
|
||||
DIR* tmpdirp = os::opendir(tmpdirname);
|
||||
|
||||
if (tmpdirp == NULL) {
|
||||
// Cannot open the directory to get the user name, return.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -335,7 +526,8 @@ static char* get_user_name_slow(int vmid, TRAPS) {
|
||||
strcat(usrdir_name, "/");
|
||||
strcat(usrdir_name, dentry->d_name);
|
||||
|
||||
DIR* subdirp = os::opendir(usrdir_name);
|
||||
// open the user directory
|
||||
DIR* subdirp = open_directory_secure(usrdir_name);
|
||||
|
||||
if (subdirp == NULL) {
|
||||
FREE_C_HEAP_ARRAY(char, usrdir_name);
|
||||
@ -504,26 +696,6 @@ static void remove_file(const char* path) {
|
||||
}
|
||||
|
||||
|
||||
// remove file
|
||||
//
|
||||
// this method removes the file with the given file name in the
|
||||
// named directory.
|
||||
//
|
||||
static void remove_file(const char* dirname, const char* filename) {
|
||||
|
||||
size_t nbytes = strlen(dirname) + strlen(filename) + 2;
|
||||
char* path = NEW_C_HEAP_ARRAY(char, nbytes, mtInternal);
|
||||
|
||||
strcpy(path, dirname);
|
||||
strcat(path, "/");
|
||||
strcat(path, filename);
|
||||
|
||||
remove_file(path);
|
||||
|
||||
FREE_C_HEAP_ARRAY(char, path);
|
||||
}
|
||||
|
||||
|
||||
// cleanup stale shared memory resources
|
||||
//
|
||||
// This method attempts to remove all stale shared memory files in
|
||||
@ -535,17 +707,11 @@ static void remove_file(const char* dirname, const char* filename) {
|
||||
//
|
||||
static void cleanup_sharedmem_resources(const char* dirname) {
|
||||
|
||||
// open the user temp directory
|
||||
DIR* dirp = os::opendir(dirname);
|
||||
|
||||
int saved_cwd_fd;
|
||||
// open the directory
|
||||
DIR* dirp = open_directory_secure_cwd(dirname, &saved_cwd_fd);
|
||||
if (dirp == NULL) {
|
||||
// directory doesn't exist, so there is nothing to cleanup
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_directory_secure(dirname)) {
|
||||
// the directory is not a secure directory
|
||||
os::closedir(dirp);
|
||||
// directory doesn't exist or is insecure, so there is nothing to cleanup
|
||||
return;
|
||||
}
|
||||
|
||||
@ -559,6 +725,7 @@ static void cleanup_sharedmem_resources(const char* dirname) {
|
||||
//
|
||||
struct dirent* entry;
|
||||
char* dbuf = NEW_C_HEAP_ARRAY(char, os::readdir_buf_size(dirname), mtInternal);
|
||||
|
||||
errno = 0;
|
||||
while ((entry = os::readdir(dirp, (struct dirent *)dbuf)) != NULL) {
|
||||
|
||||
@ -569,7 +736,7 @@ static void cleanup_sharedmem_resources(const char* dirname) {
|
||||
if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
|
||||
|
||||
// attempt to remove all unexpected files, except "." and ".."
|
||||
remove_file(dirname, entry->d_name);
|
||||
unlink(entry->d_name);
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
@ -592,11 +759,14 @@ static void cleanup_sharedmem_resources(const char* dirname) {
|
||||
if ((pid == os::current_process_id()) ||
|
||||
(kill(pid, 0) == OS_ERR && (errno == ESRCH || errno == EPERM))) {
|
||||
|
||||
remove_file(dirname, entry->d_name);
|
||||
unlink(entry->d_name);
|
||||
}
|
||||
errno = 0;
|
||||
}
|
||||
os::closedir(dirp);
|
||||
|
||||
// close the directory and reset the current working directory
|
||||
close_directory_secure_cwd(dirp, saved_cwd_fd);
|
||||
|
||||
FREE_C_HEAP_ARRAY(char, dbuf);
|
||||
}
|
||||
|
||||
@ -653,19 +823,54 @@ static int create_sharedmem_resources(const char* dirname, const char* filename,
|
||||
return -1;
|
||||
}
|
||||
|
||||
int result;
|
||||
|
||||
RESTARTABLE(::open(filename, O_RDWR|O_CREAT|O_TRUNC, S_IREAD|S_IWRITE), result);
|
||||
if (result == OS_ERR) {
|
||||
if (PrintMiscellaneous && Verbose) {
|
||||
warning("could not create file %s: %s\n", filename, strerror(errno));
|
||||
}
|
||||
int saved_cwd_fd;
|
||||
// open the directory and set the current working directory to it
|
||||
DIR* dirp = open_directory_secure_cwd(dirname, &saved_cwd_fd);
|
||||
if (dirp == NULL) {
|
||||
// Directory doesn't exist or is insecure, so cannot create shared
|
||||
// memory file.
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Open the filename in the current directory.
|
||||
// Cannot use O_TRUNC here; truncation of an existing file has to happen
|
||||
// after the is_file_secure() check below.
|
||||
int result;
|
||||
RESTARTABLE(::open(filename, O_RDWR|O_CREAT|O_NOFOLLOW, S_IREAD|S_IWRITE), result);
|
||||
if (result == OS_ERR) {
|
||||
if (PrintMiscellaneous && Verbose) {
|
||||
if (errno == ELOOP) {
|
||||
warning("file %s is a symlink and is not secure\n", filename);
|
||||
} else {
|
||||
warning("could not create file %s: %s\n", filename, strerror(errno));
|
||||
}
|
||||
}
|
||||
// close the directory and reset the current working directory
|
||||
close_directory_secure_cwd(dirp, saved_cwd_fd);
|
||||
|
||||
return -1;
|
||||
}
|
||||
// close the directory and reset the current working directory
|
||||
close_directory_secure_cwd(dirp, saved_cwd_fd);
|
||||
|
||||
// save the file descriptor
|
||||
int fd = result;
|
||||
|
||||
// check to see if the file is secure
|
||||
if (!is_file_secure(fd, filename)) {
|
||||
::close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// truncate the file to get rid of any existing data
|
||||
RESTARTABLE(::ftruncate(fd, (off_t)0), result);
|
||||
if (result == OS_ERR) {
|
||||
if (PrintMiscellaneous && Verbose) {
|
||||
warning("could not truncate shared memory file: %s\n", strerror(errno));
|
||||
}
|
||||
::close(fd);
|
||||
return -1;
|
||||
}
|
||||
// set the file size
|
||||
RESTARTABLE(::ftruncate(fd, (off_t)size), result);
|
||||
if (result == OS_ERR) {
|
||||
@ -701,8 +906,15 @@ static int open_sharedmem_file(const char* filename, int oflags, TRAPS) {
|
||||
THROW_MSG_(vmSymbols::java_io_IOException(), strerror(errno), OS_ERR);
|
||||
}
|
||||
}
|
||||
int fd = result;
|
||||
|
||||
return result;
|
||||
// check to see if the file is secure
|
||||
if (!is_file_secure(fd, filename)) {
|
||||
::close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
// create a named shared memory region. returns the address of the
|
||||
@ -734,13 +946,21 @@ static char* mmap_create_shared(size_t size) {
|
||||
char* dirname = get_user_tmp_dir(user_name);
|
||||
char* filename = get_sharedmem_filename(dirname, vmid);
|
||||
|
||||
// get the short filename
|
||||
char* short_filename = strrchr(filename, '/');
|
||||
if (short_filename == NULL) {
|
||||
short_filename = filename;
|
||||
} else {
|
||||
short_filename++;
|
||||
}
|
||||
|
||||
// cleanup any stale shared memory files
|
||||
cleanup_sharedmem_resources(dirname);
|
||||
|
||||
assert(((size > 0) && (size % os::vm_page_size() == 0)),
|
||||
"unexpected PerfMemory region size");
|
||||
|
||||
fd = create_sharedmem_resources(dirname, filename, size);
|
||||
fd = create_sharedmem_resources(dirname, short_filename, size);
|
||||
|
||||
FREE_C_HEAP_ARRAY(char, user_name);
|
||||
FREE_C_HEAP_ARRAY(char, dirname);
|
||||
@ -856,12 +1076,12 @@ static void mmap_attach_shared(const char* user, int vmid, PerfMemory::PerfMemor
|
||||
// constructs for the file and the shared memory mapping.
|
||||
if (mode == PerfMemory::PERF_MODE_RO) {
|
||||
mmap_prot = PROT_READ;
|
||||
file_flags = O_RDONLY;
|
||||
file_flags = O_RDONLY | O_NOFOLLOW;
|
||||
}
|
||||
else if (mode == PerfMemory::PERF_MODE_RW) {
|
||||
#ifdef LATER
|
||||
mmap_prot = PROT_READ | PROT_WRITE;
|
||||
file_flags = O_RDWR;
|
||||
file_flags = O_RDWR | O_NOFOLLOW;
|
||||
#else
|
||||
THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(),
|
||||
"Unsupported access mode");
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright 2007, 2008, 2011 Red Hat, Inc.
|
||||
* Copyright 2007, 2008, 2011, 2015, Red Hat, Inc.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -237,7 +237,13 @@ inline jint Atomic::xchg(jint exchange_value, volatile jint* dest) {
|
||||
// operation. Note that some platforms only support this with the
|
||||
// limitation that the only valid value to store is the immediate
|
||||
// constant 1. There is a test for this in JNI_CreateJavaVM().
|
||||
return __sync_lock_test_and_set (dest, exchange_value);
|
||||
jint result = __sync_lock_test_and_set (dest, exchange_value);
|
||||
// All atomic operations are expected to be full memory barriers
|
||||
// (see atomic.hpp). However, __sync_lock_test_and_set is not
|
||||
// a full memory barrier, but an acquire barrier. Hence, this added
|
||||
// barrier.
|
||||
__sync_synchronize();
|
||||
return result;
|
||||
#endif // M68K
|
||||
#endif // ARM
|
||||
}
|
||||
@ -250,7 +256,9 @@ inline intptr_t Atomic::xchg_ptr(intptr_t exchange_value,
|
||||
#ifdef M68K
|
||||
return m68k_lock_test_and_set(dest, exchange_value);
|
||||
#else
|
||||
return __sync_lock_test_and_set (dest, exchange_value);
|
||||
intptr_t result = __sync_lock_test_and_set (dest, exchange_value);
|
||||
__sync_synchronize();
|
||||
return result;
|
||||
#endif // M68K
|
||||
#endif // ARM
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright 2007, 2008, 2011 Red Hat, Inc.
|
||||
* Copyright 2007, 2008, 2011, 2015, Red Hat, Inc.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -231,7 +231,13 @@ inline jint Atomic::xchg(jint exchange_value, volatile jint* dest) {
|
||||
// operation. Note that some platforms only support this with the
|
||||
// limitation that the only valid value to store is the immediate
|
||||
// constant 1. There is a test for this in JNI_CreateJavaVM().
|
||||
return __sync_lock_test_and_set (dest, exchange_value);
|
||||
jint result = __sync_lock_test_and_set (dest, exchange_value);
|
||||
// All atomic operations are expected to be full memory barriers
|
||||
// (see atomic.hpp). However, __sync_lock_test_and_set is not
|
||||
// a full memory barrier, but an acquire barrier. Hence, this added
|
||||
// barrier.
|
||||
__sync_synchronize();
|
||||
return result;
|
||||
#endif // M68K
|
||||
#endif // ARM
|
||||
}
|
||||
@ -244,7 +250,9 @@ inline intptr_t Atomic::xchg_ptr(intptr_t exchange_value,
|
||||
#ifdef M68K
|
||||
return m68k_lock_test_and_set(dest, exchange_value);
|
||||
#else
|
||||
return __sync_lock_test_and_set (dest, exchange_value);
|
||||
intptr_t result = __sync_lock_test_and_set (dest, exchange_value);
|
||||
__sync_synchronize();
|
||||
return result;
|
||||
#endif // M68K
|
||||
#endif // ARM
|
||||
}
|
||||
|
||||
@ -89,8 +89,8 @@ class BCEscapeAnalyzer::StateInfo {
|
||||
public:
|
||||
ArgumentMap *_vars;
|
||||
ArgumentMap *_stack;
|
||||
short _stack_height;
|
||||
short _max_stack;
|
||||
int _stack_height;
|
||||
int _max_stack;
|
||||
bool _initialized;
|
||||
ArgumentMap empty_map;
|
||||
|
||||
|
||||
@ -59,7 +59,7 @@ ciInstanceKlass::ciInstanceKlass(KlassHandle h_k) :
|
||||
_has_nonstatic_fields = ik->has_nonstatic_fields();
|
||||
_has_default_methods = ik->has_default_methods();
|
||||
_nonstatic_fields = NULL; // initialized lazily by compute_nonstatic_fields:
|
||||
|
||||
_has_injected_fields = -1;
|
||||
_implementor = NULL; // we will fill these lazily
|
||||
|
||||
Thread *thread = Thread::current();
|
||||
@ -100,6 +100,7 @@ ciInstanceKlass::ciInstanceKlass(ciSymbol* name,
|
||||
_nonstatic_field_size = -1;
|
||||
_has_nonstatic_fields = false;
|
||||
_nonstatic_fields = NULL;
|
||||
_has_injected_fields = -1;
|
||||
_loader = loader;
|
||||
_protection_domain = protection_domain;
|
||||
_is_shared = false;
|
||||
@ -500,6 +501,34 @@ ciInstanceKlass::compute_nonstatic_fields_impl(GrowableArray<ciField*>*
|
||||
return fields;
|
||||
}
|
||||
|
||||
void ciInstanceKlass::compute_injected_fields_helper() {
|
||||
ASSERT_IN_VM;
|
||||
InstanceKlass* k = get_instanceKlass();
|
||||
|
||||
for (InternalFieldStream fs(k); !fs.done(); fs.next()) {
|
||||
if (fs.access_flags().is_static()) continue;
|
||||
_has_injected_fields++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool ciInstanceKlass::compute_injected_fields() {
|
||||
assert(_has_injected_fields == -1, "shouldn't be initialized yet");
|
||||
assert(is_loaded(), "must be loaded");
|
||||
|
||||
if (super() != NULL && super()->has_injected_fields()) {
|
||||
_has_injected_fields = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
_has_injected_fields = 0;
|
||||
GUARDED_VM_ENTRY({
|
||||
compute_injected_fields_helper();
|
||||
});
|
||||
|
||||
return _has_injected_fields > 0 ? true : false;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// ciInstanceKlass::find_method
|
||||
//
|
||||
|
||||
@ -64,6 +64,7 @@ private:
|
||||
|
||||
ciConstantPoolCache* _field_cache; // cached map index->field
|
||||
GrowableArray<ciField*>* _nonstatic_fields;
|
||||
int _has_injected_fields; // any non static injected fields? lazily initialized.
|
||||
|
||||
// The possible values of the _implementor fall into following three cases:
|
||||
// NULL: no implementor.
|
||||
@ -71,6 +72,9 @@ private:
|
||||
// Itsef: more than one implementors.
|
||||
ciInstanceKlass* _implementor;
|
||||
|
||||
bool compute_injected_fields();
|
||||
void compute_injected_fields_helper();
|
||||
|
||||
protected:
|
||||
ciInstanceKlass(KlassHandle h_k);
|
||||
ciInstanceKlass(ciSymbol* name, jobject loader, jobject protection_domain);
|
||||
@ -186,6 +190,14 @@ public:
|
||||
else
|
||||
return _nonstatic_fields->length();
|
||||
}
|
||||
|
||||
bool has_injected_fields() {
|
||||
if (_has_injected_fields == -1) {
|
||||
return compute_injected_fields();
|
||||
}
|
||||
return _has_injected_fields > 0 ? true : false;
|
||||
}
|
||||
|
||||
// nth nonstatic field (presented by ascending address)
|
||||
ciField* nonstatic_field_at(int i) {
|
||||
assert(_nonstatic_fields != NULL, "");
|
||||
|
||||
@ -786,17 +786,12 @@ void ClassLoaderDataGraph::clean_metaspaces() {
|
||||
MetadataOnStackMark md_on_stack(has_redefined_a_class);
|
||||
|
||||
if (has_redefined_a_class) {
|
||||
// purge_previous_versions also cleans weak method links. Because
|
||||
// one method's MDO can reference another method from another
|
||||
// class loader, we need to first clean weak method links for all
|
||||
// class loaders here. Below, we can then free redefined methods
|
||||
// for all class loaders.
|
||||
for (ClassLoaderData* data = _head; data != NULL; data = data->next()) {
|
||||
data->classes_do(InstanceKlass::purge_previous_versions);
|
||||
}
|
||||
}
|
||||
|
||||
// Need to purge the previous version before deallocating.
|
||||
// Should purge the previous version before deallocating.
|
||||
free_deallocate_lists();
|
||||
}
|
||||
|
||||
@ -834,8 +829,6 @@ void ClassLoaderDataGraph::post_class_unload_events(void) {
|
||||
|
||||
void ClassLoaderDataGraph::free_deallocate_lists() {
|
||||
for (ClassLoaderData* cld = _head; cld != NULL; cld = cld->next()) {
|
||||
// We need to keep this data until InstanceKlass::purge_previous_version has been
|
||||
// called on all alive classes. See the comment in ClassLoaderDataGraph::clean_metaspaces.
|
||||
cld->free_deallocate_list();
|
||||
}
|
||||
}
|
||||
|
||||
417
hotspot/src/share/vm/classfile/compactHashtable.cpp
Normal file
417
hotspot/src/share/vm/classfile/compactHashtable.cpp
Normal file
@ -0,0 +1,417 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2014, Oracle and/or its affiliates. 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "precompiled.hpp"
|
||||
#include "classfile/javaClasses.hpp"
|
||||
#include "memory/metaspaceShared.hpp"
|
||||
#include "utilities/numberSeq.hpp"
|
||||
#include <sys/stat.h>
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
//
|
||||
// The compact hash table writer implementations
|
||||
//
|
||||
CompactHashtableWriter::CompactHashtableWriter(const char* table_name,
|
||||
int num_entries,
|
||||
CompactHashtableStats* stats) {
|
||||
assert(DumpSharedSpaces, "dump-time only");
|
||||
_table_name = table_name;
|
||||
_num_entries = num_entries;
|
||||
_num_buckets = number_of_buckets(_num_entries);
|
||||
_buckets = NEW_C_HEAP_ARRAY(Entry*, _num_buckets, mtSymbol);
|
||||
memset(_buckets, 0, sizeof(Entry*) * _num_buckets);
|
||||
|
||||
/* bucket sizes table */
|
||||
_bucket_sizes = NEW_C_HEAP_ARRAY(juint, _num_buckets, mtSymbol);
|
||||
memset(_bucket_sizes, 0, sizeof(juint) * _num_buckets);
|
||||
|
||||
stats->hashentry_count = _num_entries;
|
||||
// Compact buckets' entries will have only the 4-byte offset, but
|
||||
// we don't know how many there will be at this point. So use a
|
||||
// conservative estimate here. The size is adjusted later when we
|
||||
// write out the buckets.
|
||||
stats->hashentry_bytes = _num_entries * 8;
|
||||
stats->bucket_count = _num_buckets;
|
||||
stats->bucket_bytes = (_num_buckets + 1) * (sizeof(juint));
|
||||
_stats = stats;
|
||||
|
||||
// See compactHashtable.hpp for table layout
|
||||
_required_bytes = sizeof(juint) * 2; // _base_address, written as 2 juints
|
||||
_required_bytes+= sizeof(juint) + // num_entries
|
||||
sizeof(juint) + // num_buckets
|
||||
stats->hashentry_bytes +
|
||||
stats->bucket_bytes;
|
||||
}
|
||||
|
||||
CompactHashtableWriter::~CompactHashtableWriter() {
|
||||
for (int index = 0; index < _num_buckets; index++) {
|
||||
Entry* next = NULL;
|
||||
for (Entry* tent = _buckets[index]; tent; tent = next) {
|
||||
next = tent->next();
|
||||
delete tent;
|
||||
}
|
||||
}
|
||||
|
||||
FREE_C_HEAP_ARRAY(juint, _bucket_sizes);
|
||||
FREE_C_HEAP_ARRAY(Entry*, _buckets);
|
||||
}
|
||||
|
||||
// Calculate the number of buckets in the temporary hash table
|
||||
int CompactHashtableWriter::number_of_buckets(int num_entries) {
|
||||
const int buksize = (int)SharedSymbolTableBucketSize;
|
||||
int num_buckets = (num_entries + buksize - 1) / buksize;
|
||||
num_buckets = (num_buckets + 1) & (~0x01);
|
||||
|
||||
return num_buckets;
|
||||
}
|
||||
|
||||
// Add a symbol entry to the temporary hash table
|
||||
void CompactHashtableWriter::add(unsigned int hash, Entry* entry) {
|
||||
int index = hash % _num_buckets;
|
||||
entry->set_next(_buckets[index]);
|
||||
_buckets[index] = entry;
|
||||
_bucket_sizes[index] ++;
|
||||
}
|
||||
|
||||
// Write the compact table's bucket infos
|
||||
juint* CompactHashtableWriter::dump_table(juint* p, juint** first_bucket,
|
||||
NumberSeq* summary) {
|
||||
int index;
|
||||
juint* compact_table = p;
|
||||
// Find the start of the buckets, skip the compact_bucket_infos table
|
||||
// and the table end offset.
|
||||
juint offset = _num_buckets + 1;
|
||||
*first_bucket = compact_table + offset;
|
||||
|
||||
for (index = 0; index < _num_buckets; index++) {
|
||||
int bucket_size = _bucket_sizes[index];
|
||||
if (bucket_size == 1) {
|
||||
// bucket with one entry is compacted and only has the symbol offset
|
||||
compact_table[index] = BUCKET_INFO(offset, COMPACT_BUCKET_TYPE);
|
||||
offset += bucket_size; // each entry contains symbol offset only
|
||||
} else {
|
||||
// regular bucket, each entry is a symbol (hash, offset) pair
|
||||
compact_table[index] = BUCKET_INFO(offset, REGULAR_BUCKET_TYPE);
|
||||
offset += bucket_size * 2; // each hash entry is 2 juints
|
||||
}
|
||||
if (offset & ~BUCKET_OFFSET_MASK) {
|
||||
vm_exit_during_initialization("CompactHashtableWriter::dump_table: Overflow! "
|
||||
"Too many symbols.");
|
||||
}
|
||||
summary->add(bucket_size);
|
||||
}
|
||||
// Mark the end of the table
|
||||
compact_table[_num_buckets] = BUCKET_INFO(offset, TABLEEND_BUCKET_TYPE);
|
||||
|
||||
return compact_table;
|
||||
}
|
||||
|
||||
// Write the compact table's entries
|
||||
juint* CompactHashtableWriter::dump_buckets(juint* compact_table, juint* p,
|
||||
NumberSeq* summary) {
|
||||
uintx base_address = uintx(MetaspaceShared::shared_rs()->base());
|
||||
uintx max_delta = uintx(MetaspaceShared::shared_rs()->size());
|
||||
assert(max_delta <= 0x7fffffff, "range check");
|
||||
int num_compact_buckets = 0;
|
||||
|
||||
assert(p != NULL, "sanity");
|
||||
for (int index = 0; index < _num_buckets; index++) {
|
||||
juint count = 0;
|
||||
int bucket_size = _bucket_sizes[index];
|
||||
int bucket_type = BUCKET_TYPE(compact_table[index]);
|
||||
|
||||
if (bucket_size == 1) {
|
||||
assert(bucket_type == COMPACT_BUCKET_TYPE, "Bad bucket type");
|
||||
num_compact_buckets ++;
|
||||
}
|
||||
for (Entry* tent = _buckets[index]; tent;
|
||||
tent = tent->next()) {
|
||||
if (bucket_type == REGULAR_BUCKET_TYPE) {
|
||||
*p++ = juint(tent->hash()); // write symbol hash
|
||||
}
|
||||
uintx deltax = uintx(tent->value()) - base_address;
|
||||
assert(deltax < max_delta, "range check");
|
||||
juint delta = juint(deltax);
|
||||
*p++ = delta; // write symbol offset
|
||||
count ++;
|
||||
}
|
||||
assert(count == _bucket_sizes[index], "sanity");
|
||||
}
|
||||
|
||||
// Adjust the hashentry_bytes in CompactHashtableStats. Each compact
|
||||
// bucket saves 4-byte.
|
||||
_stats->hashentry_bytes -= num_compact_buckets * 4;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
// Write the compact table
|
||||
void CompactHashtableWriter::dump(char** top, char* end) {
|
||||
NumberSeq summary;
|
||||
char* old_top = *top;
|
||||
juint* p = (juint*)(*top);
|
||||
|
||||
uintx base_address = uintx(MetaspaceShared::shared_rs()->base());
|
||||
|
||||
*p++ = high(base_address);
|
||||
*p++ = low (base_address); // base address
|
||||
*p++ = _num_entries; // number of entries in the table
|
||||
*p++ = _num_buckets; // number of buckets in the table
|
||||
|
||||
juint* first_bucket = NULL;
|
||||
juint* compact_table = dump_table(p, &first_bucket, &summary);
|
||||
juint* bucket_end = dump_buckets(compact_table, first_bucket, &summary);
|
||||
|
||||
assert(bucket_end <= (juint*)end, "cannot write past end");
|
||||
*top = (char*)bucket_end;
|
||||
|
||||
if (PrintSharedSpaces) {
|
||||
double avg_cost = 0.0;
|
||||
if (_num_entries > 0) {
|
||||
avg_cost = double(_required_bytes)/double(_num_entries);
|
||||
}
|
||||
tty->print_cr("Shared %s table stats -------- base: " PTR_FORMAT, _table_name, (intptr_t)base_address);
|
||||
tty->print_cr("Number of entries : %9d", _num_entries);
|
||||
tty->print_cr("Total bytes used : %9d", (int)((*top) - old_top));
|
||||
tty->print_cr("Average bytes per entry : %9.3f", avg_cost);
|
||||
tty->print_cr("Average bucket size : %9.3f", summary.avg());
|
||||
tty->print_cr("Variance of bucket size : %9.3f", summary.variance());
|
||||
tty->print_cr("Std. dev. of bucket size: %9.3f", summary.sd());
|
||||
tty->print_cr("Maximum bucket size : %9d", (int)summary.maximum());
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// The CompactHashtable implementation
|
||||
//
|
||||
template <class T, class N> const char* CompactHashtable<T, N>::init(const char* buffer) {
|
||||
assert(!DumpSharedSpaces, "run-time only");
|
||||
juint*p = (juint*)buffer;
|
||||
juint upper = *p++;
|
||||
juint lower = *p++;
|
||||
_base_address = uintx(jlong_from(upper, lower));
|
||||
_entry_count = *p++;
|
||||
_bucket_count = *p++;
|
||||
_buckets = p;
|
||||
_table_end_offset = BUCKET_OFFSET(p[_bucket_count]); // located at the end of the bucket_info table
|
||||
|
||||
juint *end = _buckets + _table_end_offset;
|
||||
return (const char*)end;
|
||||
}
|
||||
|
||||
// Explicitly instantiate these types
|
||||
template class CompactHashtable<Symbol*, char>;
|
||||
|
||||
#ifndef O_BINARY // if defined (Win32) use binary files.
|
||||
#define O_BINARY 0 // otherwise do nothing.
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
//
|
||||
// HashtableTextDump
|
||||
//
|
||||
HashtableTextDump::HashtableTextDump(const char* filename) : _fd(-1) {
|
||||
struct stat st;
|
||||
if (os::stat(filename, &st) != 0) {
|
||||
quit("Unable to get hashtable dump file size", filename);
|
||||
}
|
||||
_size = st.st_size;
|
||||
_fd = open(filename, O_RDONLY | O_BINARY, 0);
|
||||
if (_fd < 0) {
|
||||
quit("Unable to open hashtable dump file", filename);
|
||||
}
|
||||
_base = os::map_memory(_fd, filename, 0, NULL, _size, true, false);
|
||||
if (_base == NULL) {
|
||||
quit("Unable to map hashtable dump file", filename);
|
||||
}
|
||||
_p = _base;
|
||||
_end = _base + st.st_size;
|
||||
_filename = filename;
|
||||
}
|
||||
|
||||
HashtableTextDump::~HashtableTextDump() {
|
||||
os::unmap_memory((char*)_base, _size);
|
||||
if (_fd >= 0) {
|
||||
close(_fd);
|
||||
}
|
||||
}
|
||||
|
||||
void HashtableTextDump::quit(const char* err, const char* msg) {
|
||||
vm_exit_during_initialization(err, msg);
|
||||
}
|
||||
|
||||
void HashtableTextDump::corrupted(const char *p) {
|
||||
char info[60];
|
||||
sprintf(info, "corrupted at pos %d", (int)(p - _base));
|
||||
quit(info, _filename);
|
||||
}
|
||||
|
||||
bool HashtableTextDump::skip_newline() {
|
||||
if (_p[0] == '\r' && _p[1] == '\n') {
|
||||
_p += 2;
|
||||
} else if (_p[0] == '\n') {
|
||||
_p += 1;
|
||||
} else {
|
||||
corrupted(_p);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int HashtableTextDump::skip(char must_be_char) {
|
||||
corrupted_if(remain() < 1);
|
||||
corrupted_if(*_p++ != must_be_char);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void HashtableTextDump::skip_past(char c) {
|
||||
for (;;) {
|
||||
corrupted_if(remain() < 1);
|
||||
if (*_p++ == c) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HashtableTextDump::check_version(const char* ver) {
|
||||
int len = (int)strlen(ver);
|
||||
corrupted_if(remain() < len);
|
||||
if (strncmp(_p, ver, len) != 0) {
|
||||
quit("wrong version of hashtable dump file", _filename);
|
||||
}
|
||||
_p += len;
|
||||
skip_newline();
|
||||
}
|
||||
|
||||
|
||||
int HashtableTextDump::scan_prefix() {
|
||||
// Expect /[0-9]+: /
|
||||
int utf8_length = get_num(':');
|
||||
if (*_p != ' ') {
|
||||
corrupted(_p);
|
||||
}
|
||||
_p++;
|
||||
return utf8_length;
|
||||
}
|
||||
|
||||
int HashtableTextDump::scan_prefix2() {
|
||||
// Expect /[0-9]+ (-|)[0-9]+: /
|
||||
int utf8_length = get_num(' ');
|
||||
if (*_p == '-') {
|
||||
_p++;
|
||||
}
|
||||
(void)get_num(':');
|
||||
if (*_p != ' ') {
|
||||
corrupted(_p);
|
||||
}
|
||||
_p++;
|
||||
return utf8_length;
|
||||
}
|
||||
|
||||
jchar HashtableTextDump::unescape(const char* from, const char* end, int count) {
|
||||
jchar value = 0;
|
||||
|
||||
corrupted_if(from + count > end);
|
||||
|
||||
for (int i=0; i<count; i++) {
|
||||
char c = *from++;
|
||||
switch (c) {
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
value = (value << 4) + c - '0';
|
||||
break;
|
||||
case 'a': case 'b': case 'c':
|
||||
case 'd': case 'e': case 'f':
|
||||
value = (value << 4) + 10 + c - 'a';
|
||||
break;
|
||||
case 'A': case 'B': case 'C':
|
||||
case 'D': case 'E': case 'F':
|
||||
value = (value << 4) + 10 + c - 'A';
|
||||
break;
|
||||
default:
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void HashtableTextDump::get_utf8(char* utf8_buffer, int utf8_length) {
|
||||
// cache in local vars
|
||||
const char* from = _p;
|
||||
const char* end = _end;
|
||||
char* to = utf8_buffer;
|
||||
int n = utf8_length;
|
||||
|
||||
for (; n > 0 && from < end; n--) {
|
||||
if (*from != '\\') {
|
||||
*to++ = *from++;
|
||||
} else {
|
||||
corrupted_if(from + 2 > end);
|
||||
char c = from[1];
|
||||
from += 2;
|
||||
switch (c) {
|
||||
case 'x':
|
||||
{
|
||||
jchar value = unescape(from, end, 2);
|
||||
from += 2;
|
||||
assert(value <= 0xff, "sanity");
|
||||
*to++ = (char)(value & 0xff);
|
||||
}
|
||||
break;
|
||||
case 't': *to++ = '\t'; break;
|
||||
case 'n': *to++ = '\n'; break;
|
||||
case 'r': *to++ = '\r'; break;
|
||||
case '\\': *to++ = '\\'; break;
|
||||
default:
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
}
|
||||
}
|
||||
corrupted_if(n > 0); // expected more chars but file has ended
|
||||
_p = from;
|
||||
skip_newline();
|
||||
}
|
||||
|
||||
// NOTE: the content is NOT the same as
|
||||
// UTF8::as_quoted_ascii(const char* utf8_str, int utf8_length, char* buf, int buflen).
|
||||
// We want to escape \r\n\t so that output [1] is more readable; [2] can be more easily
|
||||
// parsed by scripts; [3] quickly processed by HashtableTextDump::get_utf8()
|
||||
void HashtableTextDump::put_utf8(outputStream* st, const char* utf8_string, int utf8_length) {
|
||||
const char *c = utf8_string;
|
||||
const char *end = c + utf8_length;
|
||||
for (; c < end; c++) {
|
||||
switch (*c) {
|
||||
case '\t': st->print("\\t"); break;
|
||||
case '\r': st->print("\\r"); break;
|
||||
case '\n': st->print("\\n"); break;
|
||||
case '\\': st->print("\\\\"); break;
|
||||
default:
|
||||
if (isprint(*c)) {
|
||||
st->print("%c", *c);
|
||||
} else {
|
||||
st->print("\\x%02x", ((unsigned int)*c) & 0xff);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
405
hotspot/src/share/vm/classfile/compactHashtable.hpp
Normal file
405
hotspot/src/share/vm/classfile/compactHashtable.hpp
Normal file
@ -0,0 +1,405 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2014, Oracle and/or its affiliates. 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHARE_VM_CLASSFILE_COMPACTHASHTABLE_HPP
|
||||
#define SHARE_VM_CLASSFILE_COMPACTHASHTABLE_HPP
|
||||
|
||||
#include "classfile/stringTable.hpp"
|
||||
#include "classfile/symbolTable.hpp"
|
||||
#include "memory/allocation.inline.hpp"
|
||||
#include "oops/symbol.hpp"
|
||||
#include "services/diagnosticCommand.hpp"
|
||||
#include "utilities/hashtable.hpp"
|
||||
|
||||
class NumberSeq;
|
||||
|
||||
// Stats for symbol tables in the CDS archive
|
||||
class CompactHashtableStats VALUE_OBJ_CLASS_SPEC {
|
||||
public:
|
||||
int hashentry_count;
|
||||
int hashentry_bytes;
|
||||
int bucket_count;
|
||||
int bucket_bytes;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// The compact hash table writer. Used at dump time for writing out
|
||||
// the compact table to the shared archive.
|
||||
//
|
||||
// At dump time, the CompactHashtableWriter obtains all entries from the
|
||||
// symbol table and adds them to a new temporary hash table. The hash
|
||||
// table size (number of buckets) is calculated using
|
||||
// '(num_entries + bucket_size - 1) / bucket_size'. The default bucket
|
||||
// size is 4 and can be changed by -XX:SharedSymbolTableBucketSize option.
|
||||
// 4 is chosen because it produces smaller sized bucket on average for
|
||||
// faster lookup. It also has relatively small number of empty buckets and
|
||||
// good distribution of the entries.
|
||||
//
|
||||
// We use a simple hash function (symbol_hash % num_bucket) for the table.
|
||||
// The new table is compacted when written out. Please see comments
|
||||
// above the CompactHashtable class for the table layout detail. The bucket
|
||||
// offsets are written to the archive as part of the compact table. The
|
||||
// bucket offset is encoded in the low 30-bit (0-29) and the bucket type
|
||||
// (regular or compact) are encoded in bit[31, 30]. For buckets with more
|
||||
// than one entry, both symbol hash and symbol offset are written to the
|
||||
// table. For buckets with only one entry, only the symbol offset is written
|
||||
// to the table and the buckets are tagged as compact in their type bits.
|
||||
// Buckets without entry are skipped from the table. Their offsets are
|
||||
// still written out for faster lookup.
|
||||
//
|
||||
class CompactHashtableWriter: public StackObj {
|
||||
public:
|
||||
class Entry: public CHeapObj<mtSymbol> {
|
||||
Entry* _next;
|
||||
unsigned int _hash;
|
||||
void* _literal;
|
||||
|
||||
public:
|
||||
Entry(unsigned int hash, Symbol *symbol) : _next(NULL), _hash(hash), _literal(symbol) {}
|
||||
|
||||
void *value() {
|
||||
return _literal;
|
||||
}
|
||||
Symbol *symbol() {
|
||||
return (Symbol*)_literal;
|
||||
}
|
||||
unsigned int hash() {
|
||||
return _hash;
|
||||
}
|
||||
Entry *next() {return _next;}
|
||||
void set_next(Entry *p) {_next = p;}
|
||||
}; // class CompactHashtableWriter::Entry
|
||||
|
||||
private:
|
||||
static int number_of_buckets(int num_entries);
|
||||
|
||||
const char* _table_name;
|
||||
int _num_entries;
|
||||
int _num_buckets;
|
||||
juint* _bucket_sizes;
|
||||
Entry** _buckets;
|
||||
int _required_bytes;
|
||||
CompactHashtableStats* _stats;
|
||||
|
||||
public:
|
||||
// This is called at dump-time only
|
||||
CompactHashtableWriter(const char* table_name, int num_entries, CompactHashtableStats* stats);
|
||||
~CompactHashtableWriter();
|
||||
|
||||
int get_required_bytes() {
|
||||
return _required_bytes;
|
||||
}
|
||||
|
||||
void add(unsigned int hash, Symbol* symbol) {
|
||||
add(hash, new Entry(hash, symbol));
|
||||
}
|
||||
|
||||
private:
|
||||
void add(unsigned int hash, Entry* entry);
|
||||
juint* dump_table(juint* p, juint** first_bucket, NumberSeq* summary);
|
||||
juint* dump_buckets(juint* table, juint* p, NumberSeq* summary);
|
||||
|
||||
public:
|
||||
void dump(char** top, char* end);
|
||||
};
|
||||
|
||||
#define REGULAR_BUCKET_TYPE 0
|
||||
#define COMPACT_BUCKET_TYPE 1
|
||||
#define TABLEEND_BUCKET_TYPE 3
|
||||
#define BUCKET_OFFSET_MASK 0x3FFFFFFF
|
||||
#define BUCKET_OFFSET(info) ((info) & BUCKET_OFFSET_MASK)
|
||||
#define BUCKET_TYPE_SHIFT 30
|
||||
#define BUCKET_TYPE(info) (((info) & ~BUCKET_OFFSET_MASK) >> BUCKET_TYPE_SHIFT)
|
||||
#define BUCKET_INFO(offset, type) (((type) << BUCKET_TYPE_SHIFT) | ((offset) & BUCKET_OFFSET_MASK))
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// CompactHashtable is used to stored the CDS archive's symbol table. Used
|
||||
// at runtime only to access the compact table from the archive.
|
||||
//
|
||||
// Because these tables are read-only (no entries can be added/deleted) at run-time
|
||||
// and tend to have large number of entries, we try to minimize the footprint
|
||||
// cost per entry.
|
||||
//
|
||||
// Layout of compact symbol table in the shared archive:
|
||||
//
|
||||
// uintx base_address;
|
||||
// juint num_symbols;
|
||||
// juint num_buckets;
|
||||
// juint bucket_infos[num_buckets+1]; // bit[31,30]: type; bit[29-0]: offset
|
||||
// juint table[]
|
||||
//
|
||||
// -----------------------------------
|
||||
// | base_address | num_symbols |
|
||||
// |---------------------------------|
|
||||
// | num_buckets | bucket_info0 |
|
||||
// |---------------------------------|
|
||||
// | bucket_info1 | bucket_info2 |
|
||||
// | bucket_info3 ... |
|
||||
// | .... | table_end_info |
|
||||
// |---------------------------------|
|
||||
// | entry0 |
|
||||
// | entry1 |
|
||||
// | entry2 |
|
||||
// | |
|
||||
// | ... |
|
||||
// -----------------------------------
|
||||
//
|
||||
// The size of the bucket_info table is 'num_buckets + 1'. Each entry of the
|
||||
// bucket_info table is a 32-bit encoding of the bucket type and bucket offset,
|
||||
// with the type in the left-most 2-bit and offset in the remaining 30-bit.
|
||||
// The last entry is a special type. It contains the offset of the last
|
||||
// bucket end. We use that information when traversing the compact table.
|
||||
//
|
||||
// There are two types of buckets, regular buckets and compact buckets. The
|
||||
// compact buckets have '01' in their highest 2-bit, and regular buckets have
|
||||
// '00' in their highest 2-bit.
|
||||
//
|
||||
// For normal buckets, each symbol's entry is 8 bytes in the table[]:
|
||||
// juint hash; /* symbol hash */
|
||||
// juint offset; /* Symbol* sym = (Symbol*)(base_address + offset) */
|
||||
//
|
||||
// For compact buckets, each entry has only the 4-byte 'offset' in the table[].
|
||||
//
|
||||
// See CompactHashtable::lookup() for how the table is searched at runtime.
|
||||
// See CompactHashtableWriter::dump() for how the table is written at CDS
|
||||
// dump time.
|
||||
//
|
||||
template <class T, class N> class CompactHashtable VALUE_OBJ_CLASS_SPEC {
|
||||
uintx _base_address;
|
||||
juint _entry_count;
|
||||
juint _bucket_count;
|
||||
juint _table_end_offset;
|
||||
juint* _buckets;
|
||||
|
||||
inline bool equals(T entry, const char* name, int len) {
|
||||
if (entry->equals(name, len)) {
|
||||
assert(entry->refcount() == -1, "must be shared");
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
CompactHashtable() {
|
||||
_entry_count = 0;
|
||||
_bucket_count = 0;
|
||||
_table_end_offset = 0;
|
||||
_buckets = 0;
|
||||
}
|
||||
const char* init(const char *buffer);
|
||||
|
||||
// Lookup an entry from the compact table
|
||||
inline T lookup(const N* name, unsigned int hash, int len) {
|
||||
if (_entry_count > 0) {
|
||||
assert(!DumpSharedSpaces, "run-time only");
|
||||
int index = hash % _bucket_count;
|
||||
juint bucket_info = _buckets[index];
|
||||
juint bucket_offset = BUCKET_OFFSET(bucket_info);
|
||||
int bucket_type = BUCKET_TYPE(bucket_info);
|
||||
juint* bucket = _buckets + bucket_offset;
|
||||
juint* bucket_end = _buckets;
|
||||
|
||||
if (bucket_type == COMPACT_BUCKET_TYPE) {
|
||||
// the compact bucket has one entry with symbol offset only
|
||||
T entry = (T)((void*)(_base_address + bucket[0]));
|
||||
if (equals(entry, name, len)) {
|
||||
return entry;
|
||||
}
|
||||
} else {
|
||||
// This is a regular bucket, which has more than one
|
||||
// entries. Each entry is a pair of symbol (hash, offset).
|
||||
// Seek until the end of the bucket.
|
||||
bucket_end += BUCKET_OFFSET(_buckets[index + 1]);
|
||||
while (bucket < bucket_end) {
|
||||
unsigned int h = (unsigned int)(bucket[0]);
|
||||
if (h == hash) {
|
||||
juint offset = bucket[1];
|
||||
T entry = (T)((void*)(_base_address + offset));
|
||||
if (equals(entry, name, len)) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
bucket += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Read/Write the contents of a hashtable textual dump (created by
|
||||
// SymbolTable::dump).
|
||||
// Because the dump file may be big (hundred of MB in extreme cases),
|
||||
// we use mmap for fast access when reading it.
|
||||
//
|
||||
class HashtableTextDump VALUE_OBJ_CLASS_SPEC {
|
||||
int _fd;
|
||||
const char* _base;
|
||||
const char* _p;
|
||||
const char* _end;
|
||||
const char* _filename;
|
||||
size_t _size;
|
||||
public:
|
||||
HashtableTextDump(const char* filename);
|
||||
~HashtableTextDump();
|
||||
|
||||
void quit(const char* err, const char* msg);
|
||||
|
||||
inline int remain() {
|
||||
return (int)(_end - _p);
|
||||
}
|
||||
|
||||
void corrupted(const char *p);
|
||||
|
||||
inline void corrupted_if(bool cond) {
|
||||
if (cond) {
|
||||
corrupted(_p);
|
||||
}
|
||||
}
|
||||
|
||||
bool skip_newline();
|
||||
int skip(char must_be_char);
|
||||
void skip_past(char c);
|
||||
void check_version(const char* ver);
|
||||
|
||||
inline int get_num(char delim) {
|
||||
const char* p = _p;
|
||||
const char* end = _end;
|
||||
int num = 0;
|
||||
|
||||
while (p < end) {
|
||||
char c = *p ++;
|
||||
if ('0' <= c && c <= '9') {
|
||||
num = num * 10 + (c - '0');
|
||||
} else if (c == delim) {
|
||||
_p = p;
|
||||
return num;
|
||||
} else {
|
||||
corrupted(p-1);
|
||||
}
|
||||
}
|
||||
corrupted(_end);
|
||||
ShouldNotReachHere();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int scan_prefix();
|
||||
int scan_prefix2();
|
||||
|
||||
jchar unescape(const char* from, const char* end, int count);
|
||||
void get_utf8(char* utf8_buffer, int utf8_length);
|
||||
static void put_utf8(outputStream* st, const char* utf8_string, int utf8_length);
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// jcmd command support for symbol table and string table dumping:
|
||||
// VM.symboltable -verbose: for dumping the symbol table
|
||||
// VM.stringtable -verbose: for dumping the string table
|
||||
//
|
||||
class VM_DumpHashtable : public VM_Operation {
|
||||
private:
|
||||
outputStream* _out;
|
||||
int _which;
|
||||
bool _verbose;
|
||||
public:
|
||||
enum {
|
||||
DumpSymbols = 1 << 0,
|
||||
DumpStrings = 1 << 1,
|
||||
DumpSysDict = 1 << 2 // not implemented yet
|
||||
};
|
||||
VM_DumpHashtable(outputStream* out, int which, bool verbose) {
|
||||
_out = out;
|
||||
_which = which;
|
||||
_verbose = verbose;
|
||||
}
|
||||
|
||||
virtual VMOp_Type type() const { return VMOp_DumpHashtable; }
|
||||
|
||||
virtual void doit() {
|
||||
switch (_which) {
|
||||
case DumpSymbols:
|
||||
SymbolTable::dump(_out, _verbose);
|
||||
break;
|
||||
case DumpStrings:
|
||||
StringTable::dump(_out, _verbose);
|
||||
break;
|
||||
default:
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class SymboltableDCmd : public DCmdWithParser {
|
||||
protected:
|
||||
DCmdArgument<bool> _verbose;
|
||||
public:
|
||||
SymboltableDCmd(outputStream* output, bool heap);
|
||||
static const char* name() {
|
||||
return "VM.symboltable";
|
||||
}
|
||||
static const char* description() {
|
||||
return "Dump symbol table.";
|
||||
}
|
||||
static const char* impact() {
|
||||
return "Medium: Depends on Java content.";
|
||||
}
|
||||
static const JavaPermission permission() {
|
||||
JavaPermission p = {"java.lang.management.ManagementPermission",
|
||||
"monitor", NULL};
|
||||
return p;
|
||||
}
|
||||
static int num_arguments();
|
||||
virtual void execute(DCmdSource source, TRAPS);
|
||||
};
|
||||
|
||||
class StringtableDCmd : public DCmdWithParser {
|
||||
protected:
|
||||
DCmdArgument<bool> _verbose;
|
||||
public:
|
||||
StringtableDCmd(outputStream* output, bool heap);
|
||||
static const char* name() {
|
||||
return "VM.stringtable";
|
||||
}
|
||||
static const char* description() {
|
||||
return "Dump string table.";
|
||||
}
|
||||
static const char* impact() {
|
||||
return "Medium: Depends on Java content.";
|
||||
}
|
||||
static const JavaPermission permission() {
|
||||
JavaPermission p = {"java.lang.management.ManagementPermission",
|
||||
"monitor", NULL};
|
||||
return p;
|
||||
}
|
||||
static int num_arguments();
|
||||
virtual void execute(DCmdSource source, TRAPS);
|
||||
};
|
||||
|
||||
#endif // SHARE_VM_CLASSFILE_COMPACTHASHTABLE_HPP
|
||||
@ -24,6 +24,7 @@
|
||||
|
||||
#include "precompiled.hpp"
|
||||
#include "classfile/altHashing.hpp"
|
||||
#include "classfile/compactHashtable.hpp"
|
||||
#include "classfile/javaClasses.hpp"
|
||||
#include "classfile/stringTable.hpp"
|
||||
#include "classfile/systemDictionary.hpp"
|
||||
@ -379,8 +380,36 @@ void StringTable::verify() {
|
||||
}
|
||||
}
|
||||
|
||||
void StringTable::dump(outputStream* st) {
|
||||
the_table()->dump_table(st, "StringTable");
|
||||
void StringTable::dump(outputStream* st, bool verbose) {
|
||||
if (!verbose) {
|
||||
the_table()->dump_table(st, "StringTable");
|
||||
} else {
|
||||
Thread* THREAD = Thread::current();
|
||||
st->print_cr("VERSION: 1.1");
|
||||
for (int i = 0; i < the_table()->table_size(); ++i) {
|
||||
HashtableEntry<oop, mtSymbol>* p = the_table()->bucket(i);
|
||||
for ( ; p != NULL; p = p->next()) {
|
||||
oop s = p->literal();
|
||||
typeArrayOop value = java_lang_String::value(s);
|
||||
int offset = java_lang_String::offset(s);
|
||||
int length = java_lang_String::length(s);
|
||||
|
||||
if (length <= 0) {
|
||||
st->print("%d: ", length);
|
||||
} else {
|
||||
ResourceMark rm(THREAD);
|
||||
jchar* chars = (jchar*)value->char_at_addr(offset);
|
||||
int utf8_length = UNICODE::utf8_length(chars, length);
|
||||
char* utf8_string = NEW_RESOURCE_ARRAY(char, utf8_length + 1);
|
||||
UNICODE::convert_to_utf8(chars, length, utf8_string);
|
||||
|
||||
st->print("%d: ", utf8_length);
|
||||
HashtableTextDump::put_utf8(st, utf8_string, utf8_length);
|
||||
}
|
||||
st->cr();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StringTable::VerifyRetTypes StringTable::compare_entries(
|
||||
@ -558,3 +587,28 @@ void StringTable::rehash_table() {
|
||||
_needs_rehashing = false;
|
||||
_the_table = new_table;
|
||||
}
|
||||
|
||||
// Utility for dumping strings
|
||||
StringtableDCmd::StringtableDCmd(outputStream* output, bool heap) :
|
||||
DCmdWithParser(output, heap),
|
||||
_verbose("-verbose", "Dump the content of each string in the table",
|
||||
"BOOLEAN", false, "false") {
|
||||
_dcmdparser.add_dcmd_option(&_verbose);
|
||||
}
|
||||
|
||||
void StringtableDCmd::execute(DCmdSource source, TRAPS) {
|
||||
VM_DumpHashtable dumper(output(), VM_DumpHashtable::DumpStrings,
|
||||
_verbose.value());
|
||||
VMThread::execute(&dumper);
|
||||
}
|
||||
|
||||
int StringtableDCmd::num_arguments() {
|
||||
ResourceMark rm;
|
||||
StringtableDCmd* dcmd = new StringtableDCmd(NULL, false);
|
||||
if (dcmd != NULL) {
|
||||
DCmdMark mark(dcmd);
|
||||
return dcmd->_dcmdparser.num_arguments();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,7 +118,7 @@ public:
|
||||
|
||||
// Debugging
|
||||
static void verify();
|
||||
static void dump(outputStream* st);
|
||||
static void dump(outputStream* st, bool verbose=false);
|
||||
|
||||
enum VerifyMesgModes {
|
||||
_verify_quietly = 0,
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
|
||||
#include "precompiled.hpp"
|
||||
#include "classfile/altHashing.hpp"
|
||||
#include "classfile/compactHashtable.hpp"
|
||||
#include "classfile/javaClasses.hpp"
|
||||
#include "classfile/symbolTable.hpp"
|
||||
#include "classfile/systemDictionary.hpp"
|
||||
@ -47,6 +48,9 @@ SymbolTable* SymbolTable::_the_table = NULL;
|
||||
// Static arena for symbols that are not deallocated
|
||||
Arena* SymbolTable::_arena = NULL;
|
||||
bool SymbolTable::_needs_rehashing = false;
|
||||
bool SymbolTable::_lookup_shared_first = false;
|
||||
|
||||
CompactHashtable<Symbol*, char> SymbolTable::_shared_table;
|
||||
|
||||
Symbol* SymbolTable::allocate_symbol(const u1* name, int len, bool c_heap, TRAPS) {
|
||||
assert (len <= Symbol::max_length(), "should be checked by caller");
|
||||
@ -186,8 +190,8 @@ void SymbolTable::rehash_table() {
|
||||
|
||||
// Lookup a symbol in a bucket.
|
||||
|
||||
Symbol* SymbolTable::lookup(int index, const char* name,
|
||||
int len, unsigned int hash) {
|
||||
Symbol* SymbolTable::lookup_dynamic(int index, const char* name,
|
||||
int len, unsigned int hash) {
|
||||
int count = 0;
|
||||
for (HashtableEntry<Symbol*, mtSymbol>* e = bucket(index); e != NULL; e = e->next()) {
|
||||
count++; // count all entries in this bucket, not just ones with same hash
|
||||
@ -207,6 +211,34 @@ Symbol* SymbolTable::lookup(int index, const char* name,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Symbol* SymbolTable::lookup_shared(const char* name,
|
||||
int len, unsigned int hash) {
|
||||
return _shared_table.lookup(name, hash, len);
|
||||
}
|
||||
|
||||
Symbol* SymbolTable::lookup(int index, const char* name,
|
||||
int len, unsigned int hash) {
|
||||
Symbol* sym;
|
||||
if (_lookup_shared_first) {
|
||||
sym = lookup_shared(name, len, hash);
|
||||
if (sym != NULL) {
|
||||
return sym;
|
||||
}
|
||||
_lookup_shared_first = false;
|
||||
return lookup_dynamic(index, name, len, hash);
|
||||
} else {
|
||||
sym = lookup_dynamic(index, name, len, hash);
|
||||
if (sym != NULL) {
|
||||
return sym;
|
||||
}
|
||||
sym = lookup_shared(name, len, hash);
|
||||
if (sym != NULL) {
|
||||
_lookup_shared_first = true;
|
||||
}
|
||||
return sym;
|
||||
}
|
||||
}
|
||||
|
||||
// Pick hashing algorithm.
|
||||
unsigned int SymbolTable::hash_symbol(const char* s, int len) {
|
||||
return use_alternate_hashcode() ?
|
||||
@ -483,10 +515,56 @@ void SymbolTable::verify() {
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolTable::dump(outputStream* st) {
|
||||
the_table()->dump_table(st, "SymbolTable");
|
||||
void SymbolTable::dump(outputStream* st, bool verbose) {
|
||||
if (!verbose) {
|
||||
the_table()->dump_table(st, "SymbolTable");
|
||||
} else {
|
||||
st->print_cr("VERSION: 1.0");
|
||||
for (int i = 0; i < the_table()->table_size(); ++i) {
|
||||
HashtableEntry<Symbol*, mtSymbol>* p = the_table()->bucket(i);
|
||||
for ( ; p != NULL; p = p->next()) {
|
||||
Symbol* s = (Symbol*)(p->literal());
|
||||
const char* utf8_string = (const char*)s->bytes();
|
||||
int utf8_length = s->utf8_length();
|
||||
st->print("%d %d: ", utf8_length, s->refcount());
|
||||
HashtableTextDump::put_utf8(st, utf8_string, utf8_length);
|
||||
st->cr();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SymbolTable::copy_compact_table(char** top, char*end) {
|
||||
#if INCLUDE_CDS
|
||||
CompactHashtableWriter ch_table("symbol", the_table()->number_of_entries(),
|
||||
&MetaspaceShared::stats()->symbol);
|
||||
if (*top + ch_table.get_required_bytes() > end) {
|
||||
// not enough space left
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < the_table()->table_size(); ++i) {
|
||||
HashtableEntry<Symbol*, mtSymbol>* p = the_table()->bucket(i);
|
||||
for ( ; p != NULL; p = p->next()) {
|
||||
Symbol* s = (Symbol*)(p->literal());
|
||||
unsigned int fixed_hash = hash_symbol((char*)s->bytes(), s->utf8_length());
|
||||
assert(fixed_hash == p->hash(), "must not rehash during dumping");
|
||||
ch_table.add(fixed_hash, s);
|
||||
}
|
||||
}
|
||||
|
||||
char* old_top = *top;
|
||||
ch_table.dump(top, end);
|
||||
|
||||
*top = (char*)align_pointer_up(*top, sizeof(void*));
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* SymbolTable::init_shared_table(const char* buffer) {
|
||||
const char* end = _shared_table.init(buffer);
|
||||
return (const char*)align_pointer_up(end, sizeof(void*));
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Non-product code
|
||||
@ -574,3 +652,29 @@ void SymbolTable::print() {
|
||||
}
|
||||
}
|
||||
#endif // PRODUCT
|
||||
|
||||
|
||||
// Utility for dumping symbols
|
||||
SymboltableDCmd::SymboltableDCmd(outputStream* output, bool heap) :
|
||||
DCmdWithParser(output, heap),
|
||||
_verbose("-verbose", "Dump the content of each symbol in the table",
|
||||
"BOOLEAN", false, "false") {
|
||||
_dcmdparser.add_dcmd_option(&_verbose);
|
||||
}
|
||||
|
||||
void SymboltableDCmd::execute(DCmdSource source, TRAPS) {
|
||||
VM_DumpHashtable dumper(output(), VM_DumpHashtable::DumpSymbols,
|
||||
_verbose.value());
|
||||
VMThread::execute(&dumper);
|
||||
}
|
||||
|
||||
int SymboltableDCmd::num_arguments() {
|
||||
ResourceMark rm;
|
||||
SymboltableDCmd* dcmd = new SymboltableDCmd(NULL, false);
|
||||
if (dcmd != NULL) {
|
||||
DCmdMark mark(dcmd);
|
||||
return dcmd->_dcmdparser.num_arguments();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,6 +73,8 @@ class TempNewSymbol : public StackObj {
|
||||
operator Symbol*() { return _temp; }
|
||||
};
|
||||
|
||||
template <class T, class N> class CompactHashtable;
|
||||
|
||||
class SymbolTable : public RehashableHashtable<Symbol*, mtSymbol> {
|
||||
friend class VMStructs;
|
||||
friend class ClassFileParser;
|
||||
@ -83,11 +85,15 @@ private:
|
||||
|
||||
// Set if one bucket is out of balance due to hash algorithm deficiency
|
||||
static bool _needs_rehashing;
|
||||
static bool _lookup_shared_first;
|
||||
|
||||
// For statistics
|
||||
static int _symbols_removed;
|
||||
static int _symbols_counted;
|
||||
|
||||
// shared symbol table.
|
||||
static CompactHashtable<Symbol*, char> _shared_table;
|
||||
|
||||
Symbol* allocate_symbol(const u1* name, int len, bool c_heap, TRAPS); // Assumes no characters larger than 0x7F
|
||||
|
||||
// Adding elements
|
||||
@ -106,6 +112,8 @@ private:
|
||||
add(loader_data, cp, names_count, name, lengths, cp_indices, hashValues, THREAD);
|
||||
}
|
||||
|
||||
static Symbol* lookup_shared(const char* name, int len, unsigned int hash);
|
||||
Symbol* lookup_dynamic(int index, const char* name, int len, unsigned int hash);
|
||||
Symbol* lookup(int index, const char* name, int len, unsigned int hash);
|
||||
|
||||
SymbolTable()
|
||||
@ -144,20 +152,6 @@ public:
|
||||
initialize_symbols(symbol_alloc_arena_size);
|
||||
}
|
||||
|
||||
static void create_table(HashtableBucket<mtSymbol>* t, int length,
|
||||
int number_of_entries) {
|
||||
assert(_the_table == NULL, "One symbol table allowed.");
|
||||
|
||||
// If CDS archive used a different symbol table size, use that size instead
|
||||
// which is better than giving an error.
|
||||
SymbolTableSize = length/bucket_size();
|
||||
|
||||
_the_table = new SymbolTable(t, number_of_entries);
|
||||
// if CDS give symbol table a default arena size since most symbols
|
||||
// are already allocated in the shared misc section.
|
||||
initialize_symbols();
|
||||
}
|
||||
|
||||
static unsigned int hash_symbol(const char* s, int len);
|
||||
|
||||
static Symbol* lookup(const char* name, int len, TRAPS);
|
||||
@ -230,18 +224,12 @@ public:
|
||||
|
||||
// Debugging
|
||||
static void verify();
|
||||
static void dump(outputStream* st);
|
||||
static void dump(outputStream* st, bool verbose=false);
|
||||
static void read(const char* filename, TRAPS);
|
||||
|
||||
// Sharing
|
||||
static void copy_buckets(char** top, char*end) {
|
||||
the_table()->Hashtable<Symbol*, mtSymbol>::copy_buckets(top, end);
|
||||
}
|
||||
static void copy_table(char** top, char*end) {
|
||||
the_table()->Hashtable<Symbol*, mtSymbol>::copy_table(top, end);
|
||||
}
|
||||
static void reverse(void* boundary = NULL) {
|
||||
the_table()->Hashtable<Symbol*, mtSymbol>::reverse(boundary);
|
||||
}
|
||||
static bool copy_compact_table(char** top, char* end);
|
||||
static const char* init_shared_table(const char* buffer);
|
||||
|
||||
// Rehash the symbol table if it gets out of balance
|
||||
static void rehash_table();
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2015, Oracle and/or its affiliates. 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
|
||||
@ -32,6 +32,7 @@
|
||||
#include "classfile/stringTable.hpp"
|
||||
#include "classfile/systemDictionary.hpp"
|
||||
#include "classfile/vmSymbols.hpp"
|
||||
#include "code/codeCache.hpp"
|
||||
#include "compiler/compileBroker.hpp"
|
||||
#include "interpreter/bytecodeStream.hpp"
|
||||
#include "interpreter/interpreter.hpp"
|
||||
@ -1627,7 +1628,7 @@ void SystemDictionary::add_to_hierarchy(instanceKlassHandle k, TRAPS) {
|
||||
// Note: must be done *after* linking k into the hierarchy (was bug 12/9/97)
|
||||
// Also, first reinitialize vtable because it may have gotten out of synch
|
||||
// while the new class wasn't connected to the class hierarchy.
|
||||
Universe::flush_dependents_on(k);
|
||||
CodeCache::flush_dependents_on(k);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -1904,11 +1905,12 @@ void SystemDictionary::initialize_preloaded_classes(TRAPS) {
|
||||
InstanceKlass::cast(WK_KLASS(Reference_klass))->set_reference_type(REF_OTHER);
|
||||
InstanceRefKlass::update_nonstatic_oop_maps(WK_KLASS(Reference_klass));
|
||||
|
||||
initialize_wk_klasses_through(WK_KLASS_ENUM_NAME(PhantomReference_klass), scan, CHECK);
|
||||
initialize_wk_klasses_through(WK_KLASS_ENUM_NAME(Cleaner_klass), scan, CHECK);
|
||||
InstanceKlass::cast(WK_KLASS(SoftReference_klass))->set_reference_type(REF_SOFT);
|
||||
InstanceKlass::cast(WK_KLASS(WeakReference_klass))->set_reference_type(REF_WEAK);
|
||||
InstanceKlass::cast(WK_KLASS(FinalReference_klass))->set_reference_type(REF_FINAL);
|
||||
InstanceKlass::cast(WK_KLASS(PhantomReference_klass))->set_reference_type(REF_PHANTOM);
|
||||
InstanceKlass::cast(WK_KLASS(Cleaner_klass))->set_reference_type(REF_CLEANER);
|
||||
|
||||
// JSR 292 classes
|
||||
WKID jsr292_group_start = WK_KLASS_ENUM_NAME(MethodHandle_klass);
|
||||
|
||||
@ -128,6 +128,7 @@ class Ticks;
|
||||
do_klass(WeakReference_klass, java_lang_ref_WeakReference, Pre ) \
|
||||
do_klass(FinalReference_klass, java_lang_ref_FinalReference, Pre ) \
|
||||
do_klass(PhantomReference_klass, java_lang_ref_PhantomReference, Pre ) \
|
||||
do_klass(Cleaner_klass, sun_misc_Cleaner, Pre ) \
|
||||
do_klass(Finalizer_klass, java_lang_ref_Finalizer, Pre ) \
|
||||
\
|
||||
do_klass(Thread_klass, java_lang_Thread, Pre ) \
|
||||
|
||||
@ -1555,14 +1555,14 @@ void ClassVerifier::verify_method(methodHandle m, TRAPS) {
|
||||
case Bytecodes::_invokespecial :
|
||||
case Bytecodes::_invokestatic :
|
||||
verify_invoke_instructions(
|
||||
&bcs, code_length, ¤t_frame,
|
||||
&this_uninit, return_type, cp, CHECK_VERIFY(this));
|
||||
&bcs, code_length, ¤t_frame, (bci >= ex_min && bci < ex_max),
|
||||
&this_uninit, return_type, cp, &stackmap_table, CHECK_VERIFY(this));
|
||||
no_control_flow = false; break;
|
||||
case Bytecodes::_invokeinterface :
|
||||
case Bytecodes::_invokedynamic :
|
||||
verify_invoke_instructions(
|
||||
&bcs, code_length, ¤t_frame,
|
||||
&this_uninit, return_type, cp, CHECK_VERIFY(this));
|
||||
&bcs, code_length, ¤t_frame, (bci >= ex_min && bci < ex_max),
|
||||
&this_uninit, return_type, cp, &stackmap_table, CHECK_VERIFY(this));
|
||||
no_control_flow = false; break;
|
||||
case Bytecodes::_new :
|
||||
{
|
||||
@ -2406,8 +2406,9 @@ bool ClassVerifier::ends_in_athrow(u4 start_bc_offset) {
|
||||
|
||||
void ClassVerifier::verify_invoke_init(
|
||||
RawBytecodeStream* bcs, u2 ref_class_index, VerificationType ref_class_type,
|
||||
StackMapFrame* current_frame, u4 code_length, bool *this_uninit,
|
||||
constantPoolHandle cp, TRAPS) {
|
||||
StackMapFrame* current_frame, u4 code_length, bool in_try_block,
|
||||
bool *this_uninit, constantPoolHandle cp, StackMapTable* stackmap_table,
|
||||
TRAPS) {
|
||||
u2 bci = bcs->bci();
|
||||
VerificationType type = current_frame->pop_stack(
|
||||
VerificationType::reference_check(), CHECK_VERIFY(this));
|
||||
@ -2423,28 +2424,36 @@ void ClassVerifier::verify_invoke_init(
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this call is done from inside of a TRY block. If so, make
|
||||
// sure that all catch clause paths end in a throw. Otherwise, this
|
||||
// can result in returning an incomplete object.
|
||||
ExceptionTable exhandlers(_method());
|
||||
int exlength = exhandlers.length();
|
||||
for(int i = 0; i < exlength; i++) {
|
||||
u2 start_pc = exhandlers.start_pc(i);
|
||||
u2 end_pc = exhandlers.end_pc(i);
|
||||
// If this invokespecial call is done from inside of a TRY block then make
|
||||
// sure that all catch clause paths end in a throw. Otherwise, this can
|
||||
// result in returning an incomplete object.
|
||||
if (in_try_block) {
|
||||
ExceptionTable exhandlers(_method());
|
||||
int exlength = exhandlers.length();
|
||||
for(int i = 0; i < exlength; i++) {
|
||||
u2 start_pc = exhandlers.start_pc(i);
|
||||
u2 end_pc = exhandlers.end_pc(i);
|
||||
|
||||
if (bci >= start_pc && bci < end_pc) {
|
||||
if (!ends_in_athrow(exhandlers.handler_pc(i))) {
|
||||
verify_error(ErrorContext::bad_code(bci),
|
||||
"Bad <init> method call from after the start of a try block");
|
||||
return;
|
||||
} else if (VerboseVerification) {
|
||||
ResourceMark rm;
|
||||
tty->print_cr(
|
||||
"Survived call to ends_in_athrow(): %s",
|
||||
current_class()->name()->as_C_string());
|
||||
if (bci >= start_pc && bci < end_pc) {
|
||||
if (!ends_in_athrow(exhandlers.handler_pc(i))) {
|
||||
verify_error(ErrorContext::bad_code(bci),
|
||||
"Bad <init> method call from after the start of a try block");
|
||||
return;
|
||||
} else if (VerboseVerification) {
|
||||
ResourceMark rm;
|
||||
tty->print_cr(
|
||||
"Survived call to ends_in_athrow(): %s",
|
||||
current_class()->name()->as_C_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check the exception handler target stackmaps with the locals from the
|
||||
// incoming stackmap (before initialize_object() changes them to outgoing
|
||||
// state).
|
||||
verify_exception_handler_targets(bci, true, current_frame,
|
||||
stackmap_table, CHECK_VERIFY(this));
|
||||
} // in_try_block
|
||||
|
||||
current_frame->initialize_object(type, current_type());
|
||||
*this_uninit = true;
|
||||
@ -2477,8 +2486,7 @@ void ClassVerifier::verify_invoke_init(
|
||||
// of the current class.
|
||||
VerificationType objectref_type = new_class_type;
|
||||
if (name_in_supers(ref_class_type.name(), current_class())) {
|
||||
Klass* ref_klass = load_class(
|
||||
ref_class_type.name(), CHECK_VERIFY(this));
|
||||
Klass* ref_klass = load_class(ref_class_type.name(), CHECK);
|
||||
Method* m = InstanceKlass::cast(ref_klass)->uncached_lookup_method(
|
||||
vmSymbols::object_initializer_name(),
|
||||
cp->signature_ref_at(bcs->get_index_u2()),
|
||||
@ -2499,6 +2507,13 @@ void ClassVerifier::verify_invoke_init(
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check the exception handler target stackmaps with the locals from the
|
||||
// incoming stackmap (before initialize_object() changes them to outgoing
|
||||
// state).
|
||||
if (in_try_block) {
|
||||
verify_exception_handler_targets(bci, *this_uninit, current_frame,
|
||||
stackmap_table, CHECK_VERIFY(this));
|
||||
}
|
||||
current_frame->initialize_object(type, new_class_type);
|
||||
} else {
|
||||
verify_error(ErrorContext::bad_type(bci, current_frame->stack_top_ctx()),
|
||||
@ -2527,8 +2542,8 @@ bool ClassVerifier::is_same_or_direct_interface(
|
||||
|
||||
void ClassVerifier::verify_invoke_instructions(
|
||||
RawBytecodeStream* bcs, u4 code_length, StackMapFrame* current_frame,
|
||||
bool *this_uninit, VerificationType return_type,
|
||||
constantPoolHandle cp, TRAPS) {
|
||||
bool in_try_block, bool *this_uninit, VerificationType return_type,
|
||||
constantPoolHandle cp, StackMapTable* stackmap_table, TRAPS) {
|
||||
// Make sure the constant pool item is the right type
|
||||
u2 index = bcs->get_index_u2();
|
||||
Bytecodes::Code opcode = bcs->raw_code();
|
||||
@ -2694,7 +2709,8 @@ void ClassVerifier::verify_invoke_instructions(
|
||||
opcode != Bytecodes::_invokedynamic) {
|
||||
if (method_name == vmSymbols::object_initializer_name()) { // <init> method
|
||||
verify_invoke_init(bcs, index, ref_class_type, current_frame,
|
||||
code_length, this_uninit, cp, CHECK_VERIFY(this));
|
||||
code_length, in_try_block, this_uninit, cp, stackmap_table,
|
||||
CHECK_VERIFY(this));
|
||||
} else { // other methods
|
||||
// Ensures that target class is assignable to method class.
|
||||
if (opcode == Bytecodes::_invokespecial) {
|
||||
|
||||
@ -301,8 +301,9 @@ class ClassVerifier : public StackObj {
|
||||
|
||||
void verify_invoke_init(
|
||||
RawBytecodeStream* bcs, u2 ref_index, VerificationType ref_class_type,
|
||||
StackMapFrame* current_frame, u4 code_length, bool* this_uninit,
|
||||
constantPoolHandle cp, TRAPS);
|
||||
StackMapFrame* current_frame, u4 code_length, bool in_try_block,
|
||||
bool* this_uninit, constantPoolHandle cp, StackMapTable* stackmap_table,
|
||||
TRAPS);
|
||||
|
||||
// Used by ends_in_athrow() to push all handlers that contain bci onto
|
||||
// the handler_stack, if the handler is not already on the stack.
|
||||
@ -316,8 +317,8 @@ class ClassVerifier : public StackObj {
|
||||
|
||||
void verify_invoke_instructions(
|
||||
RawBytecodeStream* bcs, u4 code_length, StackMapFrame* current_frame,
|
||||
bool* this_uninit, VerificationType return_type,
|
||||
constantPoolHandle cp, TRAPS);
|
||||
bool in_try_block, bool* this_uninit, VerificationType return_type,
|
||||
constantPoolHandle cp, StackMapTable* stackmap_table, TRAPS);
|
||||
|
||||
VerificationType get_newarray_type(u2 index, u2 bci, TRAPS);
|
||||
void verify_anewarray(u2 bci, u2 index, constantPoolHandle cp,
|
||||
|
||||
@ -79,6 +79,7 @@
|
||||
template(java_lang_ref_WeakReference, "java/lang/ref/WeakReference") \
|
||||
template(java_lang_ref_FinalReference, "java/lang/ref/FinalReference") \
|
||||
template(java_lang_ref_PhantomReference, "java/lang/ref/PhantomReference") \
|
||||
template(sun_misc_Cleaner, "sun/misc/Cleaner") \
|
||||
template(java_lang_ref_Finalizer, "java/lang/ref/Finalizer") \
|
||||
template(java_lang_reflect_AccessibleObject, "java/lang/reflect/AccessibleObject") \
|
||||
template(java_lang_reflect_Method, "java/lang/reflect/Method") \
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2015, Oracle and/or its affiliates. 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
|
||||
@ -1005,6 +1005,117 @@ void CodeCache::make_marked_nmethods_not_entrant() {
|
||||
}
|
||||
}
|
||||
|
||||
// Flushes compiled methods dependent on dependee.
|
||||
void CodeCache::flush_dependents_on(instanceKlassHandle dependee) {
|
||||
assert_lock_strong(Compile_lock);
|
||||
|
||||
if (number_of_nmethods_with_dependencies() == 0) return;
|
||||
|
||||
// CodeCache can only be updated by a thread_in_VM and they will all be
|
||||
// stopped during the safepoint so CodeCache will be safe to update without
|
||||
// holding the CodeCache_lock.
|
||||
|
||||
KlassDepChange changes(dependee);
|
||||
|
||||
// Compute the dependent nmethods
|
||||
if (mark_for_deoptimization(changes) > 0) {
|
||||
// At least one nmethod has been marked for deoptimization
|
||||
VM_Deoptimize op;
|
||||
VMThread::execute(&op);
|
||||
}
|
||||
}
|
||||
|
||||
// Flushes compiled methods dependent on a particular CallSite
|
||||
// instance when its target is different than the given MethodHandle.
|
||||
void CodeCache::flush_dependents_on(Handle call_site, Handle method_handle) {
|
||||
assert_lock_strong(Compile_lock);
|
||||
|
||||
if (number_of_nmethods_with_dependencies() == 0) return;
|
||||
|
||||
// CodeCache can only be updated by a thread_in_VM and they will all be
|
||||
// stopped during the safepoint so CodeCache will be safe to update without
|
||||
// holding the CodeCache_lock.
|
||||
|
||||
CallSiteDepChange changes(call_site(), method_handle());
|
||||
|
||||
// Compute the dependent nmethods that have a reference to a
|
||||
// CallSite object. We use InstanceKlass::mark_dependent_nmethod
|
||||
// directly instead of CodeCache::mark_for_deoptimization because we
|
||||
// want dependents on the call site class only not all classes in
|
||||
// the ContextStream.
|
||||
int marked = 0;
|
||||
{
|
||||
MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
|
||||
InstanceKlass* call_site_klass = InstanceKlass::cast(call_site->klass());
|
||||
marked = call_site_klass->mark_dependent_nmethods(changes);
|
||||
}
|
||||
if (marked > 0) {
|
||||
// At least one nmethod has been marked for deoptimization
|
||||
VM_Deoptimize op;
|
||||
VMThread::execute(&op);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HOTSWAP
|
||||
// Flushes compiled methods dependent on dependee in the evolutionary sense
|
||||
void CodeCache::flush_evol_dependents_on(instanceKlassHandle ev_k_h) {
|
||||
// --- Compile_lock is not held. However we are at a safepoint.
|
||||
assert_locked_or_safepoint(Compile_lock);
|
||||
if (number_of_nmethods_with_dependencies() == 0) return;
|
||||
|
||||
// CodeCache can only be updated by a thread_in_VM and they will all be
|
||||
// stopped during the safepoint so CodeCache will be safe to update without
|
||||
// holding the CodeCache_lock.
|
||||
|
||||
// Compute the dependent nmethods
|
||||
if (mark_for_evol_deoptimization(ev_k_h) > 0) {
|
||||
// At least one nmethod has been marked for deoptimization
|
||||
|
||||
// All this already happens inside a VM_Operation, so we'll do all the work here.
|
||||
// Stuff copied from VM_Deoptimize and modified slightly.
|
||||
|
||||
// We do not want any GCs to happen while we are in the middle of this VM operation
|
||||
ResourceMark rm;
|
||||
DeoptimizationMarker dm;
|
||||
|
||||
// Deoptimize all activations depending on marked nmethods
|
||||
Deoptimization::deoptimize_dependents();
|
||||
|
||||
// Make the dependent methods not entrant (in VM_Deoptimize they are made zombies)
|
||||
make_marked_nmethods_not_entrant();
|
||||
}
|
||||
}
|
||||
#endif // HOTSWAP
|
||||
|
||||
|
||||
// Flushes compiled methods dependent on dependee
|
||||
void CodeCache::flush_dependents_on_method(methodHandle m_h) {
|
||||
// --- Compile_lock is not held. However we are at a safepoint.
|
||||
assert_locked_or_safepoint(Compile_lock);
|
||||
|
||||
// CodeCache can only be updated by a thread_in_VM and they will all be
|
||||
// stopped dring the safepoint so CodeCache will be safe to update without
|
||||
// holding the CodeCache_lock.
|
||||
|
||||
// Compute the dependent nmethods
|
||||
if (mark_for_deoptimization(m_h()) > 0) {
|
||||
// At least one nmethod has been marked for deoptimization
|
||||
|
||||
// All this already happens inside a VM_Operation, so we'll do all the work here.
|
||||
// Stuff copied from VM_Deoptimize and modified slightly.
|
||||
|
||||
// We do not want any GCs to happen while we are in the middle of this VM operation
|
||||
ResourceMark rm;
|
||||
DeoptimizationMarker dm;
|
||||
|
||||
// Deoptimize all activations depending on marked nmethods
|
||||
Deoptimization::deoptimize_dependents();
|
||||
|
||||
// Make the dependent methods not entrant (in VM_Deoptimize they are made zombies)
|
||||
make_marked_nmethods_not_entrant();
|
||||
}
|
||||
}
|
||||
|
||||
void CodeCache::verify() {
|
||||
assert_locked_or_safepoint(CodeCache_lock);
|
||||
FOR_ALL_HEAPS(heap) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2015, Oracle and/or its affiliates. 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
|
||||
@ -209,16 +209,28 @@ class CodeCache : AllStatic {
|
||||
static void verify_icholder_relocations();
|
||||
|
||||
// Deoptimization
|
||||
private:
|
||||
static int mark_for_deoptimization(DepChange& changes);
|
||||
#ifdef HOTSWAP
|
||||
static int mark_for_evol_deoptimization(instanceKlassHandle dependee);
|
||||
#endif // HOTSWAP
|
||||
|
||||
public:
|
||||
static void mark_all_nmethods_for_deoptimization();
|
||||
static int mark_for_deoptimization(Method* dependee);
|
||||
static void make_marked_nmethods_zombies();
|
||||
static void make_marked_nmethods_not_entrant();
|
||||
|
||||
// Flushing and deoptimization
|
||||
static void flush_dependents_on(instanceKlassHandle dependee);
|
||||
static void flush_dependents_on(Handle call_site, Handle method_handle);
|
||||
#ifdef HOTSWAP
|
||||
// Flushing and deoptimization in case of evolution
|
||||
static void flush_evol_dependents_on(instanceKlassHandle dependee);
|
||||
#endif // HOTSWAP
|
||||
// Support for fullspeed debugging
|
||||
static void flush_dependents_on_method(methodHandle dependee);
|
||||
|
||||
// tells how many nmethods have dependencies
|
||||
static int number_of_nmethods_with_dependencies();
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2014, Oracle and/or its affiliates. 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
|
||||
@ -560,7 +560,7 @@ void Dependencies::print_dependency(DepType dept, GrowableArray<DepArgument>* ar
|
||||
put_star = !Dependencies::is_concrete_klass((Klass*)arg.metadata_value());
|
||||
} else if (arg.is_method()) {
|
||||
what = "method ";
|
||||
put_star = !Dependencies::is_concrete_method((Method*)arg.metadata_value());
|
||||
put_star = !Dependencies::is_concrete_method((Method*)arg.metadata_value(), NULL);
|
||||
} else if (arg.is_klass()) {
|
||||
what = "class ";
|
||||
} else {
|
||||
@ -878,8 +878,8 @@ class ClassHierarchyWalker {
|
||||
// Static methods don't override non-static so punt
|
||||
return true;
|
||||
}
|
||||
if ( !Dependencies::is_concrete_method(lm)
|
||||
&& !Dependencies::is_concrete_method(m)
|
||||
if ( !Dependencies::is_concrete_method(lm, k)
|
||||
&& !Dependencies::is_concrete_method(m, ctxk)
|
||||
&& lm->method_holder()->is_subtype_of(m->method_holder()))
|
||||
// Method m is overridden by lm, but both are non-concrete.
|
||||
return true;
|
||||
@ -915,8 +915,17 @@ class ClassHierarchyWalker {
|
||||
} else if (!k->oop_is_instance()) {
|
||||
return false; // no methods to find in an array type
|
||||
} else {
|
||||
Method* m = InstanceKlass::cast(k)->find_method(_name, _signature);
|
||||
if (m == NULL || !Dependencies::is_concrete_method(m)) return false;
|
||||
// Search class hierarchy first.
|
||||
Method* m = InstanceKlass::cast(k)->find_instance_method(_name, _signature);
|
||||
if (!Dependencies::is_concrete_method(m, k)) {
|
||||
// Check interface defaults also, if any exist.
|
||||
Array<Method*>* default_methods = InstanceKlass::cast(k)->default_methods();
|
||||
if (default_methods == NULL)
|
||||
return false;
|
||||
m = InstanceKlass::cast(k)->find_method(default_methods, _name, _signature);
|
||||
if (!Dependencies::is_concrete_method(m, NULL))
|
||||
return false;
|
||||
}
|
||||
_found_methods[_num_participants] = m;
|
||||
// Note: If add_participant(k) is called,
|
||||
// the method m will already be memoized for it.
|
||||
@ -1209,15 +1218,17 @@ bool Dependencies::is_concrete_klass(Klass* k) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Dependencies::is_concrete_method(Method* m) {
|
||||
// Statics are irrelevant to virtual call sites.
|
||||
if (m->is_static()) return false;
|
||||
|
||||
// We could also return false if m does not yet appear to be
|
||||
// executed, if the VM version supports this distinction also.
|
||||
// Default methods are considered "concrete" as well.
|
||||
return !m->is_abstract() &&
|
||||
!m->is_overpass(); // error functions aren't concrete
|
||||
bool Dependencies::is_concrete_method(Method* m, Klass * k) {
|
||||
// NULL is not a concrete method,
|
||||
// statics are irrelevant to virtual call sites,
|
||||
// abstract methods are not concrete,
|
||||
// overpass (error) methods are not concrete if k is abstract
|
||||
//
|
||||
// note "true" is conservative answer --
|
||||
// overpass clause is false if k == NULL, implies return true if
|
||||
// answer depends on overpass clause.
|
||||
return ! ( m == NULL || m -> is_static() || m -> is_abstract() ||
|
||||
m->is_overpass() && k != NULL && k -> is_abstract() );
|
||||
}
|
||||
|
||||
|
||||
@ -1242,16 +1253,6 @@ bool Dependencies::is_concrete_klass(ciInstanceKlass* k) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Dependencies::is_concrete_method(ciMethod* m) {
|
||||
// Statics are irrelevant to virtual call sites.
|
||||
if (m->is_static()) return false;
|
||||
|
||||
// We could also return false if m does not yet appear to be
|
||||
// executed, if the VM version supports this distinction also.
|
||||
return !m->is_abstract();
|
||||
}
|
||||
|
||||
|
||||
bool Dependencies::has_finalizable_subclass(ciInstanceKlass* k) {
|
||||
return k->has_finalizable_subclass();
|
||||
}
|
||||
@ -1469,7 +1470,7 @@ Method* Dependencies::find_unique_concrete_method(Klass* ctxk, Method* m) {
|
||||
Klass* wit = wf.find_witness_definer(ctxk);
|
||||
if (wit != NULL) return NULL; // Too many witnesses.
|
||||
Method* fm = wf.found_method(0); // Will be NULL if num_parts == 0.
|
||||
if (Dependencies::is_concrete_method(m)) {
|
||||
if (Dependencies::is_concrete_method(m, ctxk)) {
|
||||
if (fm == NULL) {
|
||||
// It turns out that m was always the only implementation.
|
||||
fm = m;
|
||||
@ -1499,61 +1500,6 @@ Klass* Dependencies::check_exclusive_concrete_methods(Klass* ctxk,
|
||||
return wf.find_witness_definer(ctxk, changes);
|
||||
}
|
||||
|
||||
// Find the set of all non-abstract methods under ctxk that match m[0].
|
||||
// (The method m[0] must be defined or inherited in ctxk.)
|
||||
// Include m itself in the set, unless it is abstract.
|
||||
// Fill the given array m[0..(mlen-1)] with this set, and return the length.
|
||||
// (The length may be zero if no concrete methods are found anywhere.)
|
||||
// If there are too many concrete methods to fit in marray, return -1.
|
||||
int Dependencies::find_exclusive_concrete_methods(Klass* ctxk,
|
||||
int mlen,
|
||||
Method* marray[]) {
|
||||
Method* m0 = marray[0];
|
||||
ClassHierarchyWalker wf(m0);
|
||||
assert(wf.check_method_context(ctxk, m0), "proper context");
|
||||
wf.record_witnesses(mlen);
|
||||
bool participants_hide_witnesses = true;
|
||||
Klass* wit = wf.find_witness_definer(ctxk);
|
||||
if (wit != NULL) return -1; // Too many witnesses.
|
||||
int num = wf.num_participants();
|
||||
assert(num <= mlen, "oob");
|
||||
// Keep track of whether m is also part of the result set.
|
||||
int mfill = 0;
|
||||
assert(marray[mfill] == m0, "sanity");
|
||||
if (Dependencies::is_concrete_method(m0))
|
||||
mfill++; // keep m0 as marray[0], the first result
|
||||
for (int i = 0; i < num; i++) {
|
||||
Method* fm = wf.found_method(i);
|
||||
if (fm == m0) continue; // Already put this guy in the list.
|
||||
if (mfill == mlen) {
|
||||
return -1; // Oops. Too many methods after all!
|
||||
}
|
||||
marray[mfill++] = fm;
|
||||
}
|
||||
#ifndef PRODUCT
|
||||
// Make sure the dependency mechanism will pass this discovery:
|
||||
if (VerifyDependencies) {
|
||||
// Turn off dependency tracing while actually testing deps.
|
||||
FlagSetting fs(TraceDependencies, false);
|
||||
switch (mfill) {
|
||||
case 1:
|
||||
guarantee(NULL == (void *)check_unique_concrete_method(ctxk, marray[0]),
|
||||
"verify dep.");
|
||||
break;
|
||||
case 2:
|
||||
guarantee(NULL == (void *)
|
||||
check_exclusive_concrete_methods(ctxk, marray[0], marray[1]),
|
||||
"verify dep.");
|
||||
break;
|
||||
default:
|
||||
ShouldNotReachHere(); // mlen > 2 yet supported
|
||||
}
|
||||
}
|
||||
#endif //PRODUCT
|
||||
return mfill;
|
||||
}
|
||||
|
||||
|
||||
Klass* Dependencies::check_has_no_finalizable_subclasses(Klass* ctxk, KlassDepChange* changes) {
|
||||
Klass* search_at = ctxk;
|
||||
if (changes != NULL)
|
||||
@ -1561,7 +1507,6 @@ Klass* Dependencies::check_has_no_finalizable_subclasses(Klass* ctxk, KlassDepCh
|
||||
return find_finalizable_subclass(search_at);
|
||||
}
|
||||
|
||||
|
||||
Klass* Dependencies::check_call_site_target_value(oop call_site, oop method_handle, CallSiteDepChange* changes) {
|
||||
assert(call_site ->is_a(SystemDictionary::CallSite_klass()), "sanity");
|
||||
assert(method_handle->is_a(SystemDictionary::MethodHandle_klass()), "sanity");
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2014, Oracle and/or its affiliates. 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
|
||||
@ -288,7 +288,7 @@ class Dependencies: public ResourceObj {
|
||||
// In that case, there would be a middle ground between concrete
|
||||
// and abstract (as defined by the Java language and VM).
|
||||
static bool is_concrete_klass(Klass* k); // k is instantiable
|
||||
static bool is_concrete_method(Method* m); // m is invocable
|
||||
static bool is_concrete_method(Method* m, Klass* k); // m is invocable
|
||||
static Klass* find_finalizable_subclass(Klass* k);
|
||||
|
||||
// These versions of the concreteness queries work through the CI.
|
||||
@ -302,7 +302,6 @@ class Dependencies: public ResourceObj {
|
||||
// not go back into the VM to get their value; they must cache the
|
||||
// bit in the CI, either eagerly or lazily.)
|
||||
static bool is_concrete_klass(ciInstanceKlass* k); // k appears instantiable
|
||||
static bool is_concrete_method(ciMethod* m); // m appears invocable
|
||||
static bool has_finalizable_subclass(ciInstanceKlass* k);
|
||||
|
||||
// As a general rule, it is OK to compile under the assumption that
|
||||
@ -349,7 +348,6 @@ class Dependencies: public ResourceObj {
|
||||
static Klass* find_unique_concrete_subtype(Klass* ctxk);
|
||||
static Method* find_unique_concrete_method(Klass* ctxk, Method* m);
|
||||
static int find_exclusive_concrete_subtypes(Klass* ctxk, int klen, Klass* k[]);
|
||||
static int find_exclusive_concrete_methods(Klass* ctxk, int mlen, Method* m[]);
|
||||
|
||||
// Create the encoding which will be stored in an nmethod.
|
||||
void encode_content_bytes();
|
||||
|
||||
@ -252,7 +252,7 @@ CompileTask* CompileTask::allocate() {
|
||||
} else {
|
||||
task = new CompileTask();
|
||||
DEBUG_ONLY(_num_allocated_tasks++;)
|
||||
assert (_num_allocated_tasks < 10000, "Leaking compilation tasks?");
|
||||
assert (WhiteBoxAPI || _num_allocated_tasks < 10000, "Leaking compilation tasks?");
|
||||
task->set_next(NULL);
|
||||
task->set_is_free(true);
|
||||
}
|
||||
|
||||
@ -105,7 +105,6 @@ class MethodMatcher : public CHeapObj<mtCompiler> {
|
||||
tty->print(".");
|
||||
print_symbol(method_name(), _method_mode);
|
||||
if (signature() != NULL) {
|
||||
tty->print(" ");
|
||||
signature()->print_symbol_on(tty);
|
||||
}
|
||||
}
|
||||
@ -467,43 +466,85 @@ static OracleCommand parse_command_name(const char * line, int* bytes_read) {
|
||||
return UnknownCommand;
|
||||
}
|
||||
|
||||
|
||||
static void usage() {
|
||||
tty->print_cr(" CompileCommand and the CompilerOracle allows simple control over");
|
||||
tty->print_cr(" what's allowed to be compiled. The standard supported directives");
|
||||
tty->print_cr(" are exclude and compileonly. The exclude directive stops a method");
|
||||
tty->print_cr(" from being compiled and compileonly excludes all methods except for");
|
||||
tty->print_cr(" the ones mentioned by compileonly directives. The basic form of");
|
||||
tty->print_cr(" all commands is a command name followed by the name of the method");
|
||||
tty->print_cr(" in one of two forms: the standard class file format as in");
|
||||
tty->print_cr(" class/name.methodName or the PrintCompilation format");
|
||||
tty->print_cr(" class.name::methodName. The method name can optionally be followed");
|
||||
tty->print_cr(" by a space then the signature of the method in the class file");
|
||||
tty->print_cr(" format. Otherwise the directive applies to all methods with the");
|
||||
tty->print_cr(" same name and class regardless of signature. Leading and trailing");
|
||||
tty->print_cr(" *'s in the class and/or method name allows a small amount of");
|
||||
tty->print_cr(" wildcarding. ");
|
||||
tty->cr();
|
||||
tty->print_cr(" Examples:");
|
||||
tty->print_cr("The CompileCommand option enables the user of the JVM to control specific");
|
||||
tty->print_cr("behavior of the dynamic compilers. Many commands require a pattern that defines");
|
||||
tty->print_cr("the set of methods the command shall be applied to. The CompileCommand");
|
||||
tty->print_cr("option provides the following commands:");
|
||||
tty->cr();
|
||||
tty->print_cr(" exclude java/lang/StringBuffer.append");
|
||||
tty->print_cr(" compileonly java/lang/StringBuffer.toString ()Ljava/lang/String;");
|
||||
tty->print_cr(" exclude java/lang/String*.*");
|
||||
tty->print_cr(" exclude *.toString");
|
||||
}
|
||||
tty->print_cr(" break,<pattern> - debug breakpoint in compiler and in generated code");
|
||||
tty->print_cr(" print,<pattern> - print assembly");
|
||||
tty->print_cr(" exclude,<pattern> - don't compile or inline");
|
||||
tty->print_cr(" inline,<pattern> - always inline");
|
||||
tty->print_cr(" dontinline,<pattern> - don't inline");
|
||||
tty->print_cr(" compileonly,<pattern> - compile only");
|
||||
tty->print_cr(" log,<pattern> - log compilation");
|
||||
tty->print_cr(" option,<pattern>,<option type>,<option name>,<value>");
|
||||
tty->print_cr(" - set value of custom option");
|
||||
tty->print_cr(" option,<pattern>,<bool option name>");
|
||||
tty->print_cr(" - shorthand for setting boolean flag");
|
||||
tty->print_cr(" quiet - silence the compile command output");
|
||||
tty->print_cr(" help - print this text");
|
||||
tty->cr();
|
||||
tty->print_cr("The preferred format for the method matching pattern is:");
|
||||
tty->print_cr(" package/Class.method()");
|
||||
tty->cr();
|
||||
tty->print_cr("For backward compatibility this form is also allowed:");
|
||||
tty->print_cr(" package.Class::method()");
|
||||
tty->cr();
|
||||
tty->print_cr("The signature can be separated by an optional whitespace or comma:");
|
||||
tty->print_cr(" package/Class.method ()");
|
||||
tty->cr();
|
||||
tty->print_cr("The class and method identifier can be used together with leading or");
|
||||
tty->print_cr("trailing *'s for a small amount of wildcarding:");
|
||||
tty->print_cr(" *ackage/Clas*.*etho*()");
|
||||
tty->cr();
|
||||
tty->print_cr("It is possible to use more than one CompileCommand on the command line:");
|
||||
tty->print_cr(" -XX:CompileCommand=exclude,java/*.* -XX:CompileCommand=log,java*.*");
|
||||
tty->cr();
|
||||
tty->print_cr("The CompileCommands can be loaded from a file with the flag");
|
||||
tty->print_cr("-XX:CompileCommandFile=<file> or be added to the file '.hotspot_compiler'");
|
||||
tty->print_cr("Use the same format in the file as the argument to the CompileCommand flag.");
|
||||
tty->print_cr("Add one command on each line.");
|
||||
tty->print_cr(" exclude java/*.*");
|
||||
tty->print_cr(" option java/*.* ReplayInline");
|
||||
tty->cr();
|
||||
tty->print_cr("The following commands have conflicting behavior: 'exclude', 'inline', 'dontinline',");
|
||||
tty->print_cr("and 'compileonly'. There is no priority of commands. Applying (a subset of) these");
|
||||
tty->print_cr("commands to the same method results in undefined behavior.");
|
||||
tty->cr();
|
||||
};
|
||||
|
||||
// The JVM specification defines the allowed characters.
|
||||
// Tokens that are disallowed by the JVM specification can have
|
||||
// a meaning to the parser so we need to include them here.
|
||||
// The parser does not enforce all rules of the JVMS - a successful parse
|
||||
// does not mean that it is an allowed name. Illegal names will
|
||||
// be ignored since they never can match a class or method.
|
||||
//
|
||||
// '\0' and 0xf0-0xff are disallowed in constant string values
|
||||
// 0x20 ' ', 0x09 '\t' and, 0x2c ',' are used in the matching
|
||||
// 0x5b '[' and 0x5d ']' can not be used because of the matcher
|
||||
// 0x28 '(' and 0x29 ')' are used for the signature
|
||||
// 0x2e '.' is always replaced before the matching
|
||||
// 0x2f '/' is only used in the class name as package separator
|
||||
|
||||
// The characters allowed in a class or method name. All characters > 0x7f
|
||||
// are allowed in order to handle obfuscated class files (e.g. Volano)
|
||||
#define RANGEBASE "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$_<>" \
|
||||
"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" \
|
||||
"\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" \
|
||||
"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf" \
|
||||
"\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" \
|
||||
"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" \
|
||||
"\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" \
|
||||
"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef" \
|
||||
"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
|
||||
#define RANGEBASE "\x1\x2\x3\x4\x5\x6\x7\x8\xa\xb\xc\xd\xe\xf" \
|
||||
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \
|
||||
"\x21\x22\x23\x24\x25\x26\x27\x2a\x2b\x2c\x2d" \
|
||||
"\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" \
|
||||
"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" \
|
||||
"\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5c\x5e\x5f" \
|
||||
"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" \
|
||||
"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" \
|
||||
"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" \
|
||||
"\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" \
|
||||
"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf" \
|
||||
"\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" \
|
||||
"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" \
|
||||
"\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" \
|
||||
"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef"
|
||||
|
||||
#define RANGE0 "[*" RANGEBASE "]"
|
||||
#define RANGESLASH "[*" RANGEBASE "/]"
|
||||
@ -681,8 +722,9 @@ void CompilerOracle::parse_from_line(char* line) {
|
||||
|
||||
if (command == UnknownCommand) {
|
||||
ttyLocker ttyl;
|
||||
tty->print_cr("CompilerOracle: unrecognized line");
|
||||
tty->print_cr("CompileCommand: unrecognized command");
|
||||
tty->print_cr(" \"%s\"", original_line);
|
||||
CompilerOracle::print_tip();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -712,9 +754,17 @@ void CompilerOracle::parse_from_line(char* line) {
|
||||
Symbol* signature = NULL;
|
||||
|
||||
line += bytes_read;
|
||||
|
||||
// Skip any leading spaces before signature
|
||||
int whitespace_read = 0;
|
||||
sscanf(line, "%*[ \t]%n", &whitespace_read);
|
||||
if (whitespace_read > 0) {
|
||||
line += whitespace_read;
|
||||
}
|
||||
|
||||
// there might be a signature following the method.
|
||||
// signatures always begin with ( so match that by hand
|
||||
if (1 == sscanf(line, "%*[ \t](%254[[);/" RANGEBASE "]%n", sig + 1, &bytes_read)) {
|
||||
if (1 == sscanf(line, "(%254[[);/" RANGEBASE "]%n", sig + 1, &bytes_read)) {
|
||||
sig[0] = '(';
|
||||
line += bytes_read;
|
||||
signature = SymbolTable::new_symbol(sig, CHECK);
|
||||
@ -740,7 +790,7 @@ void CompilerOracle::parse_from_line(char* line) {
|
||||
if (match != NULL && !_quiet) {
|
||||
// Print out the last match added
|
||||
ttyLocker ttyl;
|
||||
tty->print("CompilerOracle: %s ", command_names[command]);
|
||||
tty->print("CompileCommand: %s ", command_names[command]);
|
||||
match->print();
|
||||
}
|
||||
line += bytes_read;
|
||||
@ -775,26 +825,36 @@ void CompilerOracle::parse_from_line(char* line) {
|
||||
ttyLocker ttyl;
|
||||
if (error_msg != NULL) {
|
||||
// an error has happened
|
||||
tty->print_cr("CompilerOracle: unrecognized line");
|
||||
tty->print_cr("CompileCommand: An error occured during parsing");
|
||||
tty->print_cr(" \"%s\"", original_line);
|
||||
if (error_msg != NULL) {
|
||||
tty->print_cr("%s", error_msg);
|
||||
}
|
||||
CompilerOracle::print_tip();
|
||||
|
||||
} else {
|
||||
// check for remaining characters
|
||||
bytes_read = 0;
|
||||
sscanf(line, "%*[ \t]%n", &bytes_read);
|
||||
if (line[bytes_read] != '\0') {
|
||||
tty->print_cr("CompilerOracle: unrecognized line");
|
||||
tty->print_cr("CompileCommand: Bad pattern");
|
||||
tty->print_cr(" \"%s\"", original_line);
|
||||
tty->print_cr(" Unrecognized text %s after command ", line);
|
||||
CompilerOracle::print_tip();
|
||||
} else if (match != NULL && !_quiet) {
|
||||
tty->print("CompilerOracle: %s ", command_names[command]);
|
||||
tty->print("CompileCommand: %s ", command_names[command]);
|
||||
match->print();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CompilerOracle::print_tip() {
|
||||
tty->cr();
|
||||
tty->print_cr("Usage: '-XX:CompileCommand=command,\"package/Class.method()\"'");
|
||||
tty->print_cr("Use: '-XX:CompileCommand=help' for more information.");
|
||||
tty->cr();
|
||||
}
|
||||
|
||||
static const char* default_cc_file = ".hotspot_compiler";
|
||||
|
||||
static const char* cc_file() {
|
||||
|
||||
@ -34,6 +34,7 @@
|
||||
class CompilerOracle : AllStatic {
|
||||
private:
|
||||
static bool _quiet;
|
||||
static void print_tip();
|
||||
|
||||
public:
|
||||
|
||||
|
||||
@ -645,7 +645,7 @@ CMSCollector::CMSCollector(ConcurrentMarkSweepGeneration* cmsGen,
|
||||
// Support for parallelizing survivor space rescan
|
||||
if ((CMSParallelRemarkEnabled && CMSParallelSurvivorRemarkEnabled) || CMSParallelInitialMarkEnabled) {
|
||||
const size_t max_plab_samples =
|
||||
((DefNewGeneration*)_young_gen)->max_survivor_size()/MinTLABSize;
|
||||
((DefNewGeneration*)_young_gen)->max_survivor_size() / plab_sample_minimum_size();
|
||||
|
||||
_survivor_plab_array = NEW_C_HEAP_ARRAY(ChunkArray, ParallelGCThreads, mtGC);
|
||||
_survivor_chunk_array = NEW_C_HEAP_ARRAY(HeapWord*, 2*max_plab_samples, mtGC);
|
||||
@ -703,6 +703,12 @@ CMSCollector::CMSCollector(ConcurrentMarkSweepGeneration* cmsGen,
|
||||
_inter_sweep_timer.start(); // start of time
|
||||
}
|
||||
|
||||
size_t CMSCollector::plab_sample_minimum_size() {
|
||||
// The default value of MinTLABSize is 2k, but there is
|
||||
// no way to get the default value if the flag has been overridden.
|
||||
return MAX2(ThreadLocalAllocBuffer::min_size() * HeapWordSize, 2 * K);
|
||||
}
|
||||
|
||||
const char* ConcurrentMarkSweepGeneration::name() const {
|
||||
return "concurrent mark-sweep generation";
|
||||
}
|
||||
|
||||
@ -737,6 +737,10 @@ class CMSCollector: public CHeapObj<mtGC> {
|
||||
size_t* _cursor;
|
||||
ChunkArray* _survivor_plab_array;
|
||||
|
||||
// A bounded minimum size of PLABs, should not return too small values since
|
||||
// this will affect the size of the data structures used for parallel young gen rescan
|
||||
size_t plab_sample_minimum_size();
|
||||
|
||||
// Support for marking stack overflow handling
|
||||
bool take_from_overflow_list(size_t num, CMSMarkStack* to_stack);
|
||||
bool par_take_from_overflow_list(size_t num,
|
||||
|
||||
@ -1910,7 +1910,6 @@ public:
|
||||
}
|
||||
|
||||
void work(uint worker_id) {
|
||||
double start = os::elapsedTime();
|
||||
FreeRegionList local_cleanup_list("Local Cleanup List");
|
||||
HRRSCleanupTask hrrs_cleanup_task;
|
||||
G1NoteEndOfConcMarkClosure g1_note_end(_g1h, &local_cleanup_list,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2014, Oracle and/or its affiliates. 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
|
||||
@ -47,6 +47,13 @@ public:
|
||||
// active field set to true.
|
||||
PtrQueue(qset_, perm, true /* active */) { }
|
||||
|
||||
// Flush before destroying; queue may be used to capture pending work while
|
||||
// doing something else, with auto-flush on completion.
|
||||
~DirtyCardQueue() { if (!is_permanent()) flush(); }
|
||||
|
||||
// Process queue entries and release resources.
|
||||
void flush() { flush_impl(); }
|
||||
|
||||
// Apply the closure to all elements, and reset the index to make the
|
||||
// buffer empty. If a closure application returns "false", return
|
||||
// "false" immediately, halting the iteration. If "consume" is true,
|
||||
|
||||
@ -99,8 +99,8 @@ void G1DefaultAllocator::release_gc_alloc_regions(uint no_of_gc_workers, Evacuat
|
||||
}
|
||||
|
||||
if (ResizePLAB) {
|
||||
_g1h->_survivor_plab_stats.adjust_desired_plab_sz(no_of_gc_workers);
|
||||
_g1h->_old_plab_stats.adjust_desired_plab_sz(no_of_gc_workers);
|
||||
_g1h->alloc_buffer_stats(InCSetState::Young)->adjust_desired_plab_sz(no_of_gc_workers);
|
||||
_g1h->alloc_buffer_stats(InCSetState::Old)->adjust_desired_plab_sz(no_of_gc_workers);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1229,7 +1229,6 @@ bool G1CollectedHeap::do_collection(bool explicit_gc,
|
||||
TraceCollectorStats tcs(g1mm()->full_collection_counters());
|
||||
TraceMemoryManagerStats tms(true /* fullGC */, gc_cause());
|
||||
|
||||
double start = os::elapsedTime();
|
||||
g1_policy()->record_full_collection_start();
|
||||
|
||||
// Note: When we have a more flexible GC logging framework that
|
||||
@ -1436,7 +1435,6 @@ bool G1CollectedHeap::do_collection(bool explicit_gc,
|
||||
|
||||
_allocator->init_mutator_alloc_region();
|
||||
|
||||
double end = os::elapsedTime();
|
||||
g1_policy()->record_full_collection_end();
|
||||
|
||||
if (G1Log::fine()) {
|
||||
@ -2068,7 +2066,7 @@ void G1CollectedHeap::stop() {
|
||||
}
|
||||
|
||||
void G1CollectedHeap::clear_humongous_is_live_table() {
|
||||
guarantee(G1ReclaimDeadHumongousObjectsAtYoungGC, "Should only be called if true");
|
||||
guarantee(G1EagerReclaimHumongousObjects, "Should only be called if true");
|
||||
_humongous_is_live.clear();
|
||||
}
|
||||
|
||||
@ -3485,8 +3483,24 @@ class RegisterHumongousWithInCSetFastTestClosure : public HeapRegionClosure {
|
||||
private:
|
||||
size_t _total_humongous;
|
||||
size_t _candidate_humongous;
|
||||
|
||||
DirtyCardQueue _dcq;
|
||||
|
||||
bool humongous_region_is_candidate(uint index) {
|
||||
HeapRegion* region = G1CollectedHeap::heap()->region_at(index);
|
||||
assert(region->is_starts_humongous(), "Must start a humongous object");
|
||||
HeapRegionRemSet* const rset = region->rem_set();
|
||||
bool const allow_stale_refs = G1EagerReclaimHumongousObjectsWithStaleRefs;
|
||||
return !oop(region->bottom())->is_objArray() &&
|
||||
((allow_stale_refs && rset->occupancy_less_or_equal_than(G1RSetSparseRegionEntries)) ||
|
||||
(!allow_stale_refs && rset->is_empty()));
|
||||
}
|
||||
|
||||
public:
|
||||
RegisterHumongousWithInCSetFastTestClosure() : _total_humongous(0), _candidate_humongous(0) {
|
||||
RegisterHumongousWithInCSetFastTestClosure()
|
||||
: _total_humongous(0),
|
||||
_candidate_humongous(0),
|
||||
_dcq(&JavaThread::dirty_card_queue_set()) {
|
||||
}
|
||||
|
||||
virtual bool doHeapRegion(HeapRegion* r) {
|
||||
@ -3496,11 +3510,29 @@ class RegisterHumongousWithInCSetFastTestClosure : public HeapRegionClosure {
|
||||
G1CollectedHeap* g1h = G1CollectedHeap::heap();
|
||||
|
||||
uint region_idx = r->hrm_index();
|
||||
bool is_candidate = !g1h->humongous_region_is_always_live(region_idx);
|
||||
// Is_candidate already filters out humongous regions with some remembered set.
|
||||
// This will not lead to humongous object that we mistakenly keep alive because
|
||||
// during young collection the remembered sets will only be added to.
|
||||
bool is_candidate = humongous_region_is_candidate(region_idx);
|
||||
// Is_candidate already filters out humongous object with large remembered sets.
|
||||
// If we have a humongous object with a few remembered sets, we simply flush these
|
||||
// remembered set entries into the DCQS. That will result in automatic
|
||||
// re-evaluation of their remembered set entries during the following evacuation
|
||||
// phase.
|
||||
if (is_candidate) {
|
||||
if (!r->rem_set()->is_empty()) {
|
||||
guarantee(r->rem_set()->occupancy_less_or_equal_than(G1RSetSparseRegionEntries),
|
||||
"Found a not-small remembered set here. This is inconsistent with previous assumptions.");
|
||||
G1SATBCardTableLoggingModRefBS* bs = g1h->g1_barrier_set();
|
||||
HeapRegionRemSetIterator hrrs(r->rem_set());
|
||||
size_t card_index;
|
||||
while (hrrs.has_next(card_index)) {
|
||||
jbyte* card_ptr = (jbyte*)bs->byte_for_index(card_index);
|
||||
if (*card_ptr != CardTableModRefBS::dirty_card_val()) {
|
||||
*card_ptr = CardTableModRefBS::dirty_card_val();
|
||||
_dcq.enqueue(card_ptr);
|
||||
}
|
||||
}
|
||||
r->rem_set()->clear_locked();
|
||||
}
|
||||
assert(r->rem_set()->is_empty(), "At this point any humongous candidate remembered set must be empty.");
|
||||
g1h->register_humongous_region_with_in_cset_fast_test(region_idx);
|
||||
_candidate_humongous++;
|
||||
}
|
||||
@ -3511,23 +3543,32 @@ class RegisterHumongousWithInCSetFastTestClosure : public HeapRegionClosure {
|
||||
|
||||
size_t total_humongous() const { return _total_humongous; }
|
||||
size_t candidate_humongous() const { return _candidate_humongous; }
|
||||
|
||||
void flush_rem_set_entries() { _dcq.flush(); }
|
||||
};
|
||||
|
||||
void G1CollectedHeap::register_humongous_regions_with_in_cset_fast_test() {
|
||||
if (!G1ReclaimDeadHumongousObjectsAtYoungGC) {
|
||||
g1_policy()->phase_times()->record_fast_reclaim_humongous_stats(0, 0);
|
||||
if (!G1EagerReclaimHumongousObjects) {
|
||||
g1_policy()->phase_times()->record_fast_reclaim_humongous_stats(0.0, 0, 0);
|
||||
return;
|
||||
}
|
||||
double time = os::elapsed_counter();
|
||||
|
||||
RegisterHumongousWithInCSetFastTestClosure cl;
|
||||
heap_region_iterate(&cl);
|
||||
g1_policy()->phase_times()->record_fast_reclaim_humongous_stats(cl.total_humongous(),
|
||||
|
||||
time = ((double)(os::elapsed_counter() - time) / os::elapsed_frequency()) * 1000.0;
|
||||
g1_policy()->phase_times()->record_fast_reclaim_humongous_stats(time,
|
||||
cl.total_humongous(),
|
||||
cl.candidate_humongous());
|
||||
_has_humongous_reclaim_candidates = cl.candidate_humongous() > 0;
|
||||
|
||||
if (_has_humongous_reclaim_candidates || G1TraceReclaimDeadHumongousObjectsAtYoungGC) {
|
||||
if (_has_humongous_reclaim_candidates || G1TraceEagerReclaimHumongousObjects) {
|
||||
clear_humongous_is_live_table();
|
||||
}
|
||||
|
||||
// Finally flush all remembered set entries to re-check into the global DCQS.
|
||||
cl.flush_rem_set_entries();
|
||||
}
|
||||
|
||||
void
|
||||
@ -6140,22 +6181,20 @@ class G1FreeHumongousRegionClosure : public HeapRegionClosure {
|
||||
// are completely up-to-date wrt to references to the humongous object.
|
||||
//
|
||||
// Other implementation considerations:
|
||||
// - never consider object arrays: while they are a valid target, they have not
|
||||
// been observed to be used as temporary objects.
|
||||
// - they would also pose considerable effort for cleaning up the the remembered
|
||||
// sets.
|
||||
// While this cleanup is not strictly necessary to be done (or done instantly),
|
||||
// given that their occurrence is very low, this saves us this additional
|
||||
// complexity.
|
||||
// - never consider object arrays at this time because they would pose
|
||||
// considerable effort for cleaning up the the remembered sets. This is
|
||||
// required because stale remembered sets might reference locations that
|
||||
// are currently allocated into.
|
||||
uint region_idx = r->hrm_index();
|
||||
if (g1h->humongous_is_live(region_idx) ||
|
||||
g1h->humongous_region_is_always_live(region_idx)) {
|
||||
|
||||
if (G1TraceReclaimDeadHumongousObjectsAtYoungGC) {
|
||||
gclog_or_tty->print_cr("Live humongous %d region %d size "SIZE_FORMAT" with remset "SIZE_FORMAT" code roots "SIZE_FORMAT" is marked %d live-other %d obj array %d",
|
||||
r->is_humongous(),
|
||||
if (G1TraceEagerReclaimHumongousObjects) {
|
||||
gclog_or_tty->print_cr("Live humongous region %u size "SIZE_FORMAT" start "PTR_FORMAT" length "UINT32_FORMAT" with remset "SIZE_FORMAT" code roots "SIZE_FORMAT" is marked %d live-other %d obj array %d",
|
||||
region_idx,
|
||||
obj->size()*HeapWordSize,
|
||||
r->bottom(),
|
||||
r->region_num(),
|
||||
r->rem_set()->occupied(),
|
||||
r->rem_set()->strong_code_roots_list_length(),
|
||||
next_bitmap->isMarked(r->bottom()),
|
||||
@ -6171,12 +6210,11 @@ class G1FreeHumongousRegionClosure : public HeapRegionClosure {
|
||||
err_msg("Eagerly reclaiming object arrays is not supported, but the object "PTR_FORMAT" is.",
|
||||
r->bottom()));
|
||||
|
||||
if (G1TraceReclaimDeadHumongousObjectsAtYoungGC) {
|
||||
gclog_or_tty->print_cr("Reclaim humongous region %d size "SIZE_FORMAT" start "PTR_FORMAT" region %d length "UINT32_FORMAT" with remset "SIZE_FORMAT" code roots "SIZE_FORMAT" is marked %d live-other %d obj array %d",
|
||||
r->is_humongous(),
|
||||
if (G1TraceEagerReclaimHumongousObjects) {
|
||||
gclog_or_tty->print_cr("Dead humongous region %u size "SIZE_FORMAT" start "PTR_FORMAT" length "UINT32_FORMAT" with remset "SIZE_FORMAT" code roots "SIZE_FORMAT" is marked %d live-other %d obj array %d",
|
||||
region_idx,
|
||||
obj->size()*HeapWordSize,
|
||||
r->bottom(),
|
||||
region_idx,
|
||||
r->region_num(),
|
||||
r->rem_set()->occupied(),
|
||||
r->rem_set()->strong_code_roots_list_length(),
|
||||
@ -6213,8 +6251,8 @@ class G1FreeHumongousRegionClosure : public HeapRegionClosure {
|
||||
void G1CollectedHeap::eagerly_reclaim_humongous_regions() {
|
||||
assert_at_safepoint(true);
|
||||
|
||||
if (!G1ReclaimDeadHumongousObjectsAtYoungGC ||
|
||||
(!_has_humongous_reclaim_candidates && !G1TraceReclaimDeadHumongousObjectsAtYoungGC)) {
|
||||
if (!G1EagerReclaimHumongousObjects ||
|
||||
(!_has_humongous_reclaim_candidates && !G1TraceEagerReclaimHumongousObjects)) {
|
||||
g1_policy()->phase_times()->record_fast_reclaim_humongous_time_ms(0.0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -56,7 +56,6 @@ class HRRSCleanupTask;
|
||||
class GenerationSpec;
|
||||
class OopsInHeapRegionClosure;
|
||||
class G1KlassScanClosure;
|
||||
class G1ScanHeapEvacClosure;
|
||||
class ObjectClosure;
|
||||
class SpaceClosure;
|
||||
class CompactibleSpaceClosure;
|
||||
@ -186,32 +185,14 @@ class G1CollectedHeap : public SharedHeap {
|
||||
friend class SurvivorGCAllocRegion;
|
||||
friend class OldGCAllocRegion;
|
||||
friend class G1Allocator;
|
||||
friend class G1DefaultAllocator;
|
||||
friend class G1ResManAllocator;
|
||||
|
||||
// Closures used in implementation.
|
||||
template <G1Barrier barrier, G1Mark do_mark_object>
|
||||
friend class G1ParCopyClosure;
|
||||
friend class G1IsAliveClosure;
|
||||
friend class G1EvacuateFollowersClosure;
|
||||
friend class G1ParScanThreadState;
|
||||
friend class G1ParScanClosureSuper;
|
||||
friend class G1ParEvacuateFollowersClosure;
|
||||
friend class G1ParTask;
|
||||
friend class G1ParGCAllocator;
|
||||
friend class G1DefaultParGCAllocator;
|
||||
friend class G1FreeGarbageRegionClosure;
|
||||
friend class RefineCardTableEntryClosure;
|
||||
friend class G1PrepareCompactClosure;
|
||||
friend class RegionSorter;
|
||||
friend class RegionResetter;
|
||||
friend class CountRCClosure;
|
||||
friend class EvacPopObjClosure;
|
||||
friend class G1ParCleanupCTTask;
|
||||
|
||||
friend class G1FreeHumongousRegionClosure;
|
||||
// Other related classes.
|
||||
friend class G1MarkSweep;
|
||||
friend class HeapRegionClaimer;
|
||||
|
||||
// Testing classes.
|
||||
@ -659,6 +640,9 @@ public:
|
||||
// Returns whether the given region (which must be a humongous (start) region)
|
||||
// is to be considered conservatively live regardless of any other conditions.
|
||||
bool humongous_region_is_always_live(uint index);
|
||||
// Returns whether the given region (which must be a humongous (start) region)
|
||||
// is considered a candidate for eager reclamation.
|
||||
bool humongous_region_is_candidate(uint index);
|
||||
// Register the given region to be part of the collection set.
|
||||
inline void register_humongous_region_with_in_cset_fast_test(uint index);
|
||||
// Register regions with humongous objects (actually on the start region) in
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2015, Oracle and/or its affiliates. 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
|
||||
@ -153,14 +153,6 @@ G1CollectorPolicy::G1CollectorPolicy() :
|
||||
_inc_cset_predicted_elapsed_time_ms(0.0),
|
||||
_inc_cset_predicted_elapsed_time_ms_diffs(0.0),
|
||||
|
||||
#ifdef _MSC_VER // the use of 'this' below gets a warning, make it go away
|
||||
#pragma warning( disable:4355 ) // 'this' : used in base member initializer list
|
||||
#endif // _MSC_VER
|
||||
|
||||
_short_lived_surv_rate_group(new SurvRateGroup(this, "Short Lived",
|
||||
G1YoungSurvRateNumRegionsSummary)),
|
||||
_survivor_surv_rate_group(new SurvRateGroup(this, "Survivor",
|
||||
G1YoungSurvRateNumRegionsSummary)),
|
||||
// add here any more surv rate groups
|
||||
_recorded_survivor_regions(0),
|
||||
_recorded_survivor_head(NULL),
|
||||
@ -169,6 +161,22 @@ G1CollectorPolicy::G1CollectorPolicy() :
|
||||
|
||||
_gc_overhead_perc(0.0) {
|
||||
|
||||
uintx confidence_perc = G1ConfidencePercent;
|
||||
// Put an artificial ceiling on this so that it's not set to a silly value.
|
||||
if (confidence_perc > 100) {
|
||||
confidence_perc = 100;
|
||||
warning("G1ConfidencePercent is set to a value that is too large, "
|
||||
"it's been updated to %u", confidence_perc);
|
||||
}
|
||||
// '_sigma' must be initialized before the SurvRateGroups below because they
|
||||
// indirecty access '_sigma' trough the 'this' pointer in their constructor.
|
||||
_sigma = (double) confidence_perc / 100.0;
|
||||
|
||||
_short_lived_surv_rate_group =
|
||||
new SurvRateGroup(this, "Short Lived", G1YoungSurvRateNumRegionsSummary);
|
||||
_survivor_surv_rate_group =
|
||||
new SurvRateGroup(this, "Survivor", G1YoungSurvRateNumRegionsSummary);
|
||||
|
||||
// Set up the region size and associated fields. Given that the
|
||||
// policy is created before the heap, we have to set this up here,
|
||||
// so it's done as soon as possible.
|
||||
@ -283,15 +291,6 @@ G1CollectorPolicy::G1CollectorPolicy() :
|
||||
double time_slice = (double) GCPauseIntervalMillis / 1000.0;
|
||||
_mmu_tracker = new G1MMUTrackerQueue(time_slice, max_gc_time);
|
||||
|
||||
uintx confidence_perc = G1ConfidencePercent;
|
||||
// Put an artificial ceiling on this so that it's not set to a silly value.
|
||||
if (confidence_perc > 100) {
|
||||
confidence_perc = 100;
|
||||
warning("G1ConfidencePercent is set to a value that is too large, "
|
||||
"it's been updated to %u", confidence_perc);
|
||||
}
|
||||
_sigma = (double) confidence_perc / 100.0;
|
||||
|
||||
// start conservatively (around 50ms is about right)
|
||||
_concurrent_mark_remark_times_ms->add(0.05);
|
||||
_concurrent_mark_cleanup_times_ms->add(0.20);
|
||||
|
||||
@ -344,11 +344,14 @@ void G1GCPhaseTimes::print(double pause_time_sec) {
|
||||
_last_redirty_logged_cards_time_ms.print(3, "Parallel Redirty");
|
||||
_last_redirty_logged_cards_processed_cards.print(3, "Redirtied Cards");
|
||||
}
|
||||
if (G1ReclaimDeadHumongousObjectsAtYoungGC) {
|
||||
print_stats(2, "Humongous Reclaim", _cur_fast_reclaim_humongous_time_ms);
|
||||
if (G1EagerReclaimHumongousObjects) {
|
||||
print_stats(2, "Humongous Register", _cur_fast_reclaim_humongous_register_time_ms);
|
||||
if (G1Log::finest()) {
|
||||
print_stats(3, "Humongous Total", _cur_fast_reclaim_humongous_total);
|
||||
print_stats(3, "Humongous Candidate", _cur_fast_reclaim_humongous_candidates);
|
||||
}
|
||||
print_stats(2, "Humongous Reclaim", _cur_fast_reclaim_humongous_time_ms);
|
||||
if (G1Log::finest()) {
|
||||
print_stats(3, "Humongous Reclaimed", _cur_fast_reclaim_humongous_reclaimed);
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,6 +157,7 @@ class G1GCPhaseTimes : public CHeapObj<mtGC> {
|
||||
double _recorded_non_young_free_cset_time_ms;
|
||||
|
||||
double _cur_fast_reclaim_humongous_time_ms;
|
||||
double _cur_fast_reclaim_humongous_register_time_ms;
|
||||
size_t _cur_fast_reclaim_humongous_total;
|
||||
size_t _cur_fast_reclaim_humongous_candidates;
|
||||
size_t _cur_fast_reclaim_humongous_reclaimed;
|
||||
@ -283,7 +284,8 @@ class G1GCPhaseTimes : public CHeapObj<mtGC> {
|
||||
_recorded_non_young_free_cset_time_ms = time_ms;
|
||||
}
|
||||
|
||||
void record_fast_reclaim_humongous_stats(size_t total, size_t candidates) {
|
||||
void record_fast_reclaim_humongous_stats(double time_ms, size_t total, size_t candidates) {
|
||||
_cur_fast_reclaim_humongous_register_time_ms = time_ms;
|
||||
_cur_fast_reclaim_humongous_total = total;
|
||||
_cur_fast_reclaim_humongous_candidates = candidates;
|
||||
}
|
||||
|
||||
@ -46,9 +46,6 @@ class ReferenceProcessor;
|
||||
class G1PrepareCompactClosure;
|
||||
|
||||
class G1MarkSweep : AllStatic {
|
||||
friend class VM_G1MarkSweep;
|
||||
friend class Scavenge;
|
||||
|
||||
public:
|
||||
|
||||
static void invoke_at_safepoint(ReferenceProcessor* rp,
|
||||
|
||||
@ -45,7 +45,8 @@
|
||||
#include "utilities/bitMap.inline.hpp"
|
||||
|
||||
G1PageBasedVirtualSpace::G1PageBasedVirtualSpace() : _low_boundary(NULL),
|
||||
_high_boundary(NULL), _committed(), _page_size(0), _special(false), _executable(false) {
|
||||
_high_boundary(NULL), _committed(), _page_size(0), _special(false),
|
||||
_dirty(), _executable(false) {
|
||||
}
|
||||
|
||||
bool G1PageBasedVirtualSpace::initialize_with_granularity(ReservedSpace rs, size_t page_size) {
|
||||
@ -66,6 +67,9 @@ bool G1PageBasedVirtualSpace::initialize_with_granularity(ReservedSpace rs, size
|
||||
assert(_committed.size() == 0, "virtual space initialized more than once");
|
||||
uintx size_in_bits = rs.size() / page_size;
|
||||
_committed.resize(size_in_bits, /* in_resource_area */ false);
|
||||
if (_special) {
|
||||
_dirty.resize(size_in_bits, /* in_resource_area */ false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -84,6 +88,7 @@ void G1PageBasedVirtualSpace::release() {
|
||||
_executable = false;
|
||||
_page_size = 0;
|
||||
_committed.resize(0, false);
|
||||
_dirty.resize(0, false);
|
||||
}
|
||||
|
||||
size_t G1PageBasedVirtualSpace::committed_size() const {
|
||||
@ -120,34 +125,43 @@ size_t G1PageBasedVirtualSpace::byte_size_for_pages(size_t num) {
|
||||
return num * _page_size;
|
||||
}
|
||||
|
||||
MemRegion G1PageBasedVirtualSpace::commit(uintptr_t start, size_t size_in_pages) {
|
||||
bool G1PageBasedVirtualSpace::commit(uintptr_t start, size_t size_in_pages) {
|
||||
// We need to make sure to commit all pages covered by the given area.
|
||||
guarantee(is_area_uncommitted(start, size_in_pages), "Specified area is not uncommitted");
|
||||
|
||||
if (!_special) {
|
||||
bool zero_filled = true;
|
||||
uintptr_t end = start + size_in_pages;
|
||||
|
||||
if (_special) {
|
||||
// Check for dirty pages and update zero_filled if any found.
|
||||
if (_dirty.get_next_one_offset(start,end) < end) {
|
||||
zero_filled = false;
|
||||
_dirty.clear_range(start, end);
|
||||
}
|
||||
} else {
|
||||
os::commit_memory_or_exit(page_start(start), byte_size_for_pages(size_in_pages), _executable,
|
||||
err_msg("Failed to commit pages from "SIZE_FORMAT" of length "SIZE_FORMAT, start, size_in_pages));
|
||||
}
|
||||
_committed.set_range(start, start + size_in_pages);
|
||||
_committed.set_range(start, end);
|
||||
|
||||
MemRegion result((HeapWord*)page_start(start), byte_size_for_pages(size_in_pages) / HeapWordSize);
|
||||
if (AlwaysPreTouch) {
|
||||
os::pretouch_memory((char*)result.start(), (char*)result.end());
|
||||
os::pretouch_memory(page_start(start), page_start(end));
|
||||
}
|
||||
return result;
|
||||
return zero_filled;
|
||||
}
|
||||
|
||||
MemRegion G1PageBasedVirtualSpace::uncommit(uintptr_t start, size_t size_in_pages) {
|
||||
void G1PageBasedVirtualSpace::uncommit(uintptr_t start, size_t size_in_pages) {
|
||||
guarantee(is_area_committed(start, size_in_pages), "checking");
|
||||
|
||||
if (!_special) {
|
||||
if (_special) {
|
||||
// Mark that memory is dirty. If committed again the memory might
|
||||
// need to be cleared explicitly.
|
||||
_dirty.set_range(start, start + size_in_pages);
|
||||
} else {
|
||||
os::uncommit_memory(page_start(start), byte_size_for_pages(size_in_pages));
|
||||
}
|
||||
|
||||
_committed.clear_range(start, start + size_in_pages);
|
||||
|
||||
MemRegion result((HeapWord*)page_start(start), byte_size_for_pages(size_in_pages) / HeapWordSize);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool G1PageBasedVirtualSpace::contains(const void* p) const {
|
||||
@ -157,7 +171,7 @@ bool G1PageBasedVirtualSpace::contains(const void* p) const {
|
||||
#ifndef PRODUCT
|
||||
void G1PageBasedVirtualSpace::print_on(outputStream* out) {
|
||||
out->print ("Virtual space:");
|
||||
if (special()) out->print(" (pinned in memory)");
|
||||
if (_special) out->print(" (pinned in memory)");
|
||||
out->cr();
|
||||
out->print_cr(" - committed: " SIZE_FORMAT, committed_size());
|
||||
out->print_cr(" - reserved: " SIZE_FORMAT, reserved_size());
|
||||
|
||||
@ -49,6 +49,12 @@ class G1PageBasedVirtualSpace VALUE_OBJ_CLASS_SPEC {
|
||||
// Bitmap used for verification of commit/uncommit operations.
|
||||
BitMap _committed;
|
||||
|
||||
// Bitmap used to keep track of which pages are dirty or not for _special
|
||||
// spaces. This is needed because for those spaces the underlying memory
|
||||
// will only be zero filled the first time it is committed. Calls to commit
|
||||
// will use this bitmap and return whether or not the memory is zero filled.
|
||||
BitMap _dirty;
|
||||
|
||||
// Indicates that the entire space has been committed and pinned in memory,
|
||||
// os::commit_memory() or os::uncommit_memory() have no function.
|
||||
bool _special;
|
||||
@ -71,12 +77,11 @@ class G1PageBasedVirtualSpace VALUE_OBJ_CLASS_SPEC {
|
||||
public:
|
||||
|
||||
// Commit the given area of pages starting at start being size_in_pages large.
|
||||
MemRegion commit(uintptr_t start, size_t size_in_pages);
|
||||
// Returns true if the given area is zero filled upon completion.
|
||||
bool commit(uintptr_t start, size_t size_in_pages);
|
||||
|
||||
// Uncommit the given area of pages starting at start being size_in_pages large.
|
||||
MemRegion uncommit(uintptr_t start, size_t size_in_pages);
|
||||
|
||||
bool special() const { return _special; }
|
||||
void uncommit(uintptr_t start, size_t size_in_pages);
|
||||
|
||||
// Initialization
|
||||
G1PageBasedVirtualSpace();
|
||||
|
||||
@ -67,9 +67,9 @@ class G1RegionsLargerThanCommitSizeMapper : public G1RegionToSpaceMapper {
|
||||
}
|
||||
|
||||
virtual void commit_regions(uintptr_t start_idx, size_t num_regions) {
|
||||
_storage.commit(start_idx * _pages_per_region, num_regions * _pages_per_region);
|
||||
bool zero_filled = _storage.commit(start_idx * _pages_per_region, num_regions * _pages_per_region);
|
||||
_commit_map.set_range(start_idx, start_idx + num_regions);
|
||||
fire_on_commit(start_idx, num_regions, true);
|
||||
fire_on_commit(start_idx, num_regions, zero_filled);
|
||||
}
|
||||
|
||||
virtual void uncommit_regions(uintptr_t start_idx, size_t num_regions) {
|
||||
@ -117,8 +117,7 @@ class G1RegionsSmallerThanCommitSizeMapper : public G1RegionToSpaceMapper {
|
||||
uint old_refcount = _refcounts.get_by_index(idx);
|
||||
bool zero_filled = false;
|
||||
if (old_refcount == 0) {
|
||||
_storage.commit(idx, 1);
|
||||
zero_filled = true;
|
||||
zero_filled = _storage.commit(idx, 1);
|
||||
}
|
||||
_refcounts.set_by_index(idx, old_refcount + 1);
|
||||
_commit_map.set_bit(i);
|
||||
|
||||
@ -31,7 +31,6 @@
|
||||
// collection set.
|
||||
|
||||
class G1CollectedHeap;
|
||||
class CardTableModRefBarrierSet;
|
||||
class ConcurrentG1Refine;
|
||||
class G1ParPushHeapRSClosure;
|
||||
|
||||
|
||||
@ -270,10 +270,14 @@
|
||||
product(uintx, G1MixedGCCountTarget, 8, \
|
||||
"The target number of mixed GCs after a marking cycle.") \
|
||||
\
|
||||
experimental(bool, G1ReclaimDeadHumongousObjectsAtYoungGC, true, \
|
||||
experimental(bool, G1EagerReclaimHumongousObjects, true, \
|
||||
"Try to reclaim dead large objects at every young GC.") \
|
||||
\
|
||||
experimental(bool, G1TraceReclaimDeadHumongousObjectsAtYoungGC, false, \
|
||||
experimental(bool, G1EagerReclaimHumongousObjectsWithStaleRefs, true, \
|
||||
"Try to reclaim dead large objects that have a few stale " \
|
||||
"references at every young GC.") \
|
||||
\
|
||||
experimental(bool, G1TraceEagerReclaimHumongousObjects, false, \
|
||||
"Print some information about large object liveness " \
|
||||
"at every young GC.") \
|
||||
\
|
||||
|
||||
@ -681,6 +681,18 @@ void OtherRegionsTable::scrub(CardTableModRefBS* ctbs,
|
||||
clear_fcc();
|
||||
}
|
||||
|
||||
bool OtherRegionsTable::occupancy_less_or_equal_than(size_t limit) const {
|
||||
if (limit <= (size_t)G1RSetSparseRegionEntries) {
|
||||
return occ_coarse() == 0 && _first_all_fine_prts == NULL && occ_sparse() <= limit;
|
||||
} else {
|
||||
// Current uses of this method may only use values less than G1RSetSparseRegionEntries
|
||||
// for the limit. The solution, comparing against occupied() would be too slow
|
||||
// at this time.
|
||||
Unimplemented();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool OtherRegionsTable::is_empty() const {
|
||||
return occ_sparse() == 0 && occ_coarse() == 0 && _first_all_fine_prts == NULL;
|
||||
}
|
||||
|
||||
@ -183,6 +183,10 @@ public:
|
||||
// Returns whether the remembered set contains the given reference.
|
||||
bool contains_reference(OopOrNarrowOopStar from) const;
|
||||
|
||||
// Returns whether this remembered set (and all sub-sets) have an occupancy
|
||||
// that is less or equal than the given occupancy.
|
||||
bool occupancy_less_or_equal_than(size_t limit) const;
|
||||
|
||||
// Removes any entries shown by the given bitmaps to contain only dead
|
||||
// objects. Not thread safe.
|
||||
// Set bits in the bitmaps indicate that the given region or card is live.
|
||||
@ -261,6 +265,10 @@ public:
|
||||
return (strong_code_roots_list_length() == 0) && _other_regions.is_empty();
|
||||
}
|
||||
|
||||
bool occupancy_less_or_equal_than(size_t occ) const {
|
||||
return (strong_code_roots_list_length() == 0) && _other_regions.occupancy_less_or_equal_than(occ);
|
||||
}
|
||||
|
||||
size_t occupied() {
|
||||
MutexLockerEx x(&_m, Mutex::_no_safepoint_check_flag);
|
||||
return occupied_locked();
|
||||
|
||||
@ -31,11 +31,15 @@
|
||||
#include "runtime/thread.inline.hpp"
|
||||
|
||||
PtrQueue::PtrQueue(PtrQueueSet* qset, bool perm, bool active) :
|
||||
_qset(qset), _buf(NULL), _index(0), _active(active),
|
||||
_qset(qset), _buf(NULL), _index(0), _sz(0), _active(active),
|
||||
_perm(perm), _lock(NULL)
|
||||
{}
|
||||
|
||||
void PtrQueue::flush() {
|
||||
PtrQueue::~PtrQueue() {
|
||||
assert(_perm || (_buf == NULL), "queue must be flushed before delete");
|
||||
}
|
||||
|
||||
void PtrQueue::flush_impl() {
|
||||
if (!_perm && _buf != NULL) {
|
||||
if (_index == _sz) {
|
||||
// No work to do.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2014, Oracle and/or its affiliates. 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
|
||||
@ -65,15 +65,18 @@ protected:
|
||||
Mutex* _lock;
|
||||
|
||||
PtrQueueSet* qset() { return _qset; }
|
||||
bool is_permanent() const { return _perm; }
|
||||
|
||||
// Process queue entries and release resources, if not permanent.
|
||||
void flush_impl();
|
||||
|
||||
public:
|
||||
// Initialize this queue to contain a null buffer, and be part of the
|
||||
// given PtrQueueSet.
|
||||
PtrQueue(PtrQueueSet* qset, bool perm = false, bool active = false);
|
||||
// Release any contained resources.
|
||||
virtual void flush();
|
||||
// Calls flush() when destroyed.
|
||||
~PtrQueue() { flush(); }
|
||||
|
||||
// Requires queue flushed or permanent.
|
||||
~PtrQueue();
|
||||
|
||||
// Associate a lock with a ptr queue.
|
||||
void set_lock(Mutex* lock) { _lock = lock; }
|
||||
|
||||
@ -39,7 +39,7 @@ void ObjPtrQueue::flush() {
|
||||
// first before we flush it, otherwise we might end up with an
|
||||
// enqueued buffer with refs into the CSet which breaks our invariants.
|
||||
filter();
|
||||
PtrQueue::flush();
|
||||
flush_impl();
|
||||
}
|
||||
|
||||
// This method removes entries from an SATB buffer that will not be
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2014, Oracle and/or its affiliates. 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
|
||||
@ -60,9 +60,8 @@ public:
|
||||
// field to true. This is done in JavaThread::initialize_queues().
|
||||
PtrQueue(qset, perm, false /* active */) { }
|
||||
|
||||
// Overrides PtrQueue::flush() so that it can filter the buffer
|
||||
// before it is flushed.
|
||||
virtual void flush();
|
||||
// Process queue entries and free resources.
|
||||
void flush();
|
||||
|
||||
// Overrides PtrQueue::should_enqueue_buffer(). See the method's
|
||||
// definition for more information.
|
||||
|
||||
@ -63,7 +63,8 @@ public:
|
||||
virtual ~ParGCAllocBuffer() {}
|
||||
|
||||
static const size_t min_size() {
|
||||
return ThreadLocalAllocBuffer::min_size();
|
||||
// Make sure that we return something that is larger than AlignmentReserve
|
||||
return align_object_size(MAX2(MinTLABSize / HeapWordSize, (uintx)oopDesc::header_size())) + AlignmentReserve;
|
||||
}
|
||||
|
||||
static const size_t max_size() {
|
||||
|
||||
@ -320,7 +320,7 @@ int LinkResolver::vtable_index_of_interface_method(KlassHandle klass,
|
||||
// First check in default method array
|
||||
if (!resolved_method->is_abstract() &&
|
||||
(InstanceKlass::cast(klass())->default_methods() != NULL)) {
|
||||
int index = InstanceKlass::find_method_index(InstanceKlass::cast(klass())->default_methods(), name, signature, false);
|
||||
int index = InstanceKlass::find_method_index(InstanceKlass::cast(klass())->default_methods(), name, signature, false, false);
|
||||
if (index >= 0 ) {
|
||||
vtable_index = InstanceKlass::cast(klass())->default_vtable_indices()->at(index);
|
||||
}
|
||||
|
||||
@ -279,10 +279,12 @@ size_t CodeHeap::alignment_offset() const {
|
||||
return sizeof(HeapBlock) & (_segment_size - 1);
|
||||
}
|
||||
|
||||
// Finds the next free heapblock. If the current one is free, that it returned
|
||||
void* CodeHeap::next_free(HeapBlock* b) const {
|
||||
// Since free blocks are merged, there is max. on free block
|
||||
// between two used ones
|
||||
// Returns the current block if available and used.
|
||||
// If not, it returns the subsequent block (if available), NULL otherwise.
|
||||
// Free blocks are merged, therefore there is at most one free block
|
||||
// between two used ones. As a result, the subsequent block (if available) is
|
||||
// guaranteed to be used.
|
||||
void* CodeHeap::next_used(HeapBlock* b) const {
|
||||
if (b != NULL && b->free()) b = next_block(b);
|
||||
assert(b == NULL || !b->free(), "must be in use or at end of heap");
|
||||
return (b == NULL) ? NULL : b->allocated_space();
|
||||
|
||||
@ -123,7 +123,7 @@ class CodeHeap : public CHeapObj<mtCode> {
|
||||
FreeBlock* search_freelist(size_t length);
|
||||
|
||||
// Iteration helpers
|
||||
void* next_free(HeapBlock* b) const;
|
||||
void* next_used(HeapBlock* b) const;
|
||||
HeapBlock* first_block() const;
|
||||
HeapBlock* next_block(HeapBlock* b) const;
|
||||
HeapBlock* block_start(void* p) const;
|
||||
@ -158,9 +158,9 @@ class CodeHeap : public CHeapObj<mtCode> {
|
||||
int freelist_length() const { return _freelist_length; } // number of elements in the freelist
|
||||
|
||||
// returns the first block or NULL
|
||||
void* first() const { return next_free(first_block()); }
|
||||
void* first() const { return next_used(first_block()); }
|
||||
// returns the next block given a block p or NULL
|
||||
void* next(void* p) const { return next_free(next_block(block_start(p))); }
|
||||
void* next(void* p) const { return next_used(next_block(block_start(p))); }
|
||||
|
||||
// Statistics
|
||||
size_t capacity() const;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2015, Oracle and/or its affiliates. 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
|
||||
@ -422,7 +422,7 @@ VirtualSpaceNode::VirtualSpaceNode(size_t bytes) : _top(NULL), _next(NULL), _rs(
|
||||
bool large_pages = false; // No large pages when dumping the CDS archive.
|
||||
char* shared_base = (char*)align_ptr_up((char*)SharedBaseAddress, Metaspace::reserve_alignment());
|
||||
|
||||
_rs = ReservedSpace(bytes, Metaspace::reserve_alignment(), large_pages, shared_base, 0);
|
||||
_rs = ReservedSpace(bytes, Metaspace::reserve_alignment(), large_pages, shared_base);
|
||||
if (_rs.is_reserved()) {
|
||||
assert(shared_base == 0 || _rs.base() == shared_base, "should match");
|
||||
} else {
|
||||
@ -3025,7 +3025,7 @@ void Metaspace::allocate_metaspace_compressed_klass_ptrs(char* requested_addr, a
|
||||
ReservedSpace metaspace_rs = ReservedSpace(compressed_class_space_size(),
|
||||
_reserve_alignment,
|
||||
large_pages,
|
||||
requested_addr, 0);
|
||||
requested_addr);
|
||||
if (!metaspace_rs.is_reserved()) {
|
||||
#if INCLUDE_CDS
|
||||
if (UseSharedSpaces) {
|
||||
@ -3039,7 +3039,7 @@ void Metaspace::allocate_metaspace_compressed_klass_ptrs(char* requested_addr, a
|
||||
can_use_cds_with_metaspace_addr(addr + increment, cds_base)) {
|
||||
addr = addr + increment;
|
||||
metaspace_rs = ReservedSpace(compressed_class_space_size(),
|
||||
_reserve_alignment, large_pages, addr, 0);
|
||||
_reserve_alignment, large_pages, addr);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -3170,7 +3170,9 @@ void Metaspace::global_initialize() {
|
||||
}
|
||||
|
||||
// the min_misc_data_size and min_misc_code_size estimates are based on
|
||||
// MetaspaceShared::generate_vtable_methods()
|
||||
// MetaspaceShared::generate_vtable_methods().
|
||||
// The minimum size only accounts for the vtable methods. Any size less than the
|
||||
// minimum required size would cause vm crash when allocating the vtable methods.
|
||||
uint min_misc_data_size = align_size_up(
|
||||
MetaspaceShared::num_virtuals * MetaspaceShared::vtbl_list_size * sizeof(void*), max_alignment);
|
||||
|
||||
@ -3336,6 +3338,10 @@ void Metaspace::initialize(Mutex* lock, MetaspaceType type) {
|
||||
Metachunk* new_chunk = get_initialization_chunk(NonClassType,
|
||||
word_size,
|
||||
vsm()->medium_chunk_bunch());
|
||||
// For dumping shared archive, report error if allocation has failed.
|
||||
if (DumpSharedSpaces && new_chunk == NULL) {
|
||||
report_insufficient_metaspace(MetaspaceAux::committed_bytes() + word_size * BytesPerWord);
|
||||
}
|
||||
assert(!DumpSharedSpaces || new_chunk != NULL, "should have enough space for both chunks");
|
||||
if (new_chunk != NULL) {
|
||||
// Add to this manager's list of chunks in use and current_chunk().
|
||||
@ -3349,6 +3355,11 @@ void Metaspace::initialize(Mutex* lock, MetaspaceType type) {
|
||||
class_vsm()->medium_chunk_bunch());
|
||||
if (class_chunk != NULL) {
|
||||
class_vsm()->add_chunk(class_chunk, true);
|
||||
} else {
|
||||
// For dumping shared archive, report error if allocation has failed.
|
||||
if (DumpSharedSpaces) {
|
||||
report_insufficient_metaspace(MetaspaceAux::committed_bytes() + class_word_size * BytesPerWord);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3829,11 +3840,13 @@ class TestVirtualSpaceNodeTest {
|
||||
assert(cm.sum_free_chunks() == 2*MediumChunk, "sizes should add up");
|
||||
}
|
||||
|
||||
{ // 4 pages of VSN is committed, some is used by chunks
|
||||
const size_t page_chunks = 4 * (size_t)os::vm_page_size() / BytesPerWord;
|
||||
// This doesn't work for systems with vm_page_size >= 16K.
|
||||
if (page_chunks < MediumChunk) {
|
||||
// 4 pages of VSN is committed, some is used by chunks
|
||||
ChunkManager cm(SpecializedChunk, SmallChunk, MediumChunk);
|
||||
VirtualSpaceNode vsn(vsn_test_size_bytes);
|
||||
const size_t page_chunks = 4 * (size_t)os::vm_page_size() / BytesPerWord;
|
||||
assert(page_chunks < MediumChunk, "Test expects medium chunks to be at least 4*page_size");
|
||||
|
||||
vsn.initialize();
|
||||
vsn.expand_by(page_chunks, page_chunks);
|
||||
vsn.get_chunk_vs(SmallChunk);
|
||||
|
||||
@ -48,6 +48,8 @@ int MetaspaceShared::_max_alignment = 0;
|
||||
|
||||
ReservedSpace* MetaspaceShared::_shared_rs = NULL;
|
||||
|
||||
MetaspaceSharedStats MetaspaceShared::_stats;
|
||||
|
||||
bool MetaspaceShared::_link_classes_made_progress;
|
||||
bool MetaspaceShared::_check_classes_made_progress;
|
||||
bool MetaspaceShared::_has_error_classes;
|
||||
@ -259,7 +261,7 @@ public:
|
||||
#define SHAREDSPACE_OBJ_TYPES_DO(f) \
|
||||
METASPACE_OBJ_TYPES_DO(f) \
|
||||
f(SymbolHashentry) \
|
||||
f(SymbolBuckets) \
|
||||
f(SymbolBucket) \
|
||||
f(Other)
|
||||
|
||||
#define SHAREDSPACE_OBJ_TYPE_DECLARE(name) name ## Type,
|
||||
@ -315,18 +317,16 @@ void DumpAllocClosure::dump_stats(int ro_all, int rw_all, int md_all, int mc_all
|
||||
int other_bytes = md_all + mc_all;
|
||||
|
||||
// Calculate size of data that was not allocated by Metaspace::allocate()
|
||||
int symbol_count = _counts[RO][MetaspaceObj::SymbolType];
|
||||
int symhash_bytes = symbol_count * sizeof (HashtableEntry<Symbol*, mtSymbol>);
|
||||
int symbuck_count = SymbolTable::the_table()->table_size();
|
||||
int symbuck_bytes = symbuck_count * sizeof(HashtableBucket<mtSymbol>);
|
||||
MetaspaceSharedStats *stats = MetaspaceShared::stats();
|
||||
|
||||
_counts[RW][SymbolHashentryType] = symbol_count;
|
||||
_bytes [RW][SymbolHashentryType] = symhash_bytes;
|
||||
other_bytes -= symhash_bytes;
|
||||
// symbols
|
||||
_counts[RW][SymbolHashentryType] = stats->symbol.hashentry_count;
|
||||
_bytes [RW][SymbolHashentryType] = stats->symbol.hashentry_bytes;
|
||||
other_bytes -= stats->symbol.hashentry_bytes;
|
||||
|
||||
_counts[RW][SymbolBucketsType] = symbuck_count;
|
||||
_bytes [RW][SymbolBucketsType] = symbuck_bytes;
|
||||
other_bytes -= symbuck_bytes;
|
||||
_counts[RW][SymbolBucketType] = stats->symbol.bucket_count;
|
||||
_bytes [RW][SymbolBucketType] = stats->symbol.bucket_bytes;
|
||||
other_bytes -= stats->symbol.bucket_bytes;
|
||||
|
||||
// TODO: count things like dictionary, vtable, etc
|
||||
_bytes[RW][OtherType] = other_bytes;
|
||||
@ -424,6 +424,13 @@ public:
|
||||
|
||||
VMOp_Type type() const { return VMOp_PopulateDumpSharedSpace; }
|
||||
void doit(); // outline because gdb sucks
|
||||
|
||||
private:
|
||||
void handle_misc_data_space_failure(bool success) {
|
||||
if (!success) {
|
||||
report_out_of_shared_space(SharedMiscData);
|
||||
}
|
||||
}
|
||||
}; // class VM_PopulateDumpSharedSpace
|
||||
|
||||
|
||||
@ -517,9 +524,8 @@ void VM_PopulateDumpSharedSpace::doit() {
|
||||
// buckets first [read-write], then copy the linked lists of entries
|
||||
// [read-only].
|
||||
|
||||
SymbolTable::reverse(md_top);
|
||||
NOT_PRODUCT(SymbolTable::verify());
|
||||
SymbolTable::copy_buckets(&md_top, md_end);
|
||||
handle_misc_data_space_failure(SymbolTable::copy_compact_table(&md_top, md_end));
|
||||
|
||||
SystemDictionary::reverse();
|
||||
SystemDictionary::copy_buckets(&md_top, md_end);
|
||||
@ -528,7 +534,6 @@ void VM_PopulateDumpSharedSpace::doit() {
|
||||
ClassLoader::copy_package_info_buckets(&md_top, md_end);
|
||||
ClassLoader::verify();
|
||||
|
||||
SymbolTable::copy_table(&md_top, md_end);
|
||||
SystemDictionary::copy_table(&md_top, md_end);
|
||||
ClassLoader::verify();
|
||||
ClassLoader::copy_package_info_table(&md_top, md_end);
|
||||
@ -1000,17 +1005,12 @@ void MetaspaceShared::initialize_shared_spaces() {
|
||||
buffer += sizeof(intptr_t);
|
||||
buffer += vtable_size;
|
||||
|
||||
// Create the symbol table using the bucket array at this spot in the
|
||||
// misc data space. Since the symbol table is often modified, this
|
||||
// region (of mapped pages) will be copy-on-write.
|
||||
// Create the shared symbol table using the bucket array at this spot in the
|
||||
// misc data space. (Todo: move this to read-only space. Currently
|
||||
// this is mapped copy-on-write but will never be written into).
|
||||
|
||||
int symbolTableLen = *(intptr_t*)buffer;
|
||||
buffer += sizeof(intptr_t);
|
||||
int number_of_entries = *(intptr_t*)buffer;
|
||||
buffer += sizeof(intptr_t);
|
||||
SymbolTable::create_table((HashtableBucket<mtSymbol>*)buffer, symbolTableLen,
|
||||
number_of_entries);
|
||||
buffer += symbolTableLen;
|
||||
buffer = (char*)SymbolTable::init_shared_table(buffer);
|
||||
SymbolTable::create_table();
|
||||
|
||||
// Create the shared dictionary using the bucket array at this spot in
|
||||
// the misc data space. Since the shared dictionary table is never
|
||||
@ -1019,7 +1019,7 @@ void MetaspaceShared::initialize_shared_spaces() {
|
||||
|
||||
int sharedDictionaryLen = *(intptr_t*)buffer;
|
||||
buffer += sizeof(intptr_t);
|
||||
number_of_entries = *(intptr_t*)buffer;
|
||||
int number_of_entries = *(intptr_t*)buffer;
|
||||
buffer += sizeof(intptr_t);
|
||||
SystemDictionary::set_shared_dictionary((HashtableBucket<mtClass>*)buffer,
|
||||
sharedDictionaryLen,
|
||||
@ -1041,18 +1041,10 @@ void MetaspaceShared::initialize_shared_spaces() {
|
||||
ClassLoader::verify();
|
||||
|
||||
// The following data in the shared misc data region are the linked
|
||||
// list elements (HashtableEntry objects) for the symbol table, string
|
||||
// table, and shared dictionary. The heap objects referred to by the
|
||||
// symbol table, string table, and shared dictionary are permanent and
|
||||
// unmovable. Since new entries added to the string and symbol tables
|
||||
// are always added at the beginning of the linked lists, THESE LINKED
|
||||
// LIST ELEMENTS ARE READ-ONLY.
|
||||
// list elements (HashtableEntry objects) for the shared dictionary
|
||||
// and package info table.
|
||||
|
||||
int len = *(intptr_t*)buffer; // skip over symbol table entries
|
||||
buffer += sizeof(intptr_t);
|
||||
buffer += len;
|
||||
|
||||
len = *(intptr_t*)buffer; // skip over shared dictionary entries
|
||||
int len = *(intptr_t*)buffer; // skip over shared dictionary entries
|
||||
buffer += sizeof(intptr_t);
|
||||
buffer += len;
|
||||
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
#ifndef SHARE_VM_MEMORY_METASPACE_SHARED_HPP
|
||||
#define SHARE_VM_MEMORY_METASPACE_SHARED_HPP
|
||||
|
||||
#include "classfile/compactHashtable.hpp"
|
||||
#include "memory/allocation.hpp"
|
||||
#include "memory/memRegion.hpp"
|
||||
#include "runtime/virtualspace.hpp"
|
||||
@ -45,12 +46,21 @@
|
||||
|
||||
class FileMapInfo;
|
||||
|
||||
class MetaspaceSharedStats VALUE_OBJ_CLASS_SPEC {
|
||||
public:
|
||||
MetaspaceSharedStats() {
|
||||
memset(this, 0, sizeof(*this));
|
||||
}
|
||||
CompactHashtableStats symbol;
|
||||
};
|
||||
|
||||
// Class Data Sharing Support
|
||||
class MetaspaceShared : AllStatic {
|
||||
|
||||
// CDS support
|
||||
static ReservedSpace* _shared_rs;
|
||||
static int _max_alignment;
|
||||
static MetaspaceSharedStats _stats;
|
||||
static bool _link_classes_made_progress;
|
||||
static bool _check_classes_made_progress;
|
||||
static bool _has_error_classes;
|
||||
@ -123,6 +133,10 @@ class MetaspaceShared : AllStatic {
|
||||
char** mc_top, char* mc_end);
|
||||
static void serialize(SerializeClosure* sc);
|
||||
|
||||
static MetaspaceSharedStats* stats() {
|
||||
return &_stats;
|
||||
}
|
||||
|
||||
// JVM/TI RedefineClasses() support:
|
||||
// Remap the shared readonly space to shared readwrite, private if
|
||||
// sharing is enabled. Simply returns true if sharing is not enabled
|
||||
|
||||
@ -118,6 +118,7 @@ ReferenceProcessor::ReferenceProcessor(MemRegion span,
|
||||
_discoveredWeakRefs = &_discoveredSoftRefs[_max_num_q];
|
||||
_discoveredFinalRefs = &_discoveredWeakRefs[_max_num_q];
|
||||
_discoveredPhantomRefs = &_discoveredFinalRefs[_max_num_q];
|
||||
_discoveredCleanerRefs = &_discoveredPhantomRefs[_max_num_q];
|
||||
|
||||
// Initialize all entries to NULL
|
||||
for (uint i = 0; i < _max_num_q * number_of_subclasses_of_ref(); i++) {
|
||||
@ -246,6 +247,13 @@ ReferenceProcessorStats ReferenceProcessor::process_discovered_references(
|
||||
phantom_count =
|
||||
process_discovered_reflist(_discoveredPhantomRefs, NULL, false,
|
||||
is_alive, keep_alive, complete_gc, task_executor);
|
||||
|
||||
// Process cleaners, but include them in phantom statistics. We expect
|
||||
// Cleaner references to be temporary, and don't want to deal with
|
||||
// possible incompatibilities arising from making it more visible.
|
||||
phantom_count +=
|
||||
process_discovered_reflist(_discoveredCleanerRefs, NULL, false,
|
||||
is_alive, keep_alive, complete_gc, task_executor);
|
||||
}
|
||||
|
||||
// Weak global JNI references. It would make more sense (semantically) to
|
||||
@ -885,6 +893,7 @@ void ReferenceProcessor::balance_all_queues() {
|
||||
balance_queues(_discoveredWeakRefs);
|
||||
balance_queues(_discoveredFinalRefs);
|
||||
balance_queues(_discoveredPhantomRefs);
|
||||
balance_queues(_discoveredCleanerRefs);
|
||||
}
|
||||
|
||||
size_t
|
||||
@ -998,6 +1007,9 @@ inline DiscoveredList* ReferenceProcessor::get_discovered_list(ReferenceType rt)
|
||||
case REF_PHANTOM:
|
||||
list = &_discoveredPhantomRefs[id];
|
||||
break;
|
||||
case REF_CLEANER:
|
||||
list = &_discoveredCleanerRefs[id];
|
||||
break;
|
||||
case REF_NONE:
|
||||
// we should not reach here if we are an InstanceRefKlass
|
||||
default:
|
||||
@ -1263,6 +1275,17 @@ void ReferenceProcessor::preclean_discovered_references(
|
||||
preclean_discovered_reflist(_discoveredPhantomRefs[i], is_alive,
|
||||
keep_alive, complete_gc, yield);
|
||||
}
|
||||
|
||||
// Cleaner references. Included in timing for phantom references. We
|
||||
// expect Cleaner references to be temporary, and don't want to deal with
|
||||
// possible incompatibilities arising from making it more visible.
|
||||
for (uint i = 0; i < _max_num_q; i++) {
|
||||
if (yield->should_return()) {
|
||||
return;
|
||||
}
|
||||
preclean_discovered_reflist(_discoveredCleanerRefs[i], is_alive,
|
||||
keep_alive, complete_gc, yield);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1331,6 +1354,7 @@ const char* ReferenceProcessor::list_name(uint i) {
|
||||
case 1: return "WeakRef";
|
||||
case 2: return "FinalRef";
|
||||
case 3: return "PhantomRef";
|
||||
case 4: return "CleanerRef";
|
||||
}
|
||||
ShouldNotReachHere();
|
||||
return NULL;
|
||||
|
||||
@ -264,9 +264,10 @@ class ReferenceProcessor : public CHeapObj<mtGC> {
|
||||
DiscoveredList* _discoveredWeakRefs;
|
||||
DiscoveredList* _discoveredFinalRefs;
|
||||
DiscoveredList* _discoveredPhantomRefs;
|
||||
DiscoveredList* _discoveredCleanerRefs;
|
||||
|
||||
public:
|
||||
static int number_of_subclasses_of_ref() { return (REF_PHANTOM - REF_OTHER); }
|
||||
static int number_of_subclasses_of_ref() { return (REF_CLEANER - REF_OTHER); }
|
||||
|
||||
uint num_q() { return _num_q; }
|
||||
uint max_num_q() { return _max_num_q; }
|
||||
|
||||
@ -35,7 +35,8 @@ enum ReferenceType {
|
||||
REF_SOFT, // Subclass of java/lang/ref/SoftReference
|
||||
REF_WEAK, // Subclass of java/lang/ref/WeakReference
|
||||
REF_FINAL, // Subclass of java/lang/ref/FinalReference
|
||||
REF_PHANTOM // Subclass of java/lang/ref/PhantomReference
|
||||
REF_PHANTOM, // Subclass of java/lang/ref/PhantomReference
|
||||
REF_CLEANER // Subclass of sun/misc/Cleaner
|
||||
};
|
||||
|
||||
#endif // SHARE_VM_MEMORY_REFRERENCETYPE_HPP
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user