This commit is contained in:
Bob Vandette 2018-10-31 10:48:13 -04:00
commit f161afb806
111 changed files with 2404 additions and 1107 deletions

View File

@ -18,12 +18,13 @@
</header>
<nav id="TOC">
<ul>
<li><a href="#using-the-run-test-framework">Using the run-test framework</a><ul>
<li><a href="#using-make-test-the-run-test-framework">Using &quot;make test&quot; (the run-test framework)</a><ul>
<li><a href="#configuration">Configuration</a></li>
</ul></li>
<li><a href="#test-selection">Test selection</a><ul>
<li><a href="#jtreg">JTReg</a></li>
<li><a href="#gtest">Gtest</a></li>
<li><a href="#special-tests">Special tests</a></li>
</ul></li>
<li><a href="#test-results-and-summary">Test results and summary</a></li>
<li><a href="#test-suite-control">Test suite control</a><ul>
@ -32,22 +33,23 @@
</ul></li>
</ul>
</nav>
<h2 id="using-the-run-test-framework">Using the run-test framework</h2>
<h2 id="using-make-test-the-run-test-framework">Using &quot;make test&quot; (the run-test framework)</h2>
<p>This new way of running tests is developer-centric. It assumes that you have built a JDK locally and want to test it. Running common test targets is simple, and more complex ad-hoc combination of tests is possible. The user interface is forgiving, and clearly report errors it cannot resolve.</p>
<p>The main target &quot;run-test&quot; uses the jdk-image as the tested product. There is also an alternate target &quot;exploded-run-test&quot; that uses the exploded image instead. Not all tests will run successfully on the exploded image, but using this target can greatly improve rebuild times for certain workflows.</p>
<p>The main target <code>test</code> uses the jdk-image as the tested product. There is also an alternate target <code>exploded-test</code> that uses the exploded image instead. Not all tests will run successfully on the exploded image, but using this target can greatly improve rebuild times for certain workflows.</p>
<p>Previously, <code>make test</code> was used invoke an old system for running test, and <code>make run-test</code> was used for the new test framework. For backward compatibility with scripts and muscle memory, <code>run-test</code> (and variants like <code>exploded-run-test</code> or <code>run-test-tier1</code>) are kept as aliases. The old system can still be accessed for some time using <code>cd test &amp;&amp; make</code>.</p>
<p>Some example command-lines:</p>
<pre><code>$ make run-test-tier1
$ make run-test-jdk_lang JTREG=&quot;JOBS=8&quot;
$ make run-test TEST=jdk_lang
$ make run-test-only TEST=&quot;gtest:LogTagSet gtest:LogTagSetDescriptions&quot; GTEST=&quot;REPEAT=-1&quot;
$ make run-test TEST=&quot;hotspot:hotspot_gc&quot; JTREG=&quot;JOBS=1;TIMEOUT=8;VM_OPTIONS=-XshowSettings -Xlog:gc+ref=debug&quot;
$ make run-test TEST=&quot;jtreg:test/hotspot:hotspot_gc test/hotspot/jtreg/native_sanity/JniVersion.java&quot;
$ make exploded-run-test TEST=tier2</code></pre>
<pre><code>$ make test-tier1
$ make test-jdk_lang JTREG=&quot;JOBS=8&quot;
$ make test TEST=jdk_lang
$ make test-only TEST=&quot;gtest:LogTagSet gtest:LogTagSetDescriptions&quot; GTEST=&quot;REPEAT=-1&quot;
$ make test TEST=&quot;hotspot:hotspot_gc&quot; JTREG=&quot;JOBS=1;TIMEOUT=8;VM_OPTIONS=-XshowSettings -Xlog:gc+ref=debug&quot;
$ make test TEST=&quot;jtreg:test/hotspot:hotspot_gc test/hotspot/jtreg/native_sanity/JniVersion.java&quot;
$ make exploded-test TEST=tier2</code></pre>
<h3 id="configuration">Configuration</h3>
<p>To be able to run JTReg tests, <code>configure</code> needs to know where to find the JTReg test framework. If it is not picked up automatically by configure, use the <code>--with-jtreg=&lt;path to jtreg home&gt;</code> option to point to the JTReg framework. Note that this option should point to the JTReg home, i.e. the top directory, containing <code>lib/jtreg.jar</code> etc. (An alternative is to set the <code>JT_HOME</code> environment variable to point to the JTReg home before running <code>configure</code>.)</p>
<h2 id="test-selection">Test selection</h2>
<p>All functionality is available using the run-test make target. In this use case, the test or tests to be executed is controlled using the <code>TEST</code> variable. To speed up subsequent test runs with no source code changes, run-test-only can be used instead, which do not depend on the source and test image build.</p>
<p>For some common top-level tests, direct make targets have been generated. This includes all JTReg test groups, the hotspot gtest, and custom tests (if present). This means that <code>make run-test-tier1</code> is equivalent to <code>make run-test TEST=&quot;tier1&quot;</code>, but the latter is more tab-completion friendly. For more complex test runs, the <code>run-test TEST=&quot;x&quot;</code> solution needs to be used.</p>
<p>All functionality is available using the <code>test</code> make target. In this use case, the test or tests to be executed is controlled using the <code>TEST</code> variable. To speed up subsequent test runs with no source code changes, <code>test-only</code> can be used instead, which do not depend on the source and test image build.</p>
<p>For some common top-level tests, direct make targets have been generated. This includes all JTReg test groups, the hotspot gtest, and custom tests (if present). This means that <code>make test-tier1</code> is equivalent to <code>make test TEST=&quot;tier1&quot;</code>, but the latter is more tab-completion friendly. For more complex test runs, the <code>test TEST=&quot;x&quot;</code> solution needs to be used.</p>
<p>The test specifications given in <code>TEST</code> is parsed into fully qualified test descriptors, which clearly and unambigously show which tests will be run. As an example, <code>:tier1</code> will expand to <code>jtreg:$(TOPDIR)/test/hotspot/jtreg:tier1 jtreg:$(TOPDIR)/test/jdk:tier1 jtreg:$(TOPDIR)/test/langtools:tier1 jtreg:$(TOPDIR)/test/nashorn:tier1 jtreg:$(TOPDIR)/test/jaxp:tier1</code>. You can always submit a list of fully qualified test descriptors in the <code>TEST</code> variable if you want to shortcut the parser.</p>
<h3 id="jtreg">JTReg</h3>
<p>JTReg tests can be selected either by picking a JTReg test group, or a selection of files or directories containing JTReg tests.</p>
@ -59,6 +61,14 @@ $ make exploded-run-test TEST=tier2</code></pre>
<p>Since the Hotspot Gtest suite is so quick, the default is to run all tests. This is specified by just <code>gtest</code>, or as a fully qualified test descriptor <code>gtest:all</code>.</p>
<p>If you want, you can single out an individual test or a group of tests, for instance <code>gtest:LogDecorations</code> or <code>gtest:LogDecorations.level_test_vm</code>. This can be particularly useful if you want to run a shaky test repeatedly.</p>
<p>For Gtest, there is a separate test suite for each JVM variant. The JVM variant is defined by adding <code>/&lt;variant&gt;</code> to the test descriptor, e.g. <code>gtest:Log/client</code>. If you specify no variant, gtest will run once for each JVM variant present (e.g. server, client). So if you only have the server JVM present, then <code>gtest:all</code> will be equivalent to <code>gtest:all/server</code>.</p>
<h3 id="special-tests">Special tests</h3>
<p>A handful of odd tests that are not covered by any other testing framework are accessible using the <code>special:</code> test descriptor. Currently, this includes <code>hotspot-internal</code>, <code>failure-handler</code> and <code>make</code>.</p>
<ul>
<li><p>Hotspot legacy internal testing (run using <code>-XX:+ExecuteInternalVMTests</code>) is run using <code>special:hotspot-internal</code> or just <code>hotspot-internal</code> as test descriptor, and will only work on a debug JVM.</p></li>
<li><p>Failure handler testing is run using <code>special:failure-handler</code> or just <code>failure-handler</code> as test descriptor.</p></li>
<li><p>Tests for the build system, including both makefiles and related functionality, is run using <code>special:make</code> or just <code>make</code> as test descriptor. This is equivalent to <code>special:make:all</code>.</p>
<p>A specific make test can be run by supplying it as argument, e.g. <code>special:make:idea</code>. As a special syntax, this can also be expressed as <code>make-idea</code>, which allows for command lines as <code>make test-make-idea</code>.</p></li>
</ul>
<h2 id="test-results-and-summary">Test results and summary</h2>
<p>At the end of the test run, a summary of all tests run will be presented. This will have a consistent look, regardless of what test suites were used. This is a sample summary:</p>
<pre><code>==============================
@ -72,7 +82,7 @@ Test summary
TEST FAILURE</code></pre>
<p>Tests where the number of TOTAL tests does not equal the number of PASSed tests will be considered a test failure. These are marked with the <code>&gt;&gt; ... &lt;&lt;</code> marker for easy identification.</p>
<p>The classification of non-passed tests differs a bit between test suites. In the summary, ERROR is used as a catch-all for tests that neither passed nor are classified as failed by the framework. This might indicate test framework error, timeout or other problems.</p>
<p>In case of test failures, <code>make run-test</code> will exit with a non-zero exit value.</p>
<p>In case of test failures, <code>make test</code> will exit with a non-zero exit value.</p>
<p>All tests have their result stored in <code>build/$BUILD/test-results/$TEST_ID</code>, where TEST_ID is a path-safe conversion from the fully qualified test descriptor, e.g. for <code>jtreg:jdk/test:tier1</code> the TEST_ID is <code>jtreg_jdk_test_tier1</code>. This path is also printed in the log at the end of the test run.</p>
<p>Additional work data is stored in <code>build/$BUILD/test-support/$TEST_ID</code>. For some frameworks, this directory might contain information that is useful in determining the cause of a failed test.</p>
<h2 id="test-suite-control">Test suite control</h2>

View File

@ -1,26 +1,32 @@
% Testing the JDK
## Using the run-test framework
## Using "make test" (the run-test framework)
This new way of running tests is developer-centric. It assumes that you have
built a JDK locally and want to test it. Running common test targets is simple,
and more complex ad-hoc combination of tests is possible. The user interface is
forgiving, and clearly report errors it cannot resolve.
The main target "run-test" uses the jdk-image as the tested product. There is
also an alternate target "exploded-run-test" that uses the exploded image
The main target `test` uses the jdk-image as the tested product. There is
also an alternate target `exploded-test` that uses the exploded image
instead. Not all tests will run successfully on the exploded image, but using
this target can greatly improve rebuild times for certain workflows.
Previously, `make test` was used invoke an old system for running test, and
`make run-test` was used for the new test framework. For backward compatibility
with scripts and muscle memory, `run-test` (and variants like
`exploded-run-test` or `run-test-tier1`) are kept as aliases. The old system
can still be accessed for some time using `cd test && make`.
Some example command-lines:
$ make run-test-tier1
$ make run-test-jdk_lang JTREG="JOBS=8"
$ make run-test TEST=jdk_lang
$ make run-test-only TEST="gtest:LogTagSet gtest:LogTagSetDescriptions" GTEST="REPEAT=-1"
$ make run-test TEST="hotspot:hotspot_gc" JTREG="JOBS=1;TIMEOUT=8;VM_OPTIONS=-XshowSettings -Xlog:gc+ref=debug"
$ make run-test TEST="jtreg:test/hotspot:hotspot_gc test/hotspot/jtreg/native_sanity/JniVersion.java"
$ make exploded-run-test TEST=tier2
$ make test-tier1
$ make test-jdk_lang JTREG="JOBS=8"
$ make test TEST=jdk_lang
$ make test-only TEST="gtest:LogTagSet gtest:LogTagSetDescriptions" GTEST="REPEAT=-1"
$ make test TEST="hotspot:hotspot_gc" JTREG="JOBS=1;TIMEOUT=8;VM_OPTIONS=-XshowSettings -Xlog:gc+ref=debug"
$ make test TEST="jtreg:test/hotspot:hotspot_gc test/hotspot/jtreg/native_sanity/JniVersion.java"
$ make exploded-test TEST=tier2
### Configuration
@ -33,16 +39,16 @@ environment variable to point to the JTReg home before running `configure`.)
## Test selection
All functionality is available using the run-test make target. In this use
case, the test or tests to be executed is controlled using the `TEST` variable.
To speed up subsequent test runs with no source code changes, run-test-only can
be used instead, which do not depend on the source and test image build.
All functionality is available using the `test` make target. In this use case,
the test or tests to be executed is controlled using the `TEST` variable. To
speed up subsequent test runs with no source code changes, `test-only` can be
used instead, which do not depend on the source and test image build.
For some common top-level tests, direct make targets have been generated. This
includes all JTReg test groups, the hotspot gtest, and custom tests (if
present). This means that `make run-test-tier1` is equivalent to `make run-test
present). This means that `make test-tier1` is equivalent to `make test
TEST="tier1"`, but the latter is more tab-completion friendly. For more complex
test runs, the `run-test TEST="x"` solution needs to be used.
test runs, the `test TEST="x"` solution needs to be used.
The test specifications given in `TEST` is parsed into fully qualified test
descriptors, which clearly and unambigously show which tests will be run. As an
@ -98,6 +104,27 @@ is defined by adding `/<variant>` to the test descriptor, e.g.
variant present (e.g. server, client). So if you only have the server JVM
present, then `gtest:all` will be equivalent to `gtest:all/server`.
### Special tests
A handful of odd tests that are not covered by any other testing framework are
accessible using the `special:` test descriptor. Currently, this includes
`hotspot-internal`, `failure-handler` and `make`.
* Hotspot legacy internal testing (run using `-XX:+ExecuteInternalVMTests`)
is run using `special:hotspot-internal` or just `hotspot-internal` as test
descriptor, and will only work on a debug JVM.
* Failure handler testing is run using `special:failure-handler` or just
`failure-handler` as test descriptor.
* Tests for the build system, including both makefiles and related
functionality, is run using `special:make` or just `make` as test
descriptor. This is equivalent to `special:make:all`.
A specific make test can be run by supplying it as argument, e.g.
`special:make:idea`. As a special syntax, this can also be expressed as
`make-idea`, which allows for command lines as `make test-make-idea`.
## Test results and summary
At the end of the test run, a summary of all tests run will be presented. This
@ -123,7 +150,7 @@ the summary, ERROR is used as a catch-all for tests that neither passed nor are
classified as failed by the framework. This might indicate test framework
error, timeout or other problems.
In case of test failures, `make run-test` will exit with a non-zero exit value.
In case of test failures, `make test` will exit with a non-zero exit value.
All tests have their result stored in `build/$BUILD/test-results/$TEST_ID`,
where TEST_ID is a path-safe conversion from the fully qualified test

View File

@ -61,7 +61,7 @@ MODULES_SOURCE_PATH := $(call PathList, $(call GetModuleSrcPath) \
$(SUPPORT_OUTPUTDIR)/rmic/* $(TOPDIR)/src/*/share/doc/stub)
# URLs
JAVADOC_BASE_URL := https://www.oracle.com/pls/topic/lookup?ctx=javase$(VERSION_NUMBER)&amp;id=homepage
JAVADOC_BASE_URL := https://docs.oracle.com/pls/topic/lookup?ctx=javase$(VERSION_NUMBER)&amp;id=homepage
BUG_SUBMIT_URL := https://bugreport.java.com/bugreport/
COPYRIGHT_URL := {@docroot}/../legal/copyright.html
LICENSE_URL := https://www.oracle.com/technetwork/java/javase/terms/license/java$(VERSION_NUMBER)speclicense.html

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2012, 2018, 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
@ -55,10 +55,10 @@ help:
$(info $(_) make install # Install the generated images locally)
$(info $(_) make reconfigure # Rerun configure with the same arguments as last time)
$(info $(_) make help # Give some help on using make)
$(info $(_) make test # Run tests, default is all tests (see TEST below))
$(info $(_) make run-test-<test> # Run test, e.g. run-test-tier1)
$(info $(_) make run-test TEST=<t> # Run test(s) given by TEST specification)
$(info $(_) make exploded-run-test TEST=<t> # Run test(s) on the exploded image instead of)
$(info $(_) make check # Run basic testing (currently tier1))
$(info $(_) make test-<test> # Run test, e.g. test-tier1)
$(info $(_) make test TEST=<t> # Run test(s) given by TEST specification)
$(info $(_) make exploded-test TEST=<t> # Run test(s) on the exploded image instead of)
$(info $(_) # the full jdk image)
$(info )
$(info Targets for cleaning)
@ -99,10 +99,12 @@ help:
$(info $(_) TEST_JOBS=<n> # Run <n> parallel test jobs)
$(info $(_) CONF_CHECK=<method> # What to do if spec file is out of date)
$(info $(_) # method is 'auto', 'ignore' or 'fail' (default))
$(info $(_) make test TEST=<test> # Only run the given test or tests, e.g.)
$(info $(_) # make test TEST="jdk_lang jdk_net")
$(info $(_) JTREG="OPT1=x;OPT2=y" # Control the JTREG test harness for run-test)
$(info $(_) GTEST="OPT1=x;OPT2=y" # Control the GTEST test harness for run-test)
$(info $(_) TEST="test1 ..." # Use the given test descriptor(s) for testing, e.g.)
$(info $(_) # make test TEST="jdk_lang gtest:all")
$(info $(_) JTREG="OPT1=x;OPT2=y" # Control the JTREG test harness)
$(info $(_) GTEST="OPT1=x;OPT2=y" # Control the GTEST test harness)
$(info $(_) TEST_OPTS="OPT1=x;..." # Generic control of all test harnesses)
$(info $(_) TEST_VM_OPTS="ARG ..." # Same as setting TEST_OPTS to VM_OPTIONS="ARG ...")
$(info )
$(if $(all_confs), $(info Available configurations in $(build_dir):) $(foreach var,$(all_confs),$(info * $(var))),\
$(info No configurations were found in $(build_dir).) $(info Run 'bash configure' to create a configuration.))

View File

@ -473,10 +473,10 @@ ALL_TARGETS += $(INTERIM_JMOD_TARGETS) interim-image generate-link-opt-data
#
define DeclareRunTestRecipe
run-test-$1:
test-$1:
+($(CD) $(TOPDIR)/make && $(MAKE) $(MAKE_ARGS) -f RunTests.gmk run-test TEST="$1")
exploded-run-test-$1:
exploded-test-$1:
+($(CD) $(TOPDIR)/make && $(MAKE) $(MAKE_ARGS) -f RunTests.gmk run-test \
TEST="$1" JDK_IMAGE_DIR=$(JDK_OUTPUTDIR))
@ -484,8 +484,8 @@ endef
# ALL_NAMED_TESTS is defined in FindTests.gmk
$(foreach t, $(ALL_NAMED_TESTS), $(eval $(call DeclareRunTestRecipe,$t)))
ALL_TEST_TARGETS := $(addprefix run-test-, $(ALL_NAMED_TESTS))
ALL_EXPLODED_TEST_TARGETS := $(addprefix exploded-run-test-, $(ALL_NAMED_TESTS))
ALL_TEST_TARGETS := $(addprefix test-, $(ALL_NAMED_TESTS))
ALL_EXPLODED_TEST_TARGETS := $(addprefix exploded-test-, $(ALL_NAMED_TESTS))
ALL_TARGETS += $(ALL_TEST_TARGETS) $(ALL_EXPLODED_TEST_TARGETS)
@ -520,13 +520,6 @@ test-image-hotspot-jtreg-graal:
+($(CD) $(TOPDIR)/make/test && $(MAKE) $(MAKE_ARGS) -f JtregGraalUnit.gmk \
test-image-hotspot-jtreg-graal)
run-test:
+($(CD) $(TOPDIR)/make && $(MAKE) $(MAKE_ARGS) -f RunTests.gmk run-test TEST="$(TEST)")
exploded-run-test:
+($(CD) $(TOPDIR)/make && $(MAKE) $(MAKE_ARGS) -f RunTests.gmk run-test \
TEST="$(TEST)" JDK_IMAGE_DIR=$(JDK_OUTPUTDIR))
ifeq ($(BUILD_GTEST), true)
test-image-hotspot-gtest:
+($(CD) $(TOPDIR)/make/hotspot/test && $(MAKE) $(MAKE_ARGS) -f GtestImage.gmk)
@ -541,11 +534,6 @@ ifeq ($(BUILD_FAILURE_HANDLER), true)
+($(CD) $(TOPDIR)/make/test && $(MAKE) $(MAKE_ARGS) \
-f BuildFailureHandler.gmk build)
# Runs the tests for the failure handler jtreg extension
test-failure-handler:
+($(CD) $(TOPDIR)/make/test && $(MAKE) $(MAKE_ARGS) \
-f BuildFailureHandler.gmk test)
# Copies the failure handler jtreg extension into the test image
test-image-failure-handler:
+($(CD) $(TOPDIR)/make/test && $(MAKE) $(MAKE_ARGS) \
@ -556,40 +544,19 @@ ALL_TARGETS += prepare-test-image build-test-hotspot-jtreg-native \
test-image-hotspot-jtreg-native build-test-jdk-jtreg-native \
test-image-jdk-jtreg-native build-test-lib build-test-failure-handler \
test-failure-handler test-image-failure-handler test-image-hotspot-gtest \
test-image-hotspot-jtreg-graal build-test-hotspot-jtreg-graal \
run-test exploded-run-test
test-image-hotspot-jtreg-graal build-test-hotspot-jtreg-graal
################################################################################
# Run tests
# Run tests specified by $(TEST), or the default test set.
test:
$(call RunTests, $(TEST), $(JDK_IMAGE_DIR))
+($(CD) $(TOPDIR)/make && $(MAKE) $(MAKE_ARGS) -f RunTests.gmk run-test TEST="$(TEST)")
test-hotspot-jtreg:
$(call RunTests, "hotspot_all", $(JDK_IMAGE_DIR))
exploded-test:
+($(CD) $(TOPDIR)/make && $(MAKE) $(MAKE_ARGS) -f RunTests.gmk run-test \
TEST="$(TEST)" JDK_IMAGE_DIR=$(JDK_OUTPUTDIR))
test-hotspot-jtreg-native:
$(call RunTests, "hotspot_native_sanity", $(JDK_IMAGE_DIR))
test-hotspot-internal:
$(call RunTests, "hotspot_internal", $(JDK_OUTPUTDIR))
test-hotspot-gtest:
$(call RunTests, "hotspot_gtest", $(JDK_OUTPUTDIR))
test-jdk-jtreg-native:
$(call RunTests, "jdk_native_sanity", $(JDK_IMAGE_DIR))
test-make:
($(CD) $(TOPDIR)/test/make && $(MAKE) $(MAKE_ARGS) -f TestMake.gmk $(TEST_TARGET))
test-compile-commands:
($(CD) $(TOPDIR)/test/make && $(MAKE) $(MAKE_ARGS) -f TestMake.gmk test-compile-commands)
ALL_TARGETS += test test-hotspot-jtreg test-hotspot-jtreg-native \
test-hotspot-internal test-hotspot-gtest test-jdk-jtreg-native test-make \
test-compile-commands
ALL_TARGETS += test exploded-test
################################################################################
# Bundles
@ -874,14 +841,18 @@ else
docs-zip: docs-jdk
# Tests
test: jdk-image test-image
run-test: jdk-image test-image
exploded-run-test: exploded-image test-image
exploded-test: exploded-image test-image
test-make: clean-test-make compile-commands
test-make-compile-commands: compile-commands
# Declare dependency for all generated test targets
$(foreach t, $(ALL_TEST_TARGETS), $(eval $t: jdk-image test-image))
$(foreach t, $(ALL_EXPLODED_TEST_TARGETS), $(eval $t: exploded-image test-image))
$(foreach t, $(filter-out test-make%, $(ALL_TEST_TARGETS)), $(eval $t: jdk-image test-image))
$(foreach t, $(filter-out exploded-test-make%, $(ALL_EXPLODED_TEST_TARGETS)), $(eval $t: exploded-image test-image))
create-buildjdk-copy: jdk.jlink-java java.base-gendata \
$(addsuffix -java, $(INTERIM_IMAGE_MODULES))
@ -890,16 +861,10 @@ else
interim-image: $(INTERIM_JMOD_TARGETS)
test-make: clean-test-make
test-compile-commands: compile-commands
build-test-lib: exploded-image-optimize
build-test-failure-handler: interim-langtools
test-failure-handler: build-test-failure-handler
test-image-failure-handler: build-test-failure-handler
build-test-hotspot-jtreg-native: buildtools-jdk \
@ -917,12 +882,6 @@ else
test-image-hotspot-gtest: hotspot
test-hotspot-internal: exploded-image
test-hotspot-jtreg: jdk-image test-image
test-hotspot-gtest: exploded-image test-image-hotspot-gtest
install: product-images
product-bundles: product-images
@ -1091,6 +1050,30 @@ all: all-images
ALL_TARGETS += default jdk images docs bundles all
# Aliases used for running tests.
# Let "run-test" be an alias for "test"
$(foreach t, $(ALL_NAMED_TESTS), $(eval run-test-$t: test-$t))
$(foreach t, $(ALL_NAMED_TESTS), $(eval exploded-run-test-$t: exploded-test-$t))
RUN_TEST_TARGETS := $(addprefix run-test-, $(ALL_NAMED_TESTS)) \
$(addprefix exploded-run-test-, $(ALL_NAMED_TESTS))
run-test: test
exploded-run-test: exploded-test
# "make check" is a common idiom for running basic testing
check: test-tier1
# Keep some old names as aliases
test-hotspot-jtreg: test-hotspot_all
test-hotspot-jtreg-native: test-hotspot_native_sanity
test-hotspot-gtest: exploded-test-gtest
test-jdk-jtreg-native: test-jdk_native_sanity
ALL_TARGETS += $(RUN_TEST_TARGETS) run-test exploded-run-test check \
test-hotspot-jtreg test-hotspot-jtreg-native test-hotspot-gtest \
test-jdk-jtreg-native
################################################################################
################################################################################
#

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2011, 2018, 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
@ -30,17 +30,6 @@
ifndef _MAINSUPPORT_GMK
_MAINSUPPORT_GMK := 1
# Run the tests specified by $1, with PRODUCT_HOME specified by $2
# JT_JAVA is picked up by the jtreg launcher and used to run Jtreg itself.
define RunTests
($(CD) $(TOPDIR)/test && $(MAKE) $(MAKE_ARGS) -j1 -k MAKEFLAGS= \
JT_HOME=$(JT_HOME) PRODUCT_HOME=$(strip $2) \
TEST_IMAGE_DIR=$(TEST_IMAGE_DIR) \
ALT_OUTPUTDIR=$(OUTPUTDIR) TEST_JOBS=$(TEST_JOBS) \
JT_JAVA=$(BOOT_JDK) JIB_JAR=$(JIB_JAR) \
JOBS=$(JOBS) $1) || true
endef
define CleanDocs
@$(PRINTF) "Cleaning docs ..."
@$(PRINTF) "\n" $(LOG_DEBUG)

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2016, 2018, 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
@ -300,16 +300,32 @@ endef
# Helper function to determine if a test specification is a special test
#
# It is a special test if it is "special:" followed by a test name.
# It is a special test if it is "special:" followed by a test name,
# if it is "make:" or "make-" followed by a make test, or any of the special test names
# as a single word.
define ParseSpecialTestSelection
$(if $(filter special:%, $1), \
$1 \
) \
$(if $(filter make%, $1), \
$(if $(filter make:%, $1), \
special:$(strip $1) \
) \
$(if $(filter make-%, $1), \
special:$(patsubst make-%,make:%, $1) \
) \
$(if $(filter make, $1), \
special:make:all \
)
) \
$(if $(filter hotspot-internal failure-handler, $1), \
special:$(strip $1) \
)
endef
ifeq ($(TEST), )
$(info No test selection given in TEST!)
$(info Please use e.g. 'run-test TEST=tier1' or 'run-test-tier1')
$(info Please use e.g. 'make test TEST=tier1' or 'make test-tier1')
$(info See doc/testing.[md|html] for help)
$(error Cannot continue)
endif
@ -663,9 +679,13 @@ define SetupRunSpecialTestBody
$$(JDK_IMAGE_DIR)/bin/java -XX:+ExecuteInternalVMTests \
-XX:+ShowMessageBoxOnError -version
else ifeq ($$($1_TEST_NAME), failure-handler)
$1_TEST_COMMAND_LINE := \
($(CD) $(TOPDIR)/make/test && $(MAKE) $(MAKE_ARGS) -f \
BuildFailureHandler.gmk test)
ifeq ($(BUILD_FAILURE_HANDLER), true)
$1_TEST_COMMAND_LINE := \
($(CD) $(TOPDIR)/make/test && $(MAKE) $(MAKE_ARGS) -f \
BuildFailureHandler.gmk test)
else
$$(error Cannot test failure handler if it is not built)
endif
else ifeq ($$($1_TEST_NAME), make)
$1_TEST_COMMAND_LINE := \
($(CD) $(TOPDIR)/test/make && $(MAKE) $(MAKE_ARGS) -f \

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2017, 2018, 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
@ -72,6 +72,15 @@ ALL_NAMED_TESTS += $(JTREG_TEST_GROUPS)
# Add Gtest
ALL_NAMED_TESTS += gtest
# Find make test targets
MAKE_TEST_TARGETS := $(shell $(MAKE) -s --no-print-directory $(MAKE_ARGS) \
SPEC=$(SPEC) -f $(TOPDIR)/test/make/TestMake.gmk print-targets)
ALL_NAMED_TESTS += $(addprefix make-, $(MAKE_TEST_TARGETS))
# Add special tests
ALL_NAMED_TESTS += hotspot-internal failure-handler make
################################################################################
endif # _FIND_TESTS_GMK

View File

@ -48,7 +48,7 @@ import static com.sun.source.doctree.DocTree.Kind.*;
* will produce the following html
* <p>
* {@code
* Please see <a href="https://www.oracle.com/pls/topic/lookup?ctx=javase10&id=Borealis">a spectacular</a> sight.
* Please see <a href="https://docs.oracle.com/pls/topic/lookup?ctx=javase10&id=Borealis">a spectacular</a> sight.
* }
*/
public class ExtLink implements Taglet {
@ -63,7 +63,7 @@ public class ExtLink implements Taglet {
static final String TAG_NAME = "extLink";
static final String URL = "https://www.oracle.com/pls/topic/lookup?ctx=javase" +
static final String URL = "https://docs.oracle.com/pls/topic/lookup?ctx=javase" +
SPEC_VERSION + "&amp;id=";
static final Pattern TAG_PATTERN = Pattern.compile("(?s)(\\s*)(?<name>\\w+)(\\s+)(?<desc>.*)$");

View File

@ -47,13 +47,8 @@ void AOTLoader::load_for_klass(InstanceKlass* ik, Thread* thread) {
return;
}
if (UseAOT) {
if (JvmtiExport::can_hotswap_or_post_breakpoint()) {
if (PrintAOT) {
warning("JVMTI capability to hotswap and post breakpoint is not compatible with AOT (switching AOT off)");
}
FLAG_SET_DEFAULT(UseAOT, false);
return;
}
// We allow hotswap to be enabled after the onload phase, but not breakpoints
assert(!JvmtiExport::can_post_breakpoint(), "AOT should have been disabled.");
FOR_ALL_AOT_HEAPS(heap) {
(*heap)->load_klass_data(ik, thread);
}
@ -120,9 +115,9 @@ void AOTLoader::initialize() {
return;
}
if (JvmtiExport::can_hotswap_or_post_breakpoint()) {
if (JvmtiExport::can_post_breakpoint()) {
if (PrintAOT) {
warning("JVMTI capability to hotswap and post breakpoint is not compatible with AOT (switching AOT off)");
warning("JVMTI capability to post breakpoint is not compatible with AOT (switching AOT off)");
}
FLAG_SET_DEFAULT(UseAOT, false);
return;

View File

@ -79,7 +79,6 @@ void Compiler::initialize() {
}
int Compiler::code_buffer_size() {
assert(SegmentedCodeCache, "Should be only used with a segmented code cache");
return Compilation::desired_max_code_buffer_size() + Compilation::desired_max_constant_size();
}
@ -90,10 +89,7 @@ BufferBlob* Compiler::init_buffer_blob() {
// setup CodeBuffer. Preallocate a BufferBlob of size
// NMethodSizeLimit plus some extra space for constants.
int code_buffer_size = Compilation::desired_max_code_buffer_size() +
Compilation::desired_max_constant_size();
BufferBlob* buffer_blob = BufferBlob::create("C1 temporary CodeBuffer", code_buffer_size);
BufferBlob* buffer_blob = BufferBlob::create("C1 temporary CodeBuffer", code_buffer_size());
if (buffer_blob != NULL) {
CompilerThread::current()->set_buffer_blob(buffer_blob);
}

View File

@ -287,54 +287,50 @@ class ClassLoaderDataGraphIterator : public StackObj {
Handle _holder;
Thread* _thread;
void hold_next() {
if (_next != NULL) {
_holder = Handle(_thread, _next->holder_phantom());
}
}
public:
ClassLoaderDataGraphIterator() : _next(ClassLoaderDataGraph::_head) {
_thread = Thread::current();
assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
hold_next();
}
bool repeat() const {
return _next != NULL;
}
ClassLoaderData* get_next() {
ClassLoaderData* next = _next;
if (_next != NULL) {
_next = _next->next();
hold_next();
ClassLoaderData* cld = _next;
// Skip already unloaded CLD for concurrent unloading.
while (cld != NULL && !cld->is_alive()) {
cld = cld->next();
}
return next;
if (cld != NULL) {
// Keep cld that is being returned alive.
_holder = Handle(_thread, cld->holder_phantom());
_next = cld->next();
} else {
_next = NULL;
}
return cld;
}
};
// These functions assume that the caller has locked the ClassLoaderDataGraph_lock
// if they are not calling the function from a safepoint.
void ClassLoaderDataGraph::classes_do(KlassClosure* klass_closure) {
ClassLoaderDataGraphIterator iter;
while (iter.repeat()) {
ClassLoaderData* cld = iter.get_next();
while (ClassLoaderData* cld = iter.get_next()) {
cld->classes_do(klass_closure);
}
}
void ClassLoaderDataGraph::classes_do(void f(Klass* const)) {
ClassLoaderDataGraphIterator iter;
while (iter.repeat()) {
ClassLoaderData* cld = iter.get_next();
while (ClassLoaderData* cld = iter.get_next()) {
cld->classes_do(f);
}
}
void ClassLoaderDataGraph::methods_do(void f(Method*)) {
ClassLoaderDataGraphIterator iter;
while (iter.repeat()) {
ClassLoaderData* cld = iter.get_next();
while (ClassLoaderData* cld = iter.get_next()) {
cld->methods_do(f);
}
}
@ -342,8 +338,7 @@ void ClassLoaderDataGraph::methods_do(void f(Method*)) {
void ClassLoaderDataGraph::modules_do(void f(ModuleEntry*)) {
assert_locked_or_safepoint(Module_lock);
ClassLoaderDataGraphIterator iter;
while (iter.repeat()) {
ClassLoaderData* cld = iter.get_next();
while (ClassLoaderData* cld = iter.get_next()) {
cld->modules_do(f);
}
}
@ -361,8 +356,7 @@ void ClassLoaderDataGraph::modules_unloading_do(void f(ModuleEntry*)) {
void ClassLoaderDataGraph::packages_do(void f(PackageEntry*)) {
assert_locked_or_safepoint(Module_lock);
ClassLoaderDataGraphIterator iter;
while (iter.repeat()) {
ClassLoaderData* cld = iter.get_next();
while (ClassLoaderData* cld = iter.get_next()) {
cld->packages_do(f);
}
}
@ -379,8 +373,7 @@ void ClassLoaderDataGraph::packages_unloading_do(void f(PackageEntry*)) {
void ClassLoaderDataGraph::loaded_classes_do(KlassClosure* klass_closure) {
ClassLoaderDataGraphIterator iter;
while (iter.repeat()) {
ClassLoaderData* cld = iter.get_next();
while (ClassLoaderData* cld = iter.get_next()) {
cld->loaded_classes_do(klass_closure);
}
}
@ -404,8 +397,7 @@ void ClassLoaderDataGraph::classes_unloading_do(void f(Klass* const)) {
}
#define FOR_ALL_DICTIONARY(X) ClassLoaderDataGraphIterator iter; \
ClassLoaderData* X; \
while ((X = iter.get_next()) != NULL) \
while (ClassLoaderData* X = iter.get_next()) \
if (X->dictionary() != NULL)
// Walk classes in the loaded class dictionaries in various forms.
@ -696,16 +688,14 @@ extern "C" int print_loader_data_graph() {
void ClassLoaderDataGraph::verify() {
ClassLoaderDataGraphIterator iter;
while (iter.repeat()) {
ClassLoaderData* cld = iter.get_next();
while (ClassLoaderData* cld = iter.get_next()) {
cld->verify();
}
}
void ClassLoaderDataGraph::print_on(outputStream * const out) {
ClassLoaderDataGraphIterator iter;
while (iter.repeat()) {
ClassLoaderData* cld = iter.get_next();
while (ClassLoaderData* cld = iter.get_next()) {
cld->print_on(out);
}
}

View File

@ -468,7 +468,7 @@ bool Dictionary::is_valid_protection_domain(unsigned int hash,
#if INCLUDE_CDS
static bool is_jfr_event_class(Klass *k) {
while (k) {
if (k->name()->equals("jdk/jfr/Event")) {
if (k->name()->equals("jdk/internal/event/Event")) {
return true;
}
k = k->super();

View File

@ -50,6 +50,7 @@
#include "oops/oop.inline.hpp"
#include "oops/symbol.hpp"
#include "oops/typeArrayOop.inline.hpp"
#include "prims/jvmtiExport.hpp"
#include "prims/resolvedMethodTable.hpp"
#include "runtime/fieldDescriptor.inline.hpp"
#include "runtime/frame.inline.hpp"
@ -125,7 +126,7 @@ static void compute_offset(int &dest_offset,
if (ik == NULL) {
ResourceMark rm;
log_error(class)("Mismatch JDK version for field: %s type: %s", name_symbol->as_C_string(), signature_symbol->as_C_string());
vm_exit_during_initialization("Invalid layout of preloaded class");
vm_exit_during_initialization("Invalid layout of well-known class");
}
if (!ik->find_local_field(name_symbol, signature_symbol, &fd) || fd.is_static() != is_static) {
@ -138,7 +139,7 @@ static void compute_offset(int &dest_offset,
LogStream ls(lt.error());
ik->print_on(&ls);
#endif //PRODUCT
vm_exit_during_initialization("Invalid layout of preloaded class: use -Xlog:class+load=info to see the origin of the problem class");
vm_exit_during_initialization("Invalid layout of well-known class: use -Xlog:class+load=info to see the origin of the problem class");
}
dest_offset = fd.offset();
}
@ -151,7 +152,7 @@ static void compute_offset(int& dest_offset, InstanceKlass* ik,
if (name == NULL) {
ResourceMark rm;
log_error(class)("Name %s should be in the SymbolTable since its class is loaded", name_string);
vm_exit_during_initialization("Invalid layout of preloaded class", ik->external_name());
vm_exit_during_initialization("Invalid layout of well-known class", ik->external_name());
}
compute_offset(dest_offset, ik, name, signature_symbol, is_static);
}
@ -1196,7 +1197,7 @@ bool java_lang_Class::restore_archived_mirror(Klass *k,
Handle class_loader, Handle module,
Handle protection_domain, TRAPS) {
// Postpone restoring archived mirror until java.lang.Class is loaded. Please
// see more details in SystemDictionary::resolve_preloaded_classes().
// see more details in SystemDictionary::resolve_well_known_classes().
if (!SystemDictionary::Class_klass_loaded()) {
assert(fixup_mirror_list() != NULL, "fixup_mirror_list not initialized");
fixup_mirror_list()->push(k);
@ -4250,12 +4251,19 @@ void JavaClasses::compute_hard_coded_offsets() {
// Compute non-hard-coded field offsets of all the classes in this file
void JavaClasses::compute_offsets() {
if (UseSharedSpaces) {
return; // field offsets are loaded from archive
assert(JvmtiExport::is_early_phase() && !(JvmtiExport::should_post_class_file_load_hook() &&
JvmtiExport::has_early_class_hook_env()),
"JavaClasses::compute_offsets() must be called in early JVMTI phase.");
// None of the classes used by the rest of this function can be replaced by
// JMVTI ClassFileLoadHook.
// We are safe to use the archived offsets, which have already been restored
// by JavaClasses::serialize_offsets, without computing the offsets again.
return;
}
// We have already called the compute_offsets() of the
// BASIC_JAVA_CLASSES_DO_PART1 classes (java_lang_String and java_lang_Class)
// earlier inside SystemDictionary::resolve_preloaded_classes()
// earlier inside SystemDictionary::resolve_well_known_classes()
BASIC_JAVA_CLASSES_DO_PART2(DO_COMPUTE_OFFSETS);
// generated interpreter code wants to know about the offsets we just computed:
@ -4356,7 +4364,7 @@ int InjectedField::compute_offset() {
tty->print_cr(" name: %s, sig: %s, flags: %08x", fs.name()->as_C_string(), fs.signature()->as_C_string(), fs.access_flags().as_int());
}
#endif //PRODUCT
vm_exit_during_initialization("Invalid layout of preloaded class: use -Xlog:class+load=info to see the origin of the problem class");
vm_exit_during_initialization("Invalid layout of well-known class: use -Xlog:class+load=info to see the origin of the problem class");
return -1;
}

View File

@ -67,7 +67,7 @@
#include "oops/oop.inline.hpp"
#include "oops/symbol.hpp"
#include "oops/typeArrayKlass.hpp"
#include "prims/jvmtiEnvBase.hpp"
#include "prims/jvmtiExport.hpp"
#include "prims/resolvedMethodTable.hpp"
#include "prims/methodHandles.hpp"
#include "runtime/arguments.hpp"
@ -1189,20 +1189,15 @@ InstanceKlass* SystemDictionary::find_shared_class(Symbol* class_name) {
}
// Load a class from the shared spaces (found through the shared system
// dictionary). Force the superclass and all interfaces to be loaded.
// Update the class definition to include sibling classes and no
// subclasses (yet). [Classes in the shared space are not part of the
// object hierarchy until loaded.]
InstanceKlass* SystemDictionary::load_shared_class(
Symbol* class_name, Handle class_loader, TRAPS) {
// Load a class for boot loader from the shared spaces (found through
// the shared system dictionary). Force the super class and all interfaces
// to be loaded.
InstanceKlass* SystemDictionary::load_shared_boot_class(Symbol* class_name,
TRAPS) {
InstanceKlass* ik = find_shared_class(class_name);
// Make sure we only return the boot class for the NULL classloader.
if (ik != NULL &&
ik->is_shared_boot_class() && class_loader.is_null()) {
Handle protection_domain;
return load_shared_class(ik, class_loader, protection_domain, THREAD);
// Make sure we only return the boot class.
if (ik != NULL && ik->is_shared_boot_class()) {
return load_shared_class(ik, Handle(), Handle(), THREAD);
}
return NULL;
}
@ -1489,13 +1484,12 @@ InstanceKlass* SystemDictionary::load_instance_class(Symbol* class_name, Handle
!search_only_bootloader_append,
"Attempt to load a class outside of boot loader's module path");
// Search the shared system dictionary for classes preloaded into the
// shared spaces.
// Search for classes in the CDS archive.
InstanceKlass* k = NULL;
{
#if INCLUDE_CDS
PerfTraceTime vmtimer(ClassLoader::perf_shared_classload_time());
k = load_shared_class(class_name, class_loader, THREAD);
k = load_shared_boot_class(class_name, THREAD);
#endif
}
@ -1958,7 +1952,7 @@ void SystemDictionary::initialize(TRAPS) {
// Allocate private object used as system class loader lock
_system_loader_lock_obj = oopFactory::new_intArray(0, CHECK);
// Initialize basic classes
resolve_preloaded_classes(CHECK);
resolve_well_known_classes(CHECK);
}
// Compact table of directions on the initialization of klasses:
@ -1971,6 +1965,19 @@ static const short wk_init_info[] = {
0
};
#ifdef ASSERT
bool SystemDictionary::is_well_known_klass(Symbol* class_name) {
int sid;
for (int i = 0; (sid = wk_init_info[i]) != 0; i++) {
Symbol* symbol = vmSymbols::symbol_at((vmSymbols::SID)sid);
if (class_name == symbol) {
return true;
}
}
return false;
}
#endif
bool SystemDictionary::resolve_wk_klass(WKID id, TRAPS) {
assert(id >= (int)FIRST_WKID && id < (int)WKID_LIMIT, "oob");
int sid = wk_init_info[id - FIRST_WKID];
@ -2002,8 +2009,8 @@ void SystemDictionary::resolve_wk_klasses_until(WKID limit_id, WKID &start_id, T
start_id = limit_id;
}
void SystemDictionary::resolve_preloaded_classes(TRAPS) {
assert(WK_KLASS(Object_klass) == NULL, "preloaded classes should only be initialized once");
void SystemDictionary::resolve_well_known_classes(TRAPS) {
assert(WK_KLASS(Object_klass) == NULL, "well-known classes should only be initialized once");
// Create the ModuleEntry for java.base. This call needs to be done here,
// after vmSymbols::initialize() is called but before any classes are pre-loaded.
@ -2071,7 +2078,8 @@ void SystemDictionary::resolve_preloaded_classes(TRAPS) {
WKID jsr292_group_end = WK_KLASS_ENUM_NAME(VolatileCallSite_klass);
resolve_wk_klasses_until(jsr292_group_start, scan, CHECK);
resolve_wk_klasses_through(jsr292_group_end, scan, CHECK);
resolve_wk_klasses_until(NOT_JVMCI(WKID_LIMIT) JVMCI_ONLY(FIRST_JVMCI_WKID), scan, CHECK);
WKID last = NOT_JVMCI(WKID_LIMIT) JVMCI_ONLY(FIRST_JVMCI_WKID);
resolve_wk_klasses_until(last, scan, CHECK);
_box_klasses[T_BOOLEAN] = WK_KLASS(Boolean_klass);
_box_klasses[T_CHAR] = WK_KLASS(Character_klass);
@ -2088,6 +2096,17 @@ void SystemDictionary::resolve_preloaded_classes(TRAPS) {
Method* method = InstanceKlass::cast(ClassLoader_klass())->find_method(vmSymbols::checkPackageAccess_name(), vmSymbols::class_protectiondomain_signature());
_has_checkPackageAccess = (method != NULL);
}
#ifdef ASSERT
if (UseSharedSpaces) {
assert(JvmtiExport::is_early_phase(),
"All well known classes must be resolved in JVMTI early phase");
for (int i = FIRST_WKID; i < last; i++) {
InstanceKlass* k = _well_known_klasses[i];
assert(k->is_shared(), "must not be replaced by JVMTI class file load hook");
}
}
#endif
}
// Tells if a given klass is a box (wrapper class, such as java.lang.Integer).

View File

@ -85,19 +85,20 @@ class ProtectionDomainCacheEntry;
class GCTimer;
class OopStorage;
// Certain classes are preloaded, such as java.lang.Object and java.lang.String.
// They are all "well-known", in the sense that no class loader is allowed
// to provide a different definition.
//
// These klasses must all have names defined in vmSymbols.
#define WK_KLASS_ENUM_NAME(kname) kname##_knum
// Certain classes, such as java.lang.Object and java.lang.String,
// are "well-known", in the sense that no class loader is allowed
// to provide a different definition.
//
// Each well-known class has a short klass name (like object_klass),
// and a vmSymbol name (like java_lang_Object).
// The order of these definitions is significant; it is the order in which
// preloading is actually performed by resolve_preloaded_classes.
//
// The order of these definitions is significant: the classes are
// resolved during early VM start-up by resolve_well_known_classes
// in this order. Changing the order may require careful restructuring
// of the VM start-up sequence.
//
#define WK_KLASSES_DO(do_klass) \
/* well-known classes */ \
do_klass(Object_klass, java_lang_Object ) \
@ -127,7 +128,7 @@ class OopStorage;
do_klass(IllegalMonitorStateException_klass, java_lang_IllegalMonitorStateException ) \
do_klass(Reference_klass, java_lang_ref_Reference ) \
\
/* Preload ref klasses and set reference types */ \
/* ref klasses and set reference types */ \
do_klass(SoftReference_klass, java_lang_ref_SoftReference ) \
do_klass(WeakReference_klass, java_lang_ref_WeakReference ) \
do_klass(FinalReference_klass, java_lang_ref_FinalReference ) \
@ -200,7 +201,7 @@ class OopStorage;
/* support for stack dump lock analysis */ \
do_klass(java_util_concurrent_locks_AbstractOwnableSynchronizer_klass, java_util_concurrent_locks_AbstractOwnableSynchronizer) \
\
/* Preload boxing klasses */ \
/* boxing klasses */ \
do_klass(Boolean_klass, java_lang_Boolean ) \
do_klass(Character_klass, java_lang_Character ) \
do_klass(Float_klass, java_lang_Float ) \
@ -391,7 +392,8 @@ public:
// Initialization
static void initialize(TRAPS);
// Checked fast access to commonly used classes - mostly preloaded
// Checked fast access to the well-known classes -- so that you don't try to use them
// before they are resolved.
static InstanceKlass* check_klass(InstanceKlass* k) {
assert(k != NULL, "klass not loaded");
return k;
@ -435,6 +437,12 @@ public:
return check_klass(_box_klasses[t]);
}
static BasicType box_klass_type(Klass* k); // inverse of box_klass
#ifdef ASSERT
static bool is_well_known_klass(Klass* k) {
return is_well_known_klass(k->name());
}
static bool is_well_known_klass(Symbol* class_name);
#endif
protected:
// Returns the class loader data to be used when looking up/updating the
@ -643,6 +651,8 @@ protected:
Handle class_loader,
Handle protection_domain,
TRAPS);
static InstanceKlass* load_shared_boot_class(Symbol* class_name,
TRAPS);
static InstanceKlass* load_instance_class(Symbol* class_name, Handle class_loader, TRAPS);
static Handle compute_loader_lock_object(Handle class_loader, TRAPS);
static void check_loader_lock_contention(Handle loader_lock, TRAPS);
@ -650,9 +660,6 @@ protected:
static bool is_parallelDefine(Handle class_loader);
public:
static InstanceKlass* load_shared_class(Symbol* class_name,
Handle class_loader,
TRAPS);
static bool is_system_class_loader(oop class_loader);
static bool is_platform_class_loader(oop class_loader);
static void clear_invoke_method_table();
@ -695,8 +702,8 @@ protected:
ClassLoaderData* loader_data,
TRAPS);
// Resolve preloaded classes so they can be used like SystemDictionary::String_klass()
static void resolve_preloaded_classes(TRAPS);
// Resolve well-known classes so they can be used like SystemDictionary::String_klass()
static void resolve_well_known_classes(TRAPS);
// Class loader constraints
static void check_constraints(unsigned int hash,
@ -707,7 +714,6 @@ protected:
InstanceKlass* k, Handle loader,
TRAPS);
// Variables holding commonly used klasses (preloaded)
static InstanceKlass* _well_known_klasses[];
// table of box klasses (int_klass, etc.)

View File

@ -274,10 +274,10 @@ void CodeCache::initialize_heaps() {
}
// Make sure we have enough space for VM internal code
uint min_code_cache_size = CodeCacheMinimumUseSpace DEBUG_ONLY(* 3);
if (non_nmethod_size < (min_code_cache_size + code_buffers_size)) {
if (non_nmethod_size < min_code_cache_size) {
vm_exit_during_initialization(err_msg(
"Not enough space in non-nmethod code heap to run VM: " SIZE_FORMAT "K < " SIZE_FORMAT "K",
non_nmethod_size/K, (min_code_cache_size + code_buffers_size)/K));
non_nmethod_size/K, min_code_cache_size/K));
}
// Verify sizes and update flag values

View File

@ -894,14 +894,19 @@ void CompileBroker::possibly_add_compiler_threads() {
EXCEPTION_MARK;
julong available_memory = os::available_memory();
// If SegmentedCodeCache is off, both values refer to the single heap (with type CodeBlobType::All).
size_t available_cc_np = CodeCache::unallocated_capacity(CodeBlobType::MethodNonProfiled),
available_cc_p = CodeCache::unallocated_capacity(CodeBlobType::MethodProfiled);
// Only do attempt to start additional threads if the lock is free.
if (!CompileThread_lock->try_lock()) return;
if (_c2_compile_queue != NULL) {
int old_c2_count = _compilers[1]->num_compiler_threads();
int new_c2_count = MIN3(_c2_count,
int new_c2_count = MIN4(_c2_count,
_c2_compile_queue->size() / 2,
(int)(available_memory / 200*M));
(int)(available_memory / (200*M)),
(int)(available_cc_np / (128*K)));
for (int i = old_c2_count; i < new_c2_count; i++) {
JavaThread *ct = make_thread(compiler2_object(i), _c2_compile_queue, _compilers[1], true, CHECK);
@ -910,17 +915,18 @@ void CompileBroker::possibly_add_compiler_threads() {
if (TraceCompilerThreads) {
ResourceMark rm;
MutexLocker mu(Threads_lock);
tty->print_cr("Added compiler thread %s (available memory: %dMB)",
ct->get_thread_name(), (int)(available_memory/M));
tty->print_cr("Added compiler thread %s (available memory: %dMB, available non-profiled code cache: %dMB)",
ct->get_thread_name(), (int)(available_memory/M), (int)(available_cc_np/M));
}
}
}
if (_c1_compile_queue != NULL) {
int old_c1_count = _compilers[0]->num_compiler_threads();
int new_c1_count = MIN3(_c1_count,
int new_c1_count = MIN4(_c1_count,
_c1_compile_queue->size() / 4,
(int)(available_memory / 100*M));
(int)(available_memory / (100*M)),
(int)(available_cc_p / (128*K)));
for (int i = old_c1_count; i < new_c1_count; i++) {
JavaThread *ct = make_thread(compiler1_object(i), _c1_compile_queue, _compilers[0], true, CHECK);
@ -929,8 +935,8 @@ void CompileBroker::possibly_add_compiler_threads() {
if (TraceCompilerThreads) {
ResourceMark rm;
MutexLocker mu(Threads_lock);
tty->print_cr("Added compiler thread %s (available memory: %dMB)",
ct->get_thread_name(), (int)(available_memory/M));
tty->print_cr("Added compiler thread %s (available memory: %dMB, available profiled code cache: %dMB)",
ct->get_thread_name(), (int)(available_memory/M), (int)(available_cc_p/M));
}
}
}
@ -1501,8 +1507,9 @@ bool CompileBroker::wait_for_jvmci_completion(JVMCICompiler* jvmci, CompileTask*
bool progress;
if (jvmci_compiler_thread != NULL) {
// If the JVMCI compiler thread is not blocked, we deem it to be making progress.
progress = jvmci_compiler_thread->thread_state() != _thread_blocked;
// If the JVMCI compiler thread is not blocked or suspended, we deem it to be making progress.
progress = jvmci_compiler_thread->thread_state() != _thread_blocked &&
!jvmci_compiler_thread->is_external_suspend();
} else {
// Still waiting on JVMCI compiler queue. This thread may be holding a lock
// that all JVMCI compiler threads are blocked on. We use the counter for

View File

@ -1043,7 +1043,7 @@ void G1CollectedHeap::prepare_heap_for_mutators() {
assert(num_free_regions() == 0, "we should not have added any free regions");
rebuild_region_sets(false /* free_list_only */);
abort_refinement();
resize_if_necessary_after_full_collection();
resize_heap_if_necessary();
// Rebuild the strong code root lists for each region
rebuild_strong_code_roots();
@ -1149,7 +1149,7 @@ void G1CollectedHeap::do_full_collection(bool clear_all_soft_refs) {
clear_all_soft_refs);
}
void G1CollectedHeap::resize_if_necessary_after_full_collection() {
void G1CollectedHeap::resize_heap_if_necessary() {
// Capacity, free and used after the GC counted as full regions to
// include the waste in the following calculations.
const size_t capacity_after_gc = capacity();
@ -1206,7 +1206,7 @@ void G1CollectedHeap::resize_if_necessary_after_full_collection() {
// Don't expand unless it's significant
size_t expand_bytes = minimum_desired_capacity - capacity_after_gc;
log_debug(gc, ergo, heap)("Attempt heap expansion (capacity lower than min desired capacity after Full GC). "
log_debug(gc, ergo, heap)("Attempt heap expansion (capacity lower than min desired capacity). "
"Capacity: " SIZE_FORMAT "B occupancy: " SIZE_FORMAT "B live: " SIZE_FORMAT "B "
"min_desired_capacity: " SIZE_FORMAT "B (" UINTX_FORMAT " %%)",
capacity_after_gc, used_after_gc, used(), minimum_desired_capacity, MinHeapFreeRatio);
@ -1218,7 +1218,7 @@ void G1CollectedHeap::resize_if_necessary_after_full_collection() {
// Capacity too large, compute shrinking size
size_t shrink_bytes = capacity_after_gc - maximum_desired_capacity;
log_debug(gc, ergo, heap)("Attempt heap shrinking (capacity higher than max desired capacity after Full GC). "
log_debug(gc, ergo, heap)("Attempt heap shrinking (capacity higher than max desired capacity). "
"Capacity: " SIZE_FORMAT "B occupancy: " SIZE_FORMAT "B live: " SIZE_FORMAT "B "
"maximum_desired_capacity: " SIZE_FORMAT "B (" UINTX_FORMAT " %%)",
capacity_after_gc, used_after_gc, used(), maximum_desired_capacity, MaxHeapFreeRatio);
@ -1394,8 +1394,8 @@ void G1CollectedHeap::shrink_helper(size_t shrink_bytes) {
void G1CollectedHeap::shrink(size_t shrink_bytes) {
_verifier->verify_region_sets_optional();
// We should only reach here at the end of a Full GC which means we
// should not not be holding to any GC alloc regions. The method
// We should only reach here at the end of a Full GC or during Remark which
// means we should not not be holding to any GC alloc regions. The method
// below will make sure of that and do any remaining clean up.
_allocator->abandon_gc_alloc_regions();
@ -4399,13 +4399,13 @@ public:
}
bool do_heap_region(HeapRegion* r) {
// After full GC, no region should have a remembered set.
r->rem_set()->clear(true);
if (r->is_empty()) {
assert(r->rem_set()->is_empty(), "Empty regions should have empty remembered sets.");
// Add free regions to the free list
r->set_free();
_hrm->insert_into_free_list(r);
} else if (!_free_list_only) {
assert(r->rem_set()->is_empty(), "At this point remembered sets must have been cleared.");
if (r->is_archive() || r->is_humongous()) {
// We ignore archive and humongous regions. We left these sets unchanged.
@ -4443,10 +4443,9 @@ void G1CollectedHeap::rebuild_region_sets(bool free_list_only) {
_archive_allocator->clear_used();
}
}
assert(used_unlocked() == recalculate_used(),
"inconsistent used_unlocked(), "
"value: " SIZE_FORMAT " recalculated: " SIZE_FORMAT,
used_unlocked(), recalculate_used());
assert(used() == recalculate_used(),
"inconsistent used(), value: " SIZE_FORMAT " recalculated: " SIZE_FORMAT,
used(), recalculate_used());
}
bool G1CollectedHeap::is_in_closed_subset(const void* p) const {

View File

@ -468,9 +468,6 @@ private:
// Callback from VM_G1CollectFull operation, or collect_as_vm_thread.
virtual void do_full_collection(bool clear_all_soft_refs);
// Resize the heap if necessary after a full collection.
void resize_if_necessary_after_full_collection();
// Callback from VM_G1CollectForAllocation operation.
// This function does everything necessary/possible to satisfy a
// failed allocation request (including collection, expansion, etc.)
@ -528,6 +525,8 @@ public:
return _g1mm;
}
void resize_heap_if_necessary();
// Expand the garbage-first heap by at least the given size (in bytes!).
// Returns true if the heap was expanded by the requested amount;
// false otherwise.
@ -1120,6 +1119,7 @@ public:
// Return the region with the given index. It assumes the index is valid.
inline HeapRegion* region_at(uint index) const;
inline HeapRegion* region_at_or_null(uint index) const;
// Return the next region (by index) that is part of the same
// humongous object that hr is part of.
@ -1157,6 +1157,11 @@ public:
template <class T>
inline HeapRegion* heap_region_containing(const T addr) const;
// Returns the HeapRegion that contains addr, or NULL if that is an uncommitted
// region. addr must not be NULL.
template <class T>
inline HeapRegion* heap_region_containing_or_null(const T addr) const;
// A CollectedHeap is divided into a dense sequence of "blocks"; that is,
// each address in the (reserved) heap is a member of exactly
// one block. The defining characteristic of a block is that it is

View File

@ -60,6 +60,9 @@ size_t G1CollectedHeap::desired_plab_sz(InCSetState dest) {
// Return the region with the given index. It assumes the index is valid.
inline HeapRegion* G1CollectedHeap::region_at(uint index) const { return _hrm.at(index); }
// Return the region with the given index, or NULL if unmapped. It assumes the index is valid.
inline HeapRegion* G1CollectedHeap::region_at_or_null(uint index) const { return _hrm.at_or_null(index); }
inline HeapRegion* G1CollectedHeap::next_region_in_humongous(HeapRegion* hr) const {
return _hrm.next_region_in_humongous(hr);
}
@ -84,6 +87,16 @@ inline HeapRegion* G1CollectedHeap::heap_region_containing(const T addr) const {
return _hrm.addr_to_region((HeapWord*) addr);
}
template <class T>
inline HeapRegion* G1CollectedHeap::heap_region_containing_or_null(const T addr) const {
assert(addr != NULL, "invariant");
assert(is_in_g1_reserved((const void*) addr),
"Address " PTR_FORMAT " is outside of the heap ranging from [" PTR_FORMAT " to " PTR_FORMAT ")",
p2i((void*)addr), p2i(g1_reserved().start()), p2i(g1_reserved().end()));
uint const region_idx = addr_to_region(addr);
return region_at_or_null(region_idx);
}
inline void G1CollectedHeap::old_set_add(HeapRegion* hr) {
_old_set.add(hr);
}

View File

@ -1178,6 +1178,8 @@ void G1ConcurrentMark::remark() {
ClassLoaderDataGraph::purge();
}
_g1h->resize_heap_if_necessary();
compute_new_sizes();
verify_during_pause(G1HeapVerifier::G1VerifyRemark, VerifyOption_G1UsePrevMarking, "Remark after");

View File

@ -38,7 +38,7 @@ G1ParCopyHelper::G1ParCopyHelper(G1CollectedHeap* g1h, G1ParScanThreadState* pa
{ }
G1ScanClosureBase::G1ScanClosureBase(G1CollectedHeap* g1h, G1ParScanThreadState* par_scan_state) :
_g1h(g1h), _par_scan_state(par_scan_state), _from(NULL)
_g1h(g1h), _par_scan_state(par_scan_state)
{ }
void G1CLDScanClosure::do_cld(ClassLoaderData* cld) {

View File

@ -36,6 +36,7 @@ class G1ConcurrentMark;
class DirtyCardToOopClosure;
class G1CMBitMap;
class G1ParScanThreadState;
class G1ScanEvacuatedObjClosure;
class G1CMTask;
class ReferenceProcessor;
@ -43,7 +44,6 @@ class G1ScanClosureBase : public BasicOopIterateClosure {
protected:
G1CollectedHeap* _g1h;
G1ParScanThreadState* _par_scan_state;
HeapRegion* _from;
G1ScanClosureBase(G1CollectedHeap* g1h, G1ParScanThreadState* par_scan_state);
~G1ScanClosureBase() { }
@ -56,24 +56,19 @@ protected:
public:
virtual ReferenceIterationMode reference_iteration_mode() { return DO_FIELDS; }
void set_region(HeapRegion* from) { _from = from; }
inline void trim_queue_partially();
};
// Used during the Update RS phase to refine remaining cards in the DCQ during garbage collection.
class G1ScanObjsDuringUpdateRSClosure: public G1ScanClosureBase {
uint _worker_i;
class G1ScanObjsDuringUpdateRSClosure : public G1ScanClosureBase {
public:
G1ScanObjsDuringUpdateRSClosure(G1CollectedHeap* g1h,
G1ParScanThreadState* pss,
uint worker_i) :
G1ScanClosureBase(g1h, pss), _worker_i(worker_i) { }
G1ParScanThreadState* pss) :
G1ScanClosureBase(g1h, pss) { }
template <class T> void do_oop_work(T* p);
virtual void do_oop(narrowOop* p) { do_oop_work(p); }
virtual void do_oop(oop* p) { do_oop_work(p); }
virtual void do_oop(oop* p) { do_oop_work(p); }
};
// Used during the Scan RS phase to scan cards from the remembered set during garbage collection.
@ -88,11 +83,22 @@ public:
virtual void do_oop(narrowOop* p) { do_oop_work(p); }
};
// This closure is applied to the fields of the objects that have just been copied during evacuation.
class G1ScanEvacuatedObjClosure : public G1ScanClosureBase {
friend class G1ScanInYoungSetter;
enum ScanningInYoungValues {
False = 0,
True,
Uninitialized
};
ScanningInYoungValues _scanning_in_young;
public:
G1ScanEvacuatedObjClosure(G1CollectedHeap* g1h, G1ParScanThreadState* par_scan_state) :
G1ScanClosureBase(g1h, par_scan_state) { }
G1ScanClosureBase(g1h, par_scan_state), _scanning_in_young(Uninitialized) { }
template <class T> void do_oop_work(T* p);
virtual void do_oop(oop* p) { do_oop_work(p); }
@ -106,6 +112,21 @@ public:
}
};
// RAII object to properly set the _scanning_in_young field in G1ScanEvacuatedObjClosure.
class G1ScanInYoungSetter : public StackObj {
G1ScanEvacuatedObjClosure* _closure;
public:
G1ScanInYoungSetter(G1ScanEvacuatedObjClosure* closure, bool new_value) : _closure(closure) {
assert(_closure->_scanning_in_young == G1ScanEvacuatedObjClosure::Uninitialized, "Must not be set");
_closure->_scanning_in_young = new_value ? G1ScanEvacuatedObjClosure::True : G1ScanEvacuatedObjClosure::False;
}
~G1ScanInYoungSetter() {
DEBUG_ONLY(_closure->_scanning_in_young = G1ScanEvacuatedObjClosure::Uninitialized;)
}
};
// Add back base class for metadata
class G1ParCopyHelper : public OopClosure {
protected:

View File

@ -82,12 +82,13 @@ inline void G1ScanEvacuatedObjClosure::do_oop_work(T* p) {
const InCSetState state = _g1h->in_cset_state(obj);
if (state.is_in_cset()) {
prefetch_and_push(p, obj);
} else {
if (HeapRegion::is_in_same_region(p, obj)) {
} else if (!HeapRegion::is_in_same_region(p, obj)) {
handle_non_cset_obj_common(state, p, obj);
assert(_scanning_in_young != Uninitialized, "Scan location has not been initialized.");
if (_scanning_in_young == True) {
return;
}
handle_non_cset_obj_common(state, p, obj);
_par_scan_state->update_rs(_from, p, obj);
_par_scan_state->enqueue_card_if_tracked(p, obj);
}
}
@ -172,13 +173,9 @@ inline void G1ScanObjsDuringUpdateRSClosure::do_oop_work(T* p) {
// Since the source is always from outside the collection set, here we implicitly know
// that this is a cross-region reference too.
prefetch_and_push(p, obj);
} else {
HeapRegion* to = _g1h->heap_region_containing(obj);
if (_from == to) {
return;
}
} else if (!HeapRegion::is_in_same_region(p, obj)) {
handle_non_cset_obj_common(state, p, obj);
to->rem_set()->add_reference(p, _worker_i);
_par_scan_state->enqueue_card_if_tracked(p, obj);
}
}
@ -193,10 +190,7 @@ inline void G1ScanObjsDuringScanRSClosure::do_oop_work(T* p) {
const InCSetState state = _g1h->in_cset_state(obj);
if (state.is_in_cset()) {
prefetch_and_push(p, obj);
} else {
if (HeapRegion::is_in_same_region(p, obj)) {
return;
}
} else if (!HeapRegion::is_in_same_region(p, obj)) {
handle_non_cset_obj_common(state, p, obj);
}
}

View File

@ -311,8 +311,7 @@ oop G1ParScanThreadState::copy_to_survivor_space(InCSetState const state,
oop* old_p = set_partial_array_mask(old);
do_oop_partial_array(old_p);
} else {
HeapRegion* const to_region = _g1h->heap_region_containing(obj_ptr);
_scanner.set_region(to_region);
G1ScanInYoungSetter x(&_scanner, dest_state.is_young());
obj->oop_iterate_backwards(&_scanner);
}
return obj;
@ -367,7 +366,7 @@ oop G1ParScanThreadState::handle_evacuation_failure_par(oop old, markOop m) {
_g1h->preserve_mark_during_evac_failure(_worker_id, old, m);
_scanner.set_region(r);
G1ScanInYoungSetter x(&_scanner, r->is_young());
old->oop_iterate_backwards(&_scanner);
return old;

View File

@ -104,17 +104,16 @@ public:
template <class T> void do_oop_ext(T* ref);
template <class T> void push_on_queue(T* ref);
template <class T> void update_rs(HeapRegion* from, T* p, oop o) {
assert(!HeapRegion::is_in_same_region(p, o), "Caller should have filtered out cross-region references already.");
// If the field originates from the to-space, we don't need to include it
// in the remembered set updates. Also, if we are not tracking the remembered
// set in the destination region, do not bother either.
if (!from->is_young() && _g1h->heap_region_containing((HeapWord*)o)->rem_set()->is_tracked()) {
size_t card_index = ct()->index_for(p);
// If the card hasn't been added to the buffer, do it.
if (ct()->mark_card_deferred(card_index)) {
dirty_card_queue().enqueue((jbyte*)ct()->byte_for_index(card_index));
}
template <class T> void enqueue_card_if_tracked(T* p, oop o) {
assert(!HeapRegion::is_in_same_region(p, o), "Should have filtered out cross-region references already.");
assert(!_g1h->heap_region_containing(p)->is_young(), "Should have filtered out from-young references already.");
if (!_g1h->heap_region_containing((HeapWord*)o)->rem_set()->is_tracked()) {
return;
}
size_t card_index = ct()->index_for(p);
// If the card hasn't been added to the buffer, do it.
if (ct()->mark_card_deferred(card_index)) {
dirty_card_queue().enqueue((jbyte*)ct()->byte_for_index(card_index));
}
}

View File

@ -61,9 +61,12 @@ template <class T> void G1ParScanThreadState::do_oop_evac(T* p) {
RawAccess<IS_NOT_NULL>::oop_store(p, obj);
assert(obj != NULL, "Must be");
if (!HeapRegion::is_in_same_region(p, obj)) {
HeapRegion* from = _g1h->heap_region_containing(p);
update_rs(from, p, obj);
if (HeapRegion::is_in_same_region(p, obj)) {
return;
}
HeapRegion* from = _g1h->heap_region_containing(p);
if (!from->is_young()) {
enqueue_card_if_tracked(p, obj);
}
}
@ -109,7 +112,9 @@ inline void G1ParScanThreadState::do_oop_partial_array(oop* p) {
// so that the heap remains parsable in case of evacuation failure.
to_obj_array->set_length(end);
}
_scanner.set_region(_g1h->heap_region_containing(to_obj));
HeapRegion* hr = _g1h->heap_region_containing(to_obj);
G1ScanInYoungSetter x(&_scanner, hr->is_young());
// Process indexes [start,end). It will also process the header
// along with the first chunk (i.e., the chunk with start == 0).
// Note that at this point the length field of to_obj_array is not

View File

@ -133,10 +133,10 @@ private:
virtual bool do_heap_region(HeapRegion* r) {
uint hrm_index = r->hrm_index();
if (!r->in_collection_set() && r->is_old_or_humongous_or_archive()) {
if (!r->in_collection_set() && r->is_old_or_humongous_or_archive() && !r->is_empty()) {
_scan_top[hrm_index] = r->top();
} else {
_scan_top[hrm_index] = r->bottom();
_scan_top[hrm_index] = NULL;
}
return false;
}
@ -191,6 +191,7 @@ public:
void reset() {
for (uint i = 0; i < _max_regions; i++) {
_iter_states[i] = Unclaimed;
_scan_top[i] = NULL;
}
G1ResetScanTopClosure cl(_scan_top);
@ -333,7 +334,7 @@ void G1ScanRSForRegionClosure::claim_card(size_t card_index, const uint region_i
void G1ScanRSForRegionClosure::scan_card(MemRegion mr, uint region_idx_for_card) {
HeapRegion* const card_region = _g1h->region_at(region_idx_for_card);
_scan_objs_on_card_cl->set_region(card_region);
assert(!card_region->is_young(), "Should not scan card in young region %u", region_idx_for_card);
card_region->oops_on_card_seq_iterate_careful<true>(mr, _scan_objs_on_card_cl);
_scan_objs_on_card_cl->trim_queue_partially();
_cards_scanned++;
@ -350,6 +351,10 @@ void G1ScanRSForRegionClosure::scan_rem_set_roots(HeapRegion* r) {
_scan_state->add_dirty_region(region_idx);
}
if (r->rem_set()->cardset_is_empty()) {
return;
}
// We claim cards in blocks so as to reduce the contention.
size_t const block_size = G1RSetScanBlockSize;
@ -367,18 +372,21 @@ void G1ScanRSForRegionClosure::scan_rem_set_roots(HeapRegion* r) {
}
_cards_claimed++;
// If the card is dirty, then G1 will scan it during Update RS.
if (_ct->is_card_claimed(card_index) || _ct->is_card_dirty(card_index)) {
HeapWord* const card_start = _g1h->bot()->address_for_index_raw(card_index);
uint const region_idx_for_card = _g1h->addr_to_region(card_start);
#ifdef ASSERT
HeapRegion* hr = _g1h->region_at_or_null(region_idx_for_card);
assert(hr == NULL || hr->is_in_reserved(card_start),
"Card start " PTR_FORMAT " to scan outside of region %u", p2i(card_start), _g1h->region_at(region_idx_for_card)->hrm_index());
#endif
HeapWord* const top = _scan_state->scan_top(region_idx_for_card);
if (card_start >= top) {
continue;
}
HeapWord* const card_start = _g1h->bot()->address_for_index(card_index);
uint const region_idx_for_card = _g1h->addr_to_region(card_start);
assert(_g1h->region_at(region_idx_for_card)->is_in_reserved(card_start),
"Card start " PTR_FORMAT " to scan outside of region %u", p2i(card_start), _g1h->region_at(region_idx_for_card)->hrm_index());
HeapWord* const top = _scan_state->scan_top(region_idx_for_card);
if (card_start >= top) {
// If the card is dirty, then G1 will scan it during Update RS.
if (_ct->is_card_claimed(card_index) || _ct->is_card_dirty(card_index)) {
continue;
}
@ -486,7 +494,7 @@ void G1RemSet::update_rem_set(G1ParScanThreadState* pss, uint worker_i) {
if (G1HotCardCache::default_use_cache()) {
G1EvacPhaseTimesTracker x(p, pss, G1GCPhaseTimes::ScanHCC, worker_i);
G1ScanObjsDuringUpdateRSClosure scan_hcc_cl(_g1h, pss, worker_i);
G1ScanObjsDuringUpdateRSClosure scan_hcc_cl(_g1h, pss);
G1RefineCardClosure refine_card_cl(_g1h, &scan_hcc_cl);
_g1h->iterate_hcc_closure(&refine_card_cl, worker_i);
}
@ -495,7 +503,7 @@ void G1RemSet::update_rem_set(G1ParScanThreadState* pss, uint worker_i) {
{
G1EvacPhaseTimesTracker x(p, pss, G1GCPhaseTimes::UpdateRS, worker_i);
G1ScanObjsDuringUpdateRSClosure update_rs_cl(_g1h, pss, worker_i);
G1ScanObjsDuringUpdateRSClosure update_rs_cl(_g1h, pss);
G1RefineCardClosure refine_card_cl(_g1h, &update_rs_cl);
_g1h->iterate_dirty_card_closure(&refine_card_cl, worker_i);
@ -545,6 +553,16 @@ void G1RemSet::refine_card_concurrently(jbyte* card_ptr,
uint worker_i) {
assert(!_g1h->is_gc_active(), "Only call concurrently");
// Construct the region representing the card.
HeapWord* start = _ct->addr_for(card_ptr);
// And find the region containing it.
HeapRegion* r = _g1h->heap_region_containing_or_null(start);
// If this is a (stale) card into an uncommitted region, exit.
if (r == NULL) {
return;
}
check_card_ptr(card_ptr, _ct);
// If the card is no longer dirty, nothing to do.
@ -552,11 +570,6 @@ void G1RemSet::refine_card_concurrently(jbyte* card_ptr,
return;
}
// Construct the region representing the card.
HeapWord* start = _ct->addr_for(card_ptr);
// And find the region containing it.
HeapRegion* r = _g1h->heap_region_containing(start);
// This check is needed for some uncommon cases where we should
// ignore the card.
//
@ -679,6 +692,18 @@ bool G1RemSet::refine_card_during_gc(jbyte* card_ptr,
G1ScanObjsDuringUpdateRSClosure* update_rs_cl) {
assert(_g1h->is_gc_active(), "Only call during GC");
// Construct the region representing the card.
HeapWord* card_start = _ct->addr_for(card_ptr);
// And find the region containing it.
uint const card_region_idx = _g1h->addr_to_region(card_start);
HeapWord* scan_limit = _scan_state->scan_top(card_region_idx);
if (scan_limit == NULL) {
// This is a card into an uncommitted region. We need to bail out early as we
// should not access the corresponding card table entry.
return false;
}
check_card_ptr(card_ptr, _ct);
// If the card is no longer dirty, nothing to do. This covers cards that were already
@ -691,13 +716,7 @@ bool G1RemSet::refine_card_during_gc(jbyte* card_ptr,
// number of potential duplicate scans (multiple threads may enqueue the same card twice).
*card_ptr = G1CardTable::clean_card_val() | G1CardTable::claimed_card_val();
// Construct the region representing the card.
HeapWord* card_start = _ct->addr_for(card_ptr);
// And find the region containing it.
uint const card_region_idx = _g1h->addr_to_region(card_start);
_scan_state->add_dirty_region(card_region_idx);
HeapWord* scan_limit = _scan_state->scan_top(card_region_idx);
if (scan_limit <= card_start) {
// If the card starts above the area in the region containing objects to scan, skip it.
return false;
@ -710,7 +729,7 @@ bool G1RemSet::refine_card_during_gc(jbyte* card_ptr,
assert(!dirty_region.is_empty(), "sanity");
HeapRegion* const card_region = _g1h->region_at(card_region_idx);
update_rs_cl->set_region(card_region);
assert(!card_region->is_young(), "Should not scan card in young region %u", card_region_idx);
bool card_processed = card_region->oops_on_card_seq_iterate_careful<true>(dirty_region, update_rs_cl);
assert(card_processed, "must be");
return true;

View File

@ -123,10 +123,7 @@ class HeapRegionManager: public CHeapObj<mtGC> {
public:
bool is_free(HeapRegion* hr) const;
#endif
// Returns whether the given region is available for allocation.
bool is_available(uint region) const;
public:
public:
// Empty constructor, we'll initialize it with the initialize() method.
HeapRegionManager();
@ -147,6 +144,13 @@ public:
// is valid.
inline HeapRegion* at(uint index) const;
// Return the HeapRegion at the given index, NULL if the index
// is for an unavailable region.
inline HeapRegion* at_or_null(uint index) const;
// Returns whether the given region is available for allocation.
bool is_available(uint region) const;
// Return the next region (by index) that is part of the same
// humongous object that hr is part of.
inline HeapRegion* next_region_in_humongous(HeapRegion* hr) const;

View File

@ -47,6 +47,16 @@ inline HeapRegion* HeapRegionManager::at(uint index) const {
return hr;
}
inline HeapRegion* HeapRegionManager::at_or_null(uint index) const {
if (!is_available(index)) {
return NULL;
}
HeapRegion* hr = _regions.get_by_index(index);
assert(hr != NULL, "All available regions must have a HeapRegion but index %u has not.", index);
assert(hr->hrm_index() == index, "sanity");
return hr;
}
inline HeapRegion* HeapRegionManager::next_region_in_humongous(HeapRegion* hr) const {
uint index = hr->hrm_index();
assert(is_available(index), "pre-condition");

View File

@ -239,10 +239,9 @@ size_t OtherRegionsTable::_mod_max_fine_entries_mask = 0;
size_t OtherRegionsTable::_fine_eviction_stride = 0;
size_t OtherRegionsTable::_fine_eviction_sample_size = 0;
OtherRegionsTable::OtherRegionsTable(HeapRegion* hr, Mutex* m) :
OtherRegionsTable::OtherRegionsTable(Mutex* m) :
_g1h(G1CollectedHeap::heap()),
_m(m),
_hr(hr),
_coarse_map(G1CollectedHeap::heap()->max_regions(), mtGC),
_n_coarse_entries(0),
_fine_grain_regions(NULL),
@ -250,7 +249,7 @@ OtherRegionsTable::OtherRegionsTable(HeapRegion* hr, Mutex* m) :
_first_all_fine_prts(NULL),
_last_all_fine_prts(NULL),
_fine_eviction_start(0),
_sparse_table(hr)
_sparse_table()
{
typedef PerRegionTable* PerRegionTablePtr;
@ -348,15 +347,6 @@ CardIdx_t OtherRegionsTable::card_within_region(OopOrNarrowOopStar within_region
}
void OtherRegionsTable::add_reference(OopOrNarrowOopStar from, uint tid) {
uint cur_hrm_ind = _hr->hrm_index();
uintptr_t from_card = uintptr_t(from) >> CardTable::card_shift;
if (G1FromCardCache::contains_or_replace(tid, cur_hrm_ind, from_card)) {
assert(contains_reference(from), "We just found " PTR_FORMAT " in the FromCardCache", p2i(from));
return;
}
// Note that this may be a continued H region.
HeapRegion* from_hr = _g1h->heap_region_containing(from);
RegionIdx_t from_hrm_ind = (RegionIdx_t) from_hr->hrm_index();
@ -569,10 +559,6 @@ size_t OtherRegionsTable::fl_mem_size() {
return PerRegionTable::fl_mem_size();
}
void OtherRegionsTable::clear_fcc() {
G1FromCardCache::clear(_hr->hrm_index());
}
void OtherRegionsTable::clear() {
// if there are no entries, skip this step
if (_first_all_fine_prts != NULL) {
@ -590,8 +576,6 @@ void OtherRegionsTable::clear() {
}
_n_fine_entries = 0;
_n_coarse_entries = 0;
clear_fcc();
}
bool OtherRegionsTable::contains_reference(OopOrNarrowOopStar from) const {
@ -627,11 +611,16 @@ HeapRegionRemSet::HeapRegionRemSet(G1BlockOffsetTable* bot,
: _bot(bot),
_code_roots(),
_m(Mutex::leaf, FormatBuffer<128>("HeapRegionRemSet lock #%u", hr->hrm_index()), true, Monitor::_safepoint_check_never),
_other_regions(hr, &_m),
_other_regions(&_m),
_hr(hr),
_state(Untracked)
{
}
void HeapRegionRemSet::clear_fcc() {
G1FromCardCache::clear(_hr->hrm_index());
}
void HeapRegionRemSet::setup_remset_size() {
// Setup sparse and fine-grain tables sizes.
// table_size = base * (log(region_size / 1M) + 1)
@ -659,6 +648,7 @@ void HeapRegionRemSet::clear_locked(bool only_cardset) {
if (!only_cardset) {
_code_roots.clear();
}
clear_fcc();
_other_regions.clear();
set_state_empty();
assert(occupied_locked() == 0, "Should be clear.");
@ -751,7 +741,7 @@ bool HeapRegionRemSetIterator::coarse_has_next(size_t& card_index) {
_coarse_cur_region_cur_card = 0;
HeapWord* r_bot =
_g1h->region_at((uint) _coarse_cur_region_index)->bottom();
_cur_region_card_offset = _bot->index_for(r_bot);
_cur_region_card_offset = _bot->index_for_raw(r_bot);
} else {
return false;
}
@ -792,7 +782,7 @@ void HeapRegionRemSetIterator::switch_to_prt(PerRegionTable* prt) {
_fine_cur_prt = prt;
HeapWord* r_bot = _fine_cur_prt->hr()->bottom();
_cur_region_card_offset = _bot->index_for(r_bot);
_cur_region_card_offset = _bot->index_for_raw(r_bot);
// The bitmap scan for the PRT always scans from _cur_region_cur_card + 1.
// To avoid special-casing this start case, and not miss the first bitmap

View File

@ -76,7 +76,6 @@ class OtherRegionsTable {
G1CollectedHeap* _g1h;
Mutex* _m;
HeapRegion* _hr;
// These are protected by "_m".
CHeapBitMap _coarse_map;
@ -124,11 +123,8 @@ class OtherRegionsTable {
bool contains_reference_locked(OopOrNarrowOopStar from) const;
public:
// Clear the from_card_cache entries for this region.
void clear_fcc();
// Create a new remembered set for the given heap region. The given mutex should
// be used to ensure consistency.
OtherRegionsTable(HeapRegion* hr, Mutex* m);
// Create a new remembered set. The given mutex is used to ensure consistency.
OtherRegionsTable(Mutex* m);
// Returns the card index of the given within_region pointer relative to the bottom
// of the given heap region.
@ -182,13 +178,21 @@ private:
OtherRegionsTable _other_regions;
HeapRegion* _hr;
void clear_fcc();
public:
HeapRegionRemSet(G1BlockOffsetTable* bot, HeapRegion* hr);
static void setup_remset_size();
bool cardset_is_empty() const {
return _other_regions.is_empty();
}
bool is_empty() const {
return (strong_code_roots_list_length() == 0) && _other_regions.is_empty();
return (strong_code_roots_list_length() == 0) && cardset_is_empty();
}
bool occupancy_less_or_equal_than(size_t occ) const {
@ -239,18 +243,18 @@ public:
if (_state == Untracked) {
return;
}
_other_regions.clear_fcc();
clear_fcc();
_state = Untracked;
}
void set_state_updating() {
guarantee(SafepointSynchronize::is_at_safepoint() && !is_tracked(), "Should only set to Updating from Untracked during safepoint but is %s", get_state_str());
_other_regions.clear_fcc();
clear_fcc();
_state = Updating;
}
void set_state_complete() {
_other_regions.clear_fcc();
clear_fcc();
_state = Complete;
}
@ -265,6 +269,15 @@ public:
if (state == Untracked) {
return;
}
uint cur_idx = _hr->hrm_index();
uintptr_t from_card = uintptr_t(from) >> CardTable::card_shift;
if (G1FromCardCache::contains_or_replace(tid, cur_idx, from_card)) {
assert(contains_reference(from), "We just found " PTR_FORMAT " in the FromCardCache", p2i(from));
return;
}
_other_regions.add_reference(from, tid);
}
@ -353,7 +366,7 @@ public:
};
class HeapRegionRemSetIterator : public StackObj {
private:
private:
// The region RSet over which we are iterating.
HeapRegionRemSet* _hrrs;
@ -401,7 +414,7 @@ class HeapRegionRemSetIterator : public StackObj {
// The Sparse remembered set iterator.
SparsePRTIter _sparse_iter;
public:
public:
HeapRegionRemSetIterator(HeapRegionRemSet* hrrs);
// If there remains one or more cards to be yielded, returns true and

View File

@ -361,8 +361,8 @@ void SparsePRT::cleanup_all() {
}
SparsePRT::SparsePRT(HeapRegion* hr) :
_hr(hr), _expanded(false), _next_expanded(NULL)
SparsePRT::SparsePRT() :
_expanded(false), _next_expanded(NULL)
{
_cur = new RSHashTable(InitialCapacity);
_next = _cur;

View File

@ -231,8 +231,6 @@ class SparsePRT {
RSHashTable* _cur;
RSHashTable* _next;
HeapRegion* _hr;
enum SomeAdditionalPrivateConstants {
InitialCapacity = 16
};
@ -254,7 +252,7 @@ class SparsePRT {
static SparsePRT* volatile _head_expanded_list;
public:
SparsePRT(HeapRegion* hr);
SparsePRT();
~SparsePRT();

View File

@ -182,214 +182,3 @@ GCPhase* TimePartitionPhasesIterator::next() {
assert(has_next(), "Must have phases left");
return _time_partitions->phase_at(_next++);
}
/////////////// Unit tests ///////////////
#ifndef PRODUCT
class TimePartitionPhasesIteratorTest {
public:
static void all() {
one_pause();
two_pauses();
one_sub_pause_phase();
many_sub_pause_phases();
many_sub_pause_phases2();
max_nested_pause_phases();
one_concurrent();
}
static void validate_gc_phase(GCPhase* phase, int level, const char* name, const Ticks& start, const Ticks& end) {
assert(phase->level() == level, "Incorrect level");
assert(strcmp(phase->name(), name) == 0, "Incorrect name");
assert(phase->start() == start, "Incorrect start");
assert(phase->end() == end, "Incorrect end");
}
static void one_pause() {
TimePartitions time_partitions;
time_partitions.report_gc_phase_start("PausePhase", 2);
time_partitions.report_gc_phase_end(8);
TimePartitionPhasesIterator iter(&time_partitions);
validate_gc_phase(iter.next(), 0, "PausePhase", 2, 8);
assert(time_partitions.sum_of_pauses() == Ticks(8) - Ticks(2), "Incorrect");
assert(time_partitions.longest_pause() == Ticks(8) - Ticks(2), "Incorrect");
assert(!iter.has_next(), "Too many elements");
}
static void two_pauses() {
TimePartitions time_partitions;
time_partitions.report_gc_phase_start("PausePhase1", 2);
time_partitions.report_gc_phase_end(3);
time_partitions.report_gc_phase_start("PausePhase2", 4);
time_partitions.report_gc_phase_end(6);
TimePartitionPhasesIterator iter(&time_partitions);
validate_gc_phase(iter.next(), 0, "PausePhase1", 2, 3);
validate_gc_phase(iter.next(), 0, "PausePhase2", 4, 6);
assert(time_partitions.sum_of_pauses() == Ticks(3) - Ticks(0), "Incorrect");
assert(time_partitions.longest_pause() == Ticks(2) - Ticks(0), "Incorrect");
assert(!iter.has_next(), "Too many elements");
}
static void one_sub_pause_phase() {
TimePartitions time_partitions;
time_partitions.report_gc_phase_start("PausePhase", 2);
time_partitions.report_gc_phase_start("SubPhase", 3);
time_partitions.report_gc_phase_end(4);
time_partitions.report_gc_phase_end(5);
TimePartitionPhasesIterator iter(&time_partitions);
validate_gc_phase(iter.next(), 0, "PausePhase", 2, 5);
validate_gc_phase(iter.next(), 1, "SubPhase", 3, 4);
assert(time_partitions.sum_of_pauses() == Ticks(3) - Ticks(0), "Incorrect");
assert(time_partitions.longest_pause() == Ticks(3) - Ticks(0), "Incorrect");
assert(!iter.has_next(), "Too many elements");
}
static void max_nested_pause_phases() {
TimePartitions time_partitions;
time_partitions.report_gc_phase_start("PausePhase", 2);
time_partitions.report_gc_phase_start("SubPhase1", 3);
time_partitions.report_gc_phase_start("SubPhase2", 4);
time_partitions.report_gc_phase_start("SubPhase3", 5);
time_partitions.report_gc_phase_end(6);
time_partitions.report_gc_phase_end(7);
time_partitions.report_gc_phase_end(8);
time_partitions.report_gc_phase_end(9);
TimePartitionPhasesIterator iter(&time_partitions);
validate_gc_phase(iter.next(), 0, "PausePhase", 2, 9);
validate_gc_phase(iter.next(), 1, "SubPhase1", 3, 8);
validate_gc_phase(iter.next(), 2, "SubPhase2", 4, 7);
validate_gc_phase(iter.next(), 3, "SubPhase3", 5, 6);
assert(time_partitions.sum_of_pauses() == Ticks(7) - Ticks(0), "Incorrect");
assert(time_partitions.longest_pause() == Ticks(7) - Ticks(0), "Incorrect");
assert(!iter.has_next(), "Too many elements");
}
static void many_sub_pause_phases() {
TimePartitions time_partitions;
time_partitions.report_gc_phase_start("PausePhase", 2);
time_partitions.report_gc_phase_start("SubPhase1", 3);
time_partitions.report_gc_phase_end(4);
time_partitions.report_gc_phase_start("SubPhase2", 5);
time_partitions.report_gc_phase_end(6);
time_partitions.report_gc_phase_start("SubPhase3", 7);
time_partitions.report_gc_phase_end(8);
time_partitions.report_gc_phase_start("SubPhase4", 9);
time_partitions.report_gc_phase_end(10);
time_partitions.report_gc_phase_end(11);
TimePartitionPhasesIterator iter(&time_partitions);
validate_gc_phase(iter.next(), 0, "PausePhase", 2, 11);
validate_gc_phase(iter.next(), 1, "SubPhase1", 3, 4);
validate_gc_phase(iter.next(), 1, "SubPhase2", 5, 6);
validate_gc_phase(iter.next(), 1, "SubPhase3", 7, 8);
validate_gc_phase(iter.next(), 1, "SubPhase4", 9, 10);
assert(time_partitions.sum_of_pauses() == Ticks(9) - Ticks(0), "Incorrect");
assert(time_partitions.longest_pause() == Ticks(9) - Ticks(0), "Incorrect");
assert(!iter.has_next(), "Too many elements");
}
static void many_sub_pause_phases2() {
TimePartitions time_partitions;
time_partitions.report_gc_phase_start("PausePhase", 2);
time_partitions.report_gc_phase_start("SubPhase1", 3);
time_partitions.report_gc_phase_start("SubPhase11", 4);
time_partitions.report_gc_phase_end(5);
time_partitions.report_gc_phase_start("SubPhase12", 6);
time_partitions.report_gc_phase_end(7);
time_partitions.report_gc_phase_end(8);
time_partitions.report_gc_phase_start("SubPhase2", 9);
time_partitions.report_gc_phase_start("SubPhase21", 10);
time_partitions.report_gc_phase_end(11);
time_partitions.report_gc_phase_start("SubPhase22", 12);
time_partitions.report_gc_phase_end(13);
time_partitions.report_gc_phase_end(14);
time_partitions.report_gc_phase_start("SubPhase3", 15);
time_partitions.report_gc_phase_end(16);
time_partitions.report_gc_phase_end(17);
TimePartitionPhasesIterator iter(&time_partitions);
validate_gc_phase(iter.next(), 0, "PausePhase", 2, 17);
validate_gc_phase(iter.next(), 1, "SubPhase1", 3, 8);
validate_gc_phase(iter.next(), 2, "SubPhase11", 4, 5);
validate_gc_phase(iter.next(), 2, "SubPhase12", 6, 7);
validate_gc_phase(iter.next(), 1, "SubPhase2", 9, 14);
validate_gc_phase(iter.next(), 2, "SubPhase21", 10, 11);
validate_gc_phase(iter.next(), 2, "SubPhase22", 12, 13);
validate_gc_phase(iter.next(), 1, "SubPhase3", 15, 16);
assert(time_partitions.sum_of_pauses() == Ticks(15) - Ticks(0), "Incorrect");
assert(time_partitions.longest_pause() == Ticks(15) - Ticks(0), "Incorrect");
assert(!iter.has_next(), "Too many elements");
}
static void one_concurrent() {
TimePartitions time_partitions;
time_partitions.report_gc_phase_start("ConcurrentPhase", 2, GCPhase::ConcurrentPhaseType);
time_partitions.report_gc_phase_end(8, GCPhase::ConcurrentPhaseType);
TimePartitionPhasesIterator iter(&time_partitions);
validate_gc_phase(iter.next(), 0, "ConcurrentPhase", 2, 8);
// ConcurrentPhaseType should not affect to both 'sum_of_pauses()' and 'longest_pause()'.
assert(time_partitions.sum_of_pauses() == Tickspan(), "Incorrect");
assert(time_partitions.longest_pause() == Tickspan(), "Incorrect");
assert(!iter.has_next(), "Too many elements");
}
};
class GCTimerTest {
public:
static void all() {
gc_start();
gc_end();
}
static void gc_start() {
GCTimer gc_timer;
gc_timer.register_gc_start(1);
assert(gc_timer.gc_start() == Ticks(1), "Incorrect");
}
static void gc_end() {
GCTimer gc_timer;
gc_timer.register_gc_start(1);
gc_timer.register_gc_end(2);
assert(gc_timer.gc_end() == Ticks(2), "Incorrect");
}
};
void GCTimer_test() {
GCTimerTest::all();
TimePartitionPhasesIteratorTest::all();
}
#endif

View File

@ -129,7 +129,6 @@ class PhasesIterator {
};
class GCTimer : public ResourceObj {
NOT_PRODUCT(friend class GCTimerTest;)
protected:
Ticks _gc_start;
Ticks _gc_end;

View File

@ -27,6 +27,8 @@
#include "classfile/classFileParser.hpp"
#include "classfile/classFileStream.hpp"
#include "classfile/javaClasses.inline.hpp"
#include "classfile/moduleEntry.hpp"
#include "classfile/modules.hpp"
#include "classfile/stackMapTable.hpp"
#include "classfile/verificationType.hpp"
#include "interpreter/bytecodes.hpp"
@ -61,25 +63,26 @@ static const char* utf8_constants[] = {
"J", // 1
"commit", // 2
"eventHandler", // 3
"Ljdk/jfr/internal/handlers/EventHandler;", // 4
"duration", // 5
"begin", // 6
"()V", // 7
"isEnabled", // 8
"()Z", // 9
"end", // 10
"shouldCommit", // 11
"startTime", // 12
"<clinit>", // 13
"jdk/jfr/FlightRecorder", // 14
"register", // 15
"(Ljava/lang/Class;)V", // 16 // LAST_REQUIRED_UTF8
"StackMapTable", // 17
"Exceptions", // 18
"duration", // 4
"begin", // 5
"()V", // 6
"isEnabled", // 7
"()Z", // 8
"end", // 9
"shouldCommit", // 10
"startTime", // 11 // LAST_REQUIRED_UTF8
"Ljdk/jfr/internal/handlers/EventHandler;", // 12
"Ljava/lang/Object;", // 13
"<clinit>", // 14
"jdk/jfr/FlightRecorder", // 15
"register", // 16
"(Ljava/lang/Class;)V", // 17
"StackMapTable", // 18
"Exceptions", // 19
"LineNumberTable", // 20
"LocalVariableTable", // 21
"LocalVariableTypeTable", // 22
"RuntimeVisibleAnnotation" // 23
"RuntimeVisibleAnnotation", // 23
};
enum utf8_req_symbols {
@ -87,7 +90,6 @@ enum utf8_req_symbols {
UTF8_REQ_J_FIELD_DESC,
UTF8_REQ_commit,
UTF8_REQ_eventHandler,
UTF8_REQ_eventHandler_FIELD_DESC,
UTF8_REQ_duration,
UTF8_REQ_begin,
UTF8_REQ_EMPTY_VOID_METHOD_DESC,
@ -96,15 +98,17 @@ enum utf8_req_symbols {
UTF8_REQ_end,
UTF8_REQ_shouldCommit,
UTF8_REQ_startTime,
UTF8_REQ_clinit,
UTF8_REQ_FlightRecorder,
UTF8_REQ_register,
UTF8_REQ_CLASS_VOID_METHOD_DESC,
NOF_UTF8_REQ_SYMBOLS
};
enum utf8_opt_symbols {
UTF8_OPT_StackMapTable = NOF_UTF8_REQ_SYMBOLS,
UTF8_OPT_eventHandler_FIELD_DESC = NOF_UTF8_REQ_SYMBOLS,
UTF8_OPT_LjavaLangObject,
UTF8_OPT_clinit,
UTF8_OPT_FlightRecorder,
UTF8_OPT_register,
UTF8_OPT_CLASS_VOID_METHOD_DESC,
UTF8_OPT_StackMapTable,
UTF8_OPT_Exceptions,
UTF8_OPT_LineNumberTable,
UTF8_OPT_LocalVariableTable,
@ -353,7 +357,7 @@ class AnnotationIterator : public StackObj {
static unsigned int unused_hash = 0;
static const char value_name[] = "value";
static bool has_registered_annotation(const InstanceKlass* ik, const Symbol* annotation_type, bool& value) {
static bool has_annotation(const InstanceKlass* ik, const Symbol* annotation_type, bool& value) {
assert(annotation_type != NULL, "invariant");
AnnotationArray* class_annotations = ik->class_annotations();
if (class_annotations == NULL) {
@ -383,16 +387,51 @@ static bool has_registered_annotation(const InstanceKlass* ik, const Symbol* ann
return false;
}
static bool registered_annotation_value(const InstanceKlass* ik, const Symbol* const registered_symbol) {
assert(registered_symbol != NULL, "invariant");
// Evaluate to the value of the first found Symbol* annotation type.
// Searching moves upwards in the klass hierarchy in order to support
// inherited annotations in addition to the ability to override.
static bool annotation_value(const InstanceKlass* ik, const Symbol* annotation_type, bool& value) {
assert(ik != NULL, "invariant");
assert(annotation_type != NULL, "invariant");
assert(JdkJfrEvent::is_a(ik), "invariant");
bool registered_value = false;
if (has_registered_annotation(ik, registered_symbol, registered_value)) {
return registered_value;
if (has_annotation(ik, annotation_type, value)) {
return true;
}
InstanceKlass* super = InstanceKlass::cast(ik->super());
return registered_annotation_value(super, registered_symbol);
InstanceKlass* const super = InstanceKlass::cast(ik->super());
return super != NULL && JdkJfrEvent::is_a(super) ? annotation_value(super, annotation_type, value) : false;
}
static const char jdk_jfr_module_name[] = "jdk.jfr";
static bool java_base_can_read_jdk_jfr() {
static bool can_read = false;
if (can_read) {
return true;
}
static Symbol* jdk_jfr_module_symbol = NULL;
if (jdk_jfr_module_symbol == NULL) {
jdk_jfr_module_symbol = SymbolTable::lookup_only(jdk_jfr_module_name, sizeof jdk_jfr_module_name - 1, unused_hash);
if (jdk_jfr_module_symbol == NULL) {
return false;
}
}
assert(jdk_jfr_module_symbol != NULL, "invariant");
ModuleEntryTable* const table = Modules::get_module_entry_table(Handle());
assert(table != NULL, "invariant");
const ModuleEntry* const java_base_module = table->javabase_moduleEntry();
if (java_base_module == NULL) {
return false;
}
assert(java_base_module != NULL, "invariant");
ModuleEntry* const jdk_jfr_module = table->lookup_only(jdk_jfr_module_symbol);
if (jdk_jfr_module == NULL) {
return false;
}
assert(jdk_jfr_module != NULL, "invariant");
if (java_base_module->can_read(jdk_jfr_module)) {
can_read = true;
}
return can_read;
}
static const char registered_constant[] = "Ljdk/jfr/Registered;";
@ -400,13 +439,23 @@ static const char registered_constant[] = "Ljdk/jfr/Registered;";
// Evaluate to the value of the first found "Ljdk/jfr/Registered;" annotation.
// Searching moves upwards in the klass hierarchy in order to support
// inherited annotations in addition to the ability to override.
static bool should_register_klass(const InstanceKlass* ik) {
static const Symbol* const registered_symbol = SymbolTable::lookup_only(registered_constant,
sizeof registered_constant - 1,
unused_hash);
static bool should_register_klass(const InstanceKlass* ik, bool& untypedEventHandler) {
assert(ik != NULL, "invariant");
assert(JdkJfrEvent::is_a(ik), "invariant");
assert(!untypedEventHandler, "invariant");
static const Symbol* registered_symbol = NULL;
if (registered_symbol == NULL) {
registered_symbol = SymbolTable::lookup_only(registered_constant, sizeof registered_constant - 1, unused_hash);
if (registered_symbol == NULL) {
return false;
}
}
assert(registered_symbol != NULL, "invariant");
return registered_annotation_value(ik, registered_symbol);
bool value = false; // to be set by annotation_value
untypedEventHandler = !(annotation_value(ik, registered_symbol, value) || java_base_can_read_jdk_jfr());
return value;
}
/*
* Map an utf8 constant back to its CONSTANT_UTF8_INFO
*/
@ -450,6 +499,9 @@ static u2 add_method_ref_info(JfrBigEndianWriter& writer,
u2 orig_cp_len,
u2& number_of_new_constants,
TRAPS) {
assert(cls_name_index != invalid_cp_index, "invariant");
assert(method_index != invalid_cp_index, "invariant");
assert(desc_index != invalid_cp_index, "invariant");
assert(is_index_within_range(cls_name_index, orig_cp_len, number_of_new_constants), "invariant");
assert(is_index_within_range(method_index, orig_cp_len, number_of_new_constants), "invariant");
assert(is_index_within_range(desc_index, orig_cp_len, number_of_new_constants), "invariant");
@ -477,9 +529,9 @@ static u2 add_flr_register_method_constants(JfrBigEndianWriter& writer,
TRAPS) {
assert(utf8_indexes != NULL, "invariant");
return add_method_ref_info(writer,
utf8_indexes[UTF8_REQ_FlightRecorder],
utf8_indexes[UTF8_REQ_register],
utf8_indexes[UTF8_REQ_CLASS_VOID_METHOD_DESC],
utf8_indexes[UTF8_OPT_FlightRecorder],
utf8_indexes[UTF8_OPT_register],
utf8_indexes[UTF8_OPT_CLASS_VOID_METHOD_DESC],
orig_cp_len,
number_of_new_constants,
THREAD);
@ -495,8 +547,8 @@ static u2 add_flr_register_method_constants(JfrBigEndianWriter& writer,
* }
*/
static jlong add_field_info(JfrBigEndianWriter& writer, u2 name_index, u2 desc_index, bool is_static = false) {
assert(name_index > 0, "invariant");
assert(desc_index > 0, "invariant");
assert(name_index != invalid_cp_index, "invariant");
assert(desc_index != invalid_cp_index, "invariant");
DEBUG_ONLY(const jlong start_offset = writer.current_offset();)
writer.write<u2>(JVM_ACC_SYNTHETIC | JVM_ACC_PRIVATE | (is_static ? JVM_ACC_STATIC : JVM_ACC_TRANSIENT)); // flags
writer.write(name_index);
@ -507,11 +559,11 @@ static jlong add_field_info(JfrBigEndianWriter& writer, u2 name_index, u2 desc_i
return writer.current_offset();
}
static u2 add_field_infos(JfrBigEndianWriter& writer, const u2* utf8_indexes) {
static u2 add_field_infos(JfrBigEndianWriter& writer, const u2* utf8_indexes, bool untypedEventHandler) {
assert(utf8_indexes != NULL, "invariant");
add_field_info(writer,
utf8_indexes[UTF8_REQ_eventHandler],
utf8_indexes[UTF8_REQ_eventHandler_FIELD_DESC],
untypedEventHandler ? utf8_indexes[UTF8_OPT_LjavaLangObject] : utf8_indexes[UTF8_OPT_eventHandler_FIELD_DESC],
true); // static
add_field_info(writer,
@ -989,7 +1041,8 @@ static jlong insert_clinit_method(const InstanceKlass* ik,
// This is to ensure that padding can be done
// where needed and to simplify size calculations.
static const u2 injected_code_length = 8;
const u2 name_index = utf8_indexes[UTF8_REQ_clinit];
const u2 name_index = utf8_indexes[UTF8_OPT_clinit];
assert(name_index != invalid_cp_index, "invariant");
const u2 desc_index = utf8_indexes[UTF8_REQ_EMPTY_VOID_METHOD_DESC];
const u2 max_stack = MAX2(clinit_method != NULL ? clinit_method->verifier_max_stack() : 1, 1);
const u2 max_locals = MAX2(clinit_method != NULL ? clinit_method->max_locals() : 0, 0);
@ -1138,59 +1191,58 @@ static u2 resolve_utf8_indexes(JfrBigEndianWriter& writer,
u2* const utf8_indexes,
u2 orig_cp_len,
const Method* clinit_method,
bool register_klass,
bool untypedEventHandler,
TRAPS) {
assert(utf8_indexes != NULL, "invariant");
u2 added_cp_entries = 0;
// resolve all required symbols
for (u2 index = 0; index < NOF_UTF8_REQ_SYMBOLS; ++index) {
utf8_indexes[index] = find_or_add_utf8_info(writer,
ik,
utf8_constants[index],
orig_cp_len,
added_cp_entries,
THREAD);
utf8_indexes[index] = find_or_add_utf8_info(writer, ik, utf8_constants[index], orig_cp_len, added_cp_entries, THREAD);
}
// Now determine optional constants (mainly "Code" attributes)
// resolve optional constants
utf8_indexes[UTF8_OPT_eventHandler_FIELD_DESC] = untypedEventHandler ? invalid_cp_index :
find_or_add_utf8_info(writer, ik, utf8_constants[UTF8_OPT_eventHandler_FIELD_DESC], orig_cp_len, added_cp_entries, THREAD);
utf8_indexes[UTF8_OPT_LjavaLangObject] = untypedEventHandler ?
find_or_add_utf8_info(writer, ik, utf8_constants[UTF8_OPT_LjavaLangObject], orig_cp_len, added_cp_entries, THREAD) : invalid_cp_index;
if (register_klass) {
utf8_indexes[UTF8_OPT_clinit] =
find_or_add_utf8_info(writer, ik, utf8_constants[UTF8_OPT_clinit], orig_cp_len, added_cp_entries, THREAD);
utf8_indexes[UTF8_OPT_FlightRecorder] =
find_or_add_utf8_info(writer, ik, utf8_constants[UTF8_OPT_FlightRecorder], orig_cp_len, added_cp_entries, THREAD);
utf8_indexes[UTF8_OPT_register] =
find_or_add_utf8_info(writer, ik, utf8_constants[UTF8_OPT_register], orig_cp_len, added_cp_entries, THREAD);
utf8_indexes[UTF8_OPT_CLASS_VOID_METHOD_DESC] =
find_or_add_utf8_info(writer, ik, utf8_constants[UTF8_OPT_CLASS_VOID_METHOD_DESC], orig_cp_len, added_cp_entries, THREAD);
} else {
utf8_indexes[UTF8_OPT_clinit] = invalid_cp_index;
utf8_indexes[UTF8_OPT_FlightRecorder] = invalid_cp_index;
utf8_indexes[UTF8_OPT_register] = invalid_cp_index;
utf8_indexes[UTF8_OPT_CLASS_VOID_METHOD_DESC] = invalid_cp_index;
}
if (clinit_method != NULL && clinit_method->has_stackmap_table()) {
utf8_indexes[UTF8_OPT_StackMapTable] =
find_or_add_utf8_info(writer,
ik,
utf8_constants[UTF8_OPT_StackMapTable],
orig_cp_len,
added_cp_entries,
THREAD);
find_or_add_utf8_info(writer, ik, utf8_constants[UTF8_OPT_StackMapTable], orig_cp_len, added_cp_entries, THREAD);
} else {
utf8_indexes[UTF8_OPT_StackMapTable] = invalid_cp_index;
}
if (clinit_method != NULL && clinit_method->has_linenumber_table()) {
utf8_indexes[UTF8_OPT_LineNumberTable] =
find_or_add_utf8_info(writer,
ik,
utf8_constants[UTF8_OPT_LineNumberTable],
orig_cp_len,
added_cp_entries,
THREAD);
find_or_add_utf8_info(writer, ik, utf8_constants[UTF8_OPT_LineNumberTable], orig_cp_len, added_cp_entries, THREAD);
} else {
utf8_indexes[UTF8_OPT_LineNumberTable] = invalid_cp_index;
}
if (clinit_method != NULL && clinit_method->has_localvariable_table()) {
utf8_indexes[UTF8_OPT_LocalVariableTable] =
find_or_add_utf8_info(writer,
ik,
utf8_constants[UTF8_OPT_LocalVariableTable],
orig_cp_len,
added_cp_entries,
THREAD);
find_or_add_utf8_info(writer, ik, utf8_constants[UTF8_OPT_LocalVariableTable], orig_cp_len, added_cp_entries, THREAD);
utf8_indexes[UTF8_OPT_LocalVariableTypeTable] =
find_or_add_utf8_info(writer,
ik,
utf8_constants[UTF8_OPT_LocalVariableTypeTable],
orig_cp_len,
added_cp_entries,
THREAD);
find_or_add_utf8_info(writer, ik, utf8_constants[UTF8_OPT_LocalVariableTypeTable], orig_cp_len, added_cp_entries, THREAD);
} else {
utf8_indexes[UTF8_OPT_LocalVariableTable] = invalid_cp_index;
utf8_indexes[UTF8_OPT_LocalVariableTypeTable] = invalid_cp_index;
@ -1207,7 +1259,8 @@ static u1* new_bytes_for_lazy_instrumentation(const InstanceKlass* ik,
// If the class already has a clinit method
// we need to take that into account
const Method* clinit_method = ik->class_initializer();
const bool register_klass = should_register_klass(ik);
bool untypedEventHandler = false;
const bool register_klass = should_register_klass(ik, untypedEventHandler);
const ClassFileStream* const orig_stream = parser.clone_stream();
const int orig_stream_size = orig_stream->length();
assert(orig_stream->current_offset() == 0, "invariant");
@ -1241,7 +1294,8 @@ static u1* new_bytes_for_lazy_instrumentation(const InstanceKlass* ik,
// Resolve_utf8_indexes will be conservative in attempting to
// locate an existing UTF8_INFO; it will only append constants
// that is absolutely required
u2 number_of_new_constants = resolve_utf8_indexes(writer, ik, utf8_indexes, orig_cp_len, clinit_method, THREAD);
u2 number_of_new_constants =
resolve_utf8_indexes(writer, ik, utf8_indexes, orig_cp_len, clinit_method, register_klass, untypedEventHandler, THREAD);
// UTF8_INFO entries now added to the constant pool
// In order to invoke a method we would need additional
// constants, JVM_CONSTANT_Class, JVM_CONSTANT_NameAndType
@ -1274,7 +1328,7 @@ static u1* new_bytes_for_lazy_instrumentation(const InstanceKlass* ik,
assert(writer.is_valid(), "invariant");
// We are sitting just after the original number of field_infos
// so this is a position where we can add (append) new field_infos
const u2 number_of_new_fields = add_field_infos(writer, utf8_indexes);
const u2 number_of_new_fields = add_field_infos(writer, utf8_indexes, untypedEventHandler);
assert(writer.is_valid(), "invariant");
const jlong new_method_len_offset = writer.current_offset();
// Additional field_infos added, update classfile fields_count

View File

@ -132,7 +132,7 @@ jobject JfrEventClasses::get_all_event_classes(TRAPS) {
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
initialize(THREAD);
assert(empty_java_util_arraylist != NULL, "should have been setup already!");
static const char jdk_jfr_event_name[] = "jdk/jfr/Event";
static const char jdk_jfr_event_name[] = "jdk/internal/event/Event";
unsigned int unused_hash = 0;
Symbol* const event_klass_name = SymbolTable::lookup_only(jdk_jfr_event_name, sizeof jdk_jfr_event_name - 1, unused_hash);

View File

@ -76,22 +76,43 @@ static traceid next_class_loader_data_id() {
return atomic_inc(&cld_id_counter) << TRACE_ID_SHIFT;
}
static bool found_jdk_internal_event_klass = false;
static bool found_jdk_jfr_event_klass = false;
static void check_klass(const Klass* klass) {
assert(klass != NULL, "invariant");
if (found_jdk_jfr_event_klass) {
if (found_jdk_internal_event_klass && found_jdk_jfr_event_klass) {
return;
}
static const Symbol* jdk_internal_event_sym = NULL;
if (jdk_internal_event_sym == NULL) {
// setup when loading the first TypeArrayKlass (Universe::genesis) hence single threaded invariant
jdk_internal_event_sym = SymbolTable::new_permanent_symbol("jdk/internal/event/Event", Thread::current());
}
assert(jdk_internal_event_sym != NULL, "invariant");
static const Symbol* jdk_jfr_event_sym = NULL;
if (jdk_jfr_event_sym == NULL) {
// setup when loading the first TypeArrayKlass (Universe::genesis) hence single threaded invariant
jdk_jfr_event_sym = SymbolTable::new_permanent_symbol("jdk/jfr/Event", Thread::current());
}
assert(jdk_jfr_event_sym != NULL, "invariant");
if (jdk_jfr_event_sym == klass->name() && klass->class_loader() == NULL) {
found_jdk_jfr_event_klass = true;
JfrTraceId::tag_as_jdk_jfr_event(klass);
const Symbol* const klass_name = klass->name();
if (!found_jdk_internal_event_klass) {
if (jdk_internal_event_sym == klass_name && klass->class_loader() == NULL) {
found_jdk_internal_event_klass = true;
JfrTraceId::tag_as_jdk_jfr_event(klass);
return;
}
}
if (!found_jdk_jfr_event_klass) {
if (jdk_jfr_event_sym == klass_name && klass->class_loader() == NULL) {
found_jdk_jfr_event_klass = true;
JfrTraceId::tag_as_jdk_jfr_event(klass);
return;
}
}
}

View File

@ -112,10 +112,8 @@ inline bool JfrTraceId::is_jdk_jfr_event(const Klass* k) {
inline void JfrTraceId::tag_as_jdk_jfr_event(const Klass* klass) {
assert(klass != NULL, "invariant");
assert(IS_NOT_AN_EVENT_KLASS(klass), "invariant");
SET_TAG(klass, JDK_JFR_EVENT_KLASS);
assert(IS_JDK_JFR_EVENT_KLASS(klass), "invariant");
assert(IS_NOT_AN_EVENT_SUB_KLASS(klass), "invariant");
}
inline bool JfrTraceId::is_jdk_jfr_event_sub(const Klass* k) {
@ -125,7 +123,7 @@ inline bool JfrTraceId::is_jdk_jfr_event_sub(const Klass* k) {
inline void JfrTraceId::tag_as_jdk_jfr_event_sub(const Klass* k) {
assert(k != NULL, "invariant");
if (IS_NOT_AN_EVENT_KLASS(k)) {
if (IS_NOT_AN_EVENT_SUB_KLASS(k)) {
SET_TAG(k, JDK_JFR_EVENT_SUBKLASS);
}
assert(IS_JDK_JFR_EVENT_SUBKLASS(k), "invariant");

View File

@ -914,6 +914,19 @@ void FileMapInfo::map_heap_regions_impl() {
return;
}
if (JvmtiExport::should_post_class_file_load_hook() && JvmtiExport::has_early_class_hook_env()) {
ShouldNotReachHere(); // CDS should have been disabled.
// The archived objects are mapped at JVM start-up, but we don't know if
// j.l.String or j.l.Class might be replaced by the ClassFileLoadHook,
// which would make the archived String or mirror objects invalid. Let's be safe and not
// use the archived objects. These 2 classes are loaded during the JVMTI "early" stage.
//
// If JvmtiExport::has_early_class_hook_env() is false, the classes of some objects
// in the archived subgraphs may be replaced by the ClassFileLoadHook. But that's OK
// because we won't install an archived object subgraph if the klass of any of the
// referenced objects are replaced. See HeapShared::initialize_from_archived_subgraph().
}
MemRegion heap_reserved = Universe::heap()->reserved_region();
log_info(cds)("CDS archive was created with max heap size = " SIZE_FORMAT "M, and the following configuration:",
@ -1224,6 +1237,15 @@ bool FileMapInfo::_validating_shared_path_table = false;
bool FileMapInfo::initialize() {
assert(UseSharedSpaces, "UseSharedSpaces expected.");
if (JvmtiExport::should_post_class_file_load_hook() && JvmtiExport::has_early_class_hook_env()) {
// CDS assumes that no classes resolved in SystemDictionary::resolve_well_known_classes
// are replaced at runtime by JVMTI ClassFileLoadHook. All of those classes are resolved
// during the JVMTI "early" stage, so we can still use CDS if
// JvmtiExport::has_early_class_hook_env() is false.
FileMapInfo::fail_continue("CDS is disabled because early JVMTI ClassFileLoadHook is in use.");
return false;
}
if (!open_for_read()) {
return false;
}

View File

@ -417,6 +417,11 @@ void HeapShared::initialize_from_archived_subgraph(Klass* k) {
Klass* resolved_k = SystemDictionary::resolve_or_null(
(obj_k)->name(), THREAD);
if (resolved_k != obj_k) {
assert(!SystemDictionary::is_well_known_klass(resolved_k),
"shared well-known classes must not be replaced by JVMTI ClassFileLoadHook");
ResourceMark rm(THREAD);
log_info(cds, heap)("Failed to load subgraph because %s was not loaded from archive",
resolved_k->external_name());
return;
}
if ((obj_k)->is_instance_klass()) {

View File

@ -604,7 +604,9 @@ bool C2Compiler::is_intrinsic_supported(const methodHandle& method, bool is_virt
return true;
}
int C2Compiler::initial_code_buffer_size() {
assert(SegmentedCodeCache, "Should be only used with a segmented code cache");
return Compile::MAX_inst_size + Compile::MAX_locs_size + initial_const_capacity;
int C2Compiler::initial_code_buffer_size(int const_size) {
// See Compile::init_scratch_buffer_blob
int locs_size = sizeof(relocInfo) * Compile::MAX_locs_size;
int slop = 2 * CodeSection::end_slop(); // space between sections
return Compile::MAX_inst_size + Compile::MAX_stubs_size + const_size + slop + locs_size;
}

View File

@ -26,6 +26,7 @@
#define SHARE_VM_OPTO_C2COMPILER_HPP
#include "compiler/abstractCompiler.hpp"
#include "opto/output.hpp"
class C2Compiler : public AbstractCompiler {
private:
@ -66,7 +67,7 @@ public:
virtual bool is_intrinsic_supported(const methodHandle& method, bool is_virtual);
// Initial size of the code buffer (may be increased at runtime)
static int initial_code_buffer_size();
static int initial_code_buffer_size(int const_size = initial_const_capacity);
};
#endif // SHARE_VM_OPTO_C2COMPILER_HPP

View File

@ -544,9 +544,7 @@ void Compile::init_scratch_buffer_blob(int const_size) {
ResourceMark rm;
_scratch_const_size = const_size;
int locs_size = sizeof(relocInfo) * MAX_locs_size;
int slop = 2 * CodeSection::end_slop(); // space between sections
int size = (MAX_inst_size + MAX_stubs_size + _scratch_const_size + slop + locs_size);
int size = C2Compiler::initial_code_buffer_size(const_size);
blob = BufferBlob::create("Compile::scratch_buffer", size);
// Record the buffer blob for next time.
set_scratch_buffer_blob(blob);

View File

@ -2116,8 +2116,17 @@ void GraphKit::uncommon_trap(int trap_request,
// We use this to determine if an object is so "fresh" that
// it does not require card marks.
Node* GraphKit::just_allocated_object(Node* current_control) {
if (C->recent_alloc_ctl() == current_control)
return C->recent_alloc_obj();
Node* ctrl = current_control;
// Object::<init> is invoked after allocation, most of invoke nodes
// will be reduced, but a region node is kept in parse time, we check
// the pattern and skip the region node if it degraded to a copy.
if (ctrl != NULL && ctrl->is_Region() && ctrl->req() == 2 &&
ctrl->as_Region()->is_copy()) {
ctrl = ctrl->as_Region()->is_copy();
}
if (C->recent_alloc_ctl() == ctrl) {
return C->recent_alloc_obj();
}
return NULL;
}

View File

@ -994,6 +994,20 @@ class JvmtiClassFileLoadHookPoster : public StackObj {
}
};
bool JvmtiExport::is_early_phase() {
return JvmtiEnvBase::get_phase() <= JVMTI_PHASE_PRIMORDIAL;
}
bool JvmtiExport::has_early_class_hook_env() {
JvmtiEnvIterator it;
for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) {
if (env->early_class_hook_env()) {
return true;
}
}
return false;
}
bool JvmtiExport::_should_post_class_file_load_hook = false;
// this entry is for class file load hook on class load, redefine and retransform

View File

@ -328,6 +328,8 @@ class JvmtiExport : public AllStatic {
JVMTI_ONLY(return _should_post_class_file_load_hook);
NOT_JVMTI(return false;)
}
static bool is_early_phase();
static bool has_early_class_hook_env();
// Return true if the class was modified by the hook.
static bool post_class_file_load_hook(Symbol* h_name, Handle class_loader,
Handle h_protection_domain,

View File

@ -46,6 +46,13 @@
#include "utilities/events.hpp"
#include "utilities/globalDefinitions.hpp"
#ifdef COMPILER1
#include "c1/c1_Compiler.hpp"
#endif
#ifdef COMPILER2
#include "opto/c2compiler.hpp"
#endif
CompilationPolicy* CompilationPolicy::_policy;
elapsedTimer CompilationPolicy::_accumulated_time;
bool CompilationPolicy::_in_vm_startup;
@ -222,6 +229,19 @@ void NonTieredCompPolicy::initialize() {
// max(log2(8)-1,1) = 2 compiler threads on an 8-way machine.
// May help big-app startup time.
_compiler_count = MAX2(log2_intptr(os::active_processor_count())-1,1);
// Make sure there is enough space in the code cache to hold all the compiler buffers
size_t buffer_size = 1;
#ifdef COMPILER1
buffer_size = is_client_compilation_mode_vm() ? Compiler::code_buffer_size() : buffer_size;
#endif
#ifdef COMPILER2
buffer_size = is_server_compilation_mode_vm() ? C2Compiler::initial_code_buffer_size() : buffer_size;
#endif
int max_count = (ReservedCodeCacheSize - (CodeCacheMinimumUseSpace DEBUG_ONLY(* 3))) / (int)buffer_size;
if (_compiler_count > max_count) {
// Lower the compiler count such that all buffers fit into the code cache
_compiler_count = MAX2(max_count, 1);
}
FLAG_SET_ERGO(intx, CICompilerCount, _compiler_count);
} else {
_compiler_count = CICompilerCount;

View File

@ -41,7 +41,6 @@
class HandshakeOperation: public StackObj {
public:
virtual void do_handshake(JavaThread* thread) = 0;
virtual void cancel_handshake(JavaThread* thread) = 0;
};
class HandshakeThreadsOperation: public HandshakeOperation {
@ -51,8 +50,6 @@ class HandshakeThreadsOperation: public HandshakeOperation {
public:
HandshakeThreadsOperation(ThreadClosure* cl) : _thread_cl(cl) {}
void do_handshake(JavaThread* thread);
void cancel_handshake(JavaThread* thread) { _done.signal(); };
bool thread_has_completed() { return _done.trywait(); }
#ifdef ASSERT
@ -121,15 +118,11 @@ class VM_HandshakeOneThread: public VM_Handshake {
DEBUG_ONLY(_op->check_state();)
TraceTime timer("Performing single-target operation (vmoperation doit)", TRACETIME_LOG(Info, handshake));
{
ThreadsListHandle tlh;
if (tlh.includes(_target)) {
set_handshake(_target);
_thread_alive = true;
}
}
if (!_thread_alive) {
ThreadsListHandle tlh;
if (tlh.includes(_target)) {
set_handshake(_target);
_thread_alive = true;
} else {
return;
}
@ -147,20 +140,9 @@ class VM_HandshakeOneThread: public VM_Handshake {
// We need to re-think this with SMR ThreadsList.
// There is an assumption in the code that the Threads_lock should be
// locked during certain phases.
MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag);
ThreadsListHandle tlh;
if (tlh.includes(_target)) {
// Warning _target's address might be re-used.
// handshake_process_by_vmthread will check the semaphore for us again.
// Since we can't have more then one handshake in flight a reuse of
// _target's address should be okay since the new thread will not have
// an operation.
{
MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag);
_target->handshake_process_by_vmthread();
} else {
// We can't warn here since the thread does cancel_handshake after
// it has been removed from the ThreadsList. So we should just keep
// looping here until while below returns false. If we have a bug,
// then we hang here, which is good for debugging.
}
} while (!poll_for_completed_thread());
DEBUG_ONLY(_op->check_state();)
@ -179,8 +161,9 @@ class VM_HandshakeAllThreads: public VM_Handshake {
DEBUG_ONLY(_op->check_state();)
TraceTime timer("Performing operation (vmoperation doit)", TRACETIME_LOG(Info, handshake));
JavaThreadIteratorWithHandle jtiwh;
int number_of_threads_issued = 0;
for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thr = jtiwh.next(); ) {
for (JavaThread *thr = jtiwh.next(); thr != NULL; thr = jtiwh.next()) {
set_handshake(thr);
number_of_threads_issued++;
}
@ -210,8 +193,9 @@ class VM_HandshakeAllThreads: public VM_Handshake {
// We need to re-think this with SMR ThreadsList.
// There is an assumption in the code that the Threads_lock should
// be locked during certain phases.
jtiwh.rewind();
MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag);
for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thr = jtiwh.next(); ) {
for (JavaThread *thr = jtiwh.next(); thr != NULL; thr = jtiwh.next()) {
// A new thread on the ThreadsList will not have an operation,
// hence it is skipped in handshake_process_by_vmthread.
thr->handshake_process_by_vmthread();
@ -262,7 +246,11 @@ void HandshakeThreadsOperation::do_handshake(JavaThread* thread) {
FormatBufferResource message("Operation for thread " PTR_FORMAT ", is_vm_thread: %s",
p2i(thread), BOOL_TO_STR(Thread::current()->is_VM_thread()));
TraceTime timer(message, TRACETIME_LOG(Debug, handshake, task));
_thread_cl->do_thread(thread);
// Only actually execute the operation for non terminated threads.
if (!thread->is_terminated()) {
_thread_cl->do_thread(thread);
}
// Use the semaphore to inform the VM thread that we have completed the operation
_done.signal();
@ -306,12 +294,7 @@ void HandshakeState::clear_handshake(JavaThread* target) {
void HandshakeState::process_self_inner(JavaThread* thread) {
assert(Thread::current() == thread, "should call from thread");
if (thread->is_terminated()) {
// If thread is not on threads list but armed, cancel.
thread->cancel_handshake();
return;
}
assert(!thread->is_terminated(), "should not be a terminated thread");
CautiouslyPreserveExceptionMark pem(thread);
ThreadInVMForHandshake tivm(thread);
@ -327,16 +310,6 @@ void HandshakeState::process_self_inner(JavaThread* thread) {
_semaphore.signal();
}
void HandshakeState::cancel_inner(JavaThread* thread) {
assert(Thread::current() == thread, "should call from thread");
assert(thread->thread_state() == _thread_in_vm, "must be in vm state");
HandshakeOperation* op = _operation;
clear_handshake(thread);
if (op != NULL) {
op->cancel_handshake(thread);
}
}
bool HandshakeState::vmthread_can_process_handshake(JavaThread* target) {
// SafepointSynchronize::safepoint_safe() does not consider an externally
// suspended thread to be safe. However, this function must be called with
@ -344,7 +317,7 @@ bool HandshakeState::vmthread_can_process_handshake(JavaThread* target) {
// resumed thus it is safe.
assert(Threads_lock->owned_by_self(), "Not holding Threads_lock.");
return SafepointSynchronize::safepoint_safe(target, target->thread_state()) ||
target->is_ext_suspended();
target->is_ext_suspended() || target->is_terminated();
}
static bool possibly_vmthread_can_process_handshake(JavaThread* target) {
@ -355,6 +328,9 @@ static bool possibly_vmthread_can_process_handshake(JavaThread* target) {
if (target->is_ext_suspended()) {
return true;
}
if (target->is_terminated()) {
return true;
}
switch (target->thread_state()) {
case _thread_in_native:
// native threads are safe if they have no java stack or have walkable stack
@ -381,6 +357,8 @@ bool HandshakeState::claim_handshake_for_vmthread() {
void HandshakeState::process_by_vmthread(JavaThread* target) {
assert(Thread::current()->is_VM_thread(), "should call from vm thread");
// Threads_lock must be held here, but that is assert()ed in
// possibly_vmthread_can_process_handshake().
if (!has_operation()) {
// JT has already cleared its handshake
@ -402,7 +380,6 @@ void HandshakeState::process_by_vmthread(JavaThread* target) {
// getting caught by the semaphore.
if (vmthread_can_process_handshake(target)) {
guarantee(!_semaphore.trywait(), "we should already own the semaphore");
_operation->do_handshake(target);
// Disarm after VM thread have executed the operation.
clear_handshake(target);

View File

@ -60,7 +60,6 @@ class HandshakeState {
bool vmthread_can_process_handshake(JavaThread* target);
void clear_handshake(JavaThread* thread);
void cancel_inner(JavaThread* thread);
void process_self_inner(JavaThread* thread);
public:
@ -72,19 +71,13 @@ public:
return _operation != NULL;
}
void cancel(JavaThread* thread) {
if (!_thread_in_process_handshake) {
FlagSetting fs(_thread_in_process_handshake, true);
cancel_inner(thread);
}
}
void process_by_self(JavaThread* thread) {
if (!_thread_in_process_handshake) {
FlagSetting fs(_thread_in_process_handshake, true);
process_self_inner(thread);
}
}
void process_by_vmthread(JavaThread* target);
};

View File

@ -4276,9 +4276,6 @@ bool Threads::destroy_vm() {
before_exit(thread);
thread->exit(true);
// thread will never call smr_delete, instead of implicit cancel
// in wait_for_vm_thread_exit we do it explicit.
thread->cancel_handshake();
// Stop VM thread.
{

View File

@ -1271,10 +1271,6 @@ class JavaThread: public Thread {
return _handshake.has_operation();
}
void cancel_handshake() {
_handshake.cancel(this);
}
void handshake_process_by_self() {
_handshake.process_by_self(this);
}

View File

@ -989,11 +989,6 @@ void ThreadsSMRSupport::smr_delete(JavaThread *thread) {
// Retry the whole scenario.
}
if (ThreadLocalHandshakes) {
// The thread is about to be deleted so cancel any handshake.
thread->cancel_handshake();
}
delete thread;
if (EnableThreadSMRStatistics) {
timer.stop();

View File

@ -38,6 +38,9 @@
#ifdef TIERED
#include "c1/c1_Compiler.hpp"
#include "opto/c2compiler.hpp"
template<CompLevel level>
bool TieredThresholdPolicy::call_predicate_helper(int i, int b, double scale, Method* method) {
double threshold_scaling;
@ -215,6 +218,7 @@ void TieredThresholdPolicy::print_event(EventType type, const methodHandle& mh,
void TieredThresholdPolicy::initialize() {
int count = CICompilerCount;
bool c1_only = TieredStopAtLevel < CompLevel_full_optimization;
#ifdef _LP64
// Turn on ergonomic compiler count selection
if (FLAG_IS_DEFAULT(CICompilerCountPerCPU) && FLAG_IS_DEFAULT(CICompilerCount)) {
@ -225,6 +229,15 @@ void TieredThresholdPolicy::initialize() {
int log_cpu = log2_intptr(os::active_processor_count());
int loglog_cpu = log2_intptr(MAX2(log_cpu, 1));
count = MAX2(log_cpu * loglog_cpu * 3 / 2, 2);
// Make sure there is enough space in the code cache to hold all the compiler buffers
size_t c1_size = Compiler::code_buffer_size();
size_t c2_size = C2Compiler::initial_code_buffer_size();
size_t buffer_size = c1_only ? c1_size : (c1_size/3 + 2*c2_size/3);
int max_count = (ReservedCodeCacheSize - (CodeCacheMinimumUseSpace DEBUG_ONLY(* 3))) / (int)buffer_size;
if (count > max_count) {
// Lower the compiler count such that all buffers fit into the code cache
count = MAX2(max_count, c1_only ? 1 : 2);
}
FLAG_SET_ERGO(intx, CICompilerCount, count);
}
#else
@ -241,7 +254,7 @@ void TieredThresholdPolicy::initialize() {
}
#endif
if (TieredStopAtLevel < CompLevel_full_optimization) {
if (c1_only) {
// No C2 compiler thread required
set_c1_count(count);
} else {

View File

@ -25,6 +25,10 @@
#ifndef SHARE_UTILITIES_CONCURRENT_HASH_TABLE_HPP
#define SHARE_UTILITIES_CONCURRENT_HASH_TABLE_HPP
#include "memory/allocation.hpp"
#include "utilities/globalCounter.hpp"
#include "utilities/globalDefinitions.hpp"
// A mostly concurrent-hash-table where the read-side is wait-free, inserts are
// CAS and deletes mutual exclude each other on per bucket-basis. VALUE is the
// type kept inside each Node and CONFIG contains hash and allocation methods.
@ -247,6 +251,7 @@ class ConcurrentHashTable : public CHeapObj<F> {
protected:
Thread* _thread;
ConcurrentHashTable<VALUE, CONFIG, F>* _cht;
GlobalCounter::CSContext _cs_context;
public:
ScopedCS(Thread* thread, ConcurrentHashTable<VALUE, CONFIG, F>* cht);
~ScopedCS();

View File

@ -208,9 +208,10 @@ inline ConcurrentHashTable<VALUE, CONFIG, F>::
template <typename VALUE, typename CONFIG, MEMFLAGS F>
inline ConcurrentHashTable<VALUE, CONFIG, F>::
ScopedCS::ScopedCS(Thread* thread, ConcurrentHashTable<VALUE, CONFIG, F>* cht)
: _thread(thread), _cht(cht)
: _thread(thread),
_cht(cht),
_cs_context(GlobalCounter::critical_section_begin(_thread))
{
GlobalCounter::critical_section_begin(_thread);
// This version is published now.
if (OrderAccess::load_acquire(&_cht->_invisible_epoch) != NULL) {
OrderAccess::release_store_fence(&_cht->_invisible_epoch, (Thread*)NULL);
@ -221,7 +222,7 @@ template <typename VALUE, typename CONFIG, MEMFLAGS F>
inline ConcurrentHashTable<VALUE, CONFIG, F>::
ScopedCS::~ScopedCS()
{
GlobalCounter::critical_section_end(_thread);
GlobalCounter::critical_section_end(_thread, _cs_context);
}
// BaseConfig
@ -502,7 +503,7 @@ inline void ConcurrentHashTable<VALUE, CONFIG, F>::
// concurrent single deletes. The _invisible_epoch can only be used by the
// owner of _resize_lock, us here. There we should not changed it in our
// own read-side.
GlobalCounter::critical_section_begin(thread);
GlobalCounter::CSContext cs_context = GlobalCounter::critical_section_begin(thread);
for (size_t bucket_it = start_idx; bucket_it < stop_idx; bucket_it++) {
Bucket* bucket = table->get_bucket(bucket_it);
Bucket* prefetch_bucket = (bucket_it+1) < stop_idx ?
@ -514,7 +515,7 @@ inline void ConcurrentHashTable<VALUE, CONFIG, F>::
continue;
}
GlobalCounter::critical_section_end(thread);
GlobalCounter::critical_section_end(thread, cs_context);
// We left critical section but the bucket cannot be removed while we hold
// the _resize_lock.
bucket->lock();
@ -530,9 +531,9 @@ inline void ConcurrentHashTable<VALUE, CONFIG, F>::
Node::destroy_node(ndel[node_it]);
DEBUG_ONLY(ndel[node_it] = (Node*)POISON_PTR;)
}
GlobalCounter::critical_section_begin(thread);
cs_context = GlobalCounter::critical_section_begin(thread);
}
GlobalCounter::critical_section_end(thread);
GlobalCounter::critical_section_end(thread, cs_context);
}
template <typename VALUE, typename CONFIG, MEMFLAGS F>

View File

@ -59,8 +59,8 @@ class GlobalCounter::CounterThreadCheck : public ThreadClosure {
void GlobalCounter::write_synchronize() {
assert((*Thread::current()->get_rcu_counter() & COUNTER_ACTIVE) == 0x0, "must be outside a critcal section");
// Atomic::add must provide fence since we have storeload dependency.
volatile uintx gbl_cnt = Atomic::add((uintx)COUNTER_INCREMENT, &_global_counter._counter,
memory_order_conservative);
uintx gbl_cnt = Atomic::add(COUNTER_INCREMENT, &_global_counter._counter);
// Handle bootstrap
if (Threads::number_of_threads() == 0) {
return;

View File

@ -33,8 +33,9 @@ class Thread;
// The GlobalCounter provides a synchronization mechanism between threads for
// safe memory reclamation and other ABA problems. All readers must call
// critical_section_begin before reading the volatile data and
// critical_section_end afterwards. The write side must call write_synchronize
// before reclaming the memory. The read-path only does an uncontented store
// critical_section_end afterwards. Such read-side critical sections may
// be properly nested. The write side must call write_synchronize
// before reclaming the memory. The read-path only does an uncontended store
// to a thread-local-storage and fence to stop any loads from floating up, thus
// light weight and wait-free. The write-side is more heavy since it must check
// all readers and wait until they have left the generation. (a system memory
@ -62,20 +63,26 @@ class GlobalCounter : public AllStatic {
class CounterThreadCheck;
public:
// Must be called before accessing the data. Only threads accessible lock-free
// can used this. Those included now are all Threads on SMR ThreadsList and
// the VMThread. Nesting is not yet supported.
static void critical_section_begin(Thread *thread);
// The type of the critical section context passed from
// critical_section_begin() to critical_section_end().
typedef uintx CSContext;
// Must be called after finished accessing the data.
// Do not provide fence, allows load/stores moving into the critical section.
static void critical_section_end(Thread *thread);
// Must be called before accessing the data. The result must be passed
// to the associated call to critical_section_end(). Acts as a full
// memory barrier before the code within the critical section.
static CSContext critical_section_begin(Thread *thread);
// Must be called after finished accessing the data. The context
// must be the result of the associated initiating critical_section_begin().
// Acts as a release memory barrier after the code within the critical
// section.
static void critical_section_end(Thread *thread, CSContext context);
// Make the data inaccessible to readers before calling. When this call
// returns it's safe to reclaim the data.
// returns it's safe to reclaim the data. Acts as a full memory barrier.
static void write_synchronize();
// A scoped object for a reads-side critical-section.
// A scoped object for a read-side critical-section.
class CriticalSection;
};

View File

@ -25,34 +25,46 @@
#ifndef SHARE_UTILITIES_GLOBAL_COUNTER_INLINE_HPP
#define SHARE_UTILITIES_GLOBAL_COUNTER_INLINE_HPP
#include "runtime/atomic.hpp"
#include "runtime/orderAccess.hpp"
#include "runtime/thread.inline.hpp"
#include "utilities/globalCounter.hpp"
inline void GlobalCounter::critical_section_begin(Thread *thread) {
inline GlobalCounter::CSContext
GlobalCounter::critical_section_begin(Thread *thread) {
assert(thread == Thread::current(), "must be current thread");
assert((*thread->get_rcu_counter() & COUNTER_ACTIVE) == 0x0, "nested critical sections, not supported yet");
uintx gbl_cnt = OrderAccess::load_acquire(&_global_counter._counter);
OrderAccess::release_store_fence(thread->get_rcu_counter(), gbl_cnt | COUNTER_ACTIVE);
uintx old_cnt = Atomic::load(thread->get_rcu_counter());
// Retain the old counter value if already active, e.g. nested.
// Otherwise, set the counter to the current version + active bit.
uintx new_cnt = old_cnt;
if ((new_cnt & COUNTER_ACTIVE) == 0) {
new_cnt = Atomic::load(&_global_counter._counter) | COUNTER_ACTIVE;
}
OrderAccess::release_store_fence(thread->get_rcu_counter(), new_cnt);
return static_cast<CSContext>(old_cnt);
}
inline void GlobalCounter::critical_section_end(Thread *thread) {
inline void
GlobalCounter::critical_section_end(Thread *thread, CSContext context) {
assert(thread == Thread::current(), "must be current thread");
assert((*thread->get_rcu_counter() & COUNTER_ACTIVE) == COUNTER_ACTIVE, "must be in critical section");
// Mainly for debugging we set it to 'now'.
uintx gbl_cnt = OrderAccess::load_acquire(&_global_counter._counter);
OrderAccess::release_store(thread->get_rcu_counter(), gbl_cnt);
// Restore the counter value from before the associated begin.
OrderAccess::release_store(thread->get_rcu_counter(),
static_cast<uintx>(context));
}
class GlobalCounter::CriticalSection {
private:
Thread* _thread;
CSContext _context;
public:
inline CriticalSection(Thread* thread) : _thread(thread) {
GlobalCounter::critical_section_begin(_thread);
}
inline CriticalSection(Thread* thread) :
_thread(thread),
_context(GlobalCounter::critical_section_begin(_thread))
{}
inline ~CriticalSection() {
GlobalCounter::critical_section_end(_thread);
GlobalCounter::critical_section_end(_thread, _context);
}
};

View File

@ -43,8 +43,6 @@ void InternalVMTests::run_test(const char* name, void (*test)()) {
void InternalVMTests::run() {
tty->print_cr("Running internal VM tests");
run_unit_test(TestReserveMemorySpecial_test);
run_unit_test(TestMetaspaceUtils_test);
run_unit_test(GCTimer_test);
tty->print_cr("All internal VM tests passed");
}

View File

@ -36,7 +36,7 @@
// Any number of threads may enter critical sections associated with a
// synchronizer object. One (at a time) other thread may wait for the
// completion of all critical sections for the synchronizer object
// that were extent when the wait was initiated. Usage is that there
// that were extant when the wait was initiated. Usage is that there
// is some state that can be accessed either before or after some
// change. An accessing thread performs the access within a critical
// section. A writer thread performs the state change, and then waits
@ -46,9 +46,7 @@
// Generally, GlobalCounter should be used instead of this class, as
// GlobalCounter has measurably better performance and doesn't have
// the single writer at a time restriction. Use this only in
// situations where GlobalCounter won't work for some reason, such as
// nesting. But note that nesting often indicates other problems, and
// may risk deadlock.
// situations where GlobalCounter won't work for some reason.
class SingleWriterSynchronizer {
volatile uint _enter;
volatile uint _exit[2];

View File

@ -233,7 +233,7 @@ class TimeInstant : public Rep<TimeSource> {
TimeInstant(jlong ticks) : Rep<TimeSource>(ticks) {}
friend class GranularTimer;
friend class ObjectSample;
// GC VM tests
// GC unit tests
friend class TimePartitionPhasesIteratorTest;
friend class GCTimerTest;
};

View File

@ -37,119 +37,104 @@ final class StringConcatHelper {
}
/**
* Check for overflow, throw the exception on overflow.
* @param len String length
* @return length
* Check for overflow, throw exception on overflow.
* @param lengthCoder String length and coder
* @return lengthCoder
*/
private static int checkOverflow(int len) {
if (len < 0) {
throw new OutOfMemoryError("Overflow: String length out of range");
private static long checkOverflow(long lengthCoder) {
if ((int)lengthCoder >= 0) {
return lengthCoder;
}
return len;
throw new OutOfMemoryError("Overflow: String length out of range");
}
/**
* Mix value length into current length
* Mix value length and coder into current length and coder.
* @param current current length
* @param value value to mix in
* @return new length
* @return new length and coder
*/
static int mixLen(int current, boolean value) {
static long mix(long current, boolean value) {
return checkOverflow(current + (value ? 4 : 5));
}
/**
* Mix value length into current length
* Mix value length and coder into current length and coder.
* @param current current length
* @param value value to mix in
* @return new length
* @return new length and coder
*/
static int mixLen(int current, byte value) {
return mixLen(current, (int)value);
static long mix(long current, byte value) {
return mix(current, (int)value);
}
/**
* Mix value length into current length
* Mix value length and coder into current length and coder.
* @param current current length
* @param value value to mix in
* @return new length
* @return new length and coder
*/
static int mixLen(int current, char value) {
return checkOverflow(current + 1);
static long mix(long current, char value) {
return checkOverflow(current + 1) | (StringLatin1.canEncode(value) ? 0 : UTF16);
}
/**
* Mix value length into current length
* Mix value length and coder into current length and coder.
* @param current current length
* @param value value to mix in
* @return new length
* @return new length and coder
*/
static int mixLen(int current, short value) {
return mixLen(current, (int)value);
static long mix(long current, short value) {
return mix(current, (int)value);
}
/**
* Mix value length into current length
* Mix value length and coder into current length and coder.
* @param current current length
* @param value value to mix in
* @return new length
* @return new length and coder
*/
static int mixLen(int current, int value) {
static long mix(long current, int value) {
return checkOverflow(current + Integer.stringSize(value));
}
/**
* Mix value length into current length
* Mix value length and coder into current length and coder.
* @param current current length
* @param value value to mix in
* @return new length
* @return new length and coder
*/
static int mixLen(int current, long value) {
static long mix(long current, long value) {
return checkOverflow(current + Long.stringSize(value));
}
/**
* Mix value length into current length
* Mix value length and coder into current length and coder.
* @param current current length
* @param value value to mix in
* @return new length
* @return new length and coder
*/
static int mixLen(int current, String value) {
return checkOverflow(current + value.length());
}
/**
* Mix coder into current coder
* @param current current coder
* @param value value to mix in
* @return new coder
*/
static byte mixCoder(byte current, char value) {
return (byte)(current | (StringLatin1.canEncode(value) ? 0 : 1));
}
/**
* Mix coder into current coder
* @param current current coder
* @param value value to mix in
* @return new coder
*/
static byte mixCoder(byte current, String value) {
return (byte)(current | value.coder());
static long mix(long current, String value) {
current += value.length();
if (value.coder() == String.UTF16) {
current |= UTF16;
}
return checkOverflow(current);
}
/**
* Prepends the stringly representation of boolean value into buffer,
* given the coder and final index. Index is measured in chars, not in bytes!
*
* @param index final char index in the buffer
* @param buf buffer to append to
* @param coder coder to add with
* @param value boolean value to encode
* @return new index
* @param indexCoder final char index in the buffer, along with coder packed
* into higher bits.
* @param buf buffer to append to
* @param value boolean value to encode
* @return updated index (coder value retained)
*/
static int prepend(int index, byte[] buf, byte coder, boolean value) {
if (coder == String.LATIN1) {
static long prepend(long indexCoder, byte[] buf, boolean value) {
int index = (int)indexCoder;
if (indexCoder < UTF16) {
if (value) {
buf[--index] = 'e';
buf[--index] = 'u';
@ -162,6 +147,7 @@ final class StringConcatHelper {
buf[--index] = 'a';
buf[--index] = 'f';
}
return index;
} else {
if (value) {
StringUTF16.putChar(buf, --index, 'e');
@ -175,72 +161,72 @@ final class StringConcatHelper {
StringUTF16.putChar(buf, --index, 'a');
StringUTF16.putChar(buf, --index, 'f');
}
return index | UTF16;
}
return index;
}
/**
* Prepends the stringly representation of byte value into buffer,
* given the coder and final index. Index is measured in chars, not in bytes!
*
* @param index final char index in the buffer
* @param buf buffer to append to
* @param coder coder to add with
* @param value byte value to encode
* @return new index
* @param indexCoder final char index in the buffer, along with coder packed
* into higher bits.
* @param buf buffer to append to
* @param value byte value to encode
* @return updated index (coder value retained)
*/
static int prepend(int index, byte[] buf, byte coder, byte value) {
return prepend(index, buf, coder, (int)value);
static long prepend(long indexCoder, byte[] buf, byte value) {
return prepend(indexCoder, buf, (int)value);
}
/**
* Prepends the stringly representation of char value into buffer,
* given the coder and final index. Index is measured in chars, not in bytes!
*
* @param index final char index in the buffer
* @param buf buffer to append to
* @param coder coder to add with
* @param value char value to encode
* @return new index
* @param indexCoder final char index in the buffer, along with coder packed
* into higher bits.
* @param buf buffer to append to
* @param value char value to encode
* @return updated index (coder value retained)
*/
static int prepend(int index, byte[] buf, byte coder, char value) {
if (coder == String.LATIN1) {
buf[--index] = (byte) (value & 0xFF);
static long prepend(long indexCoder, byte[] buf, char value) {
if (indexCoder < UTF16) {
buf[(int)(--indexCoder)] = (byte) (value & 0xFF);
} else {
StringUTF16.putChar(buf, --index, value);
StringUTF16.putChar(buf, (int)(--indexCoder), value);
}
return index;
return indexCoder;
}
/**
* Prepends the stringly representation of short value into buffer,
* given the coder and final index. Index is measured in chars, not in bytes!
*
* @param index final char index in the buffer
* @param buf buffer to append to
* @param coder coder to add with
* @param value short value to encode
* @return new index
* @param indexCoder final char index in the buffer, along with coder packed
* into higher bits.
* @param buf buffer to append to
* @param value short value to encode
* @return updated index (coder value retained)
*/
static int prepend(int index, byte[] buf, byte coder, short value) {
return prepend(index, buf, coder, (int)value);
static long prepend(long indexCoder, byte[] buf, short value) {
return prepend(indexCoder, buf, (int)value);
}
/**
* Prepends the stringly representation of integer value into buffer,
* given the coder and final index. Index is measured in chars, not in bytes!
*
* @param index final char index in the buffer
* @param buf buffer to append to
* @param coder coder to add with
* @param value integer value to encode
* @return new index
* @param indexCoder final char index in the buffer, along with coder packed
* into higher bits.
* @param buf buffer to append to
* @param value integer value to encode
* @return updated index (coder value retained)
*/
static int prepend(int index, byte[] buf, byte coder, int value) {
if (coder == String.LATIN1) {
return Integer.getChars(value, index, buf);
static long prepend(long indexCoder, byte[] buf, int value) {
if (indexCoder < UTF16) {
return Integer.getChars(value, (int)indexCoder, buf);
} else {
return StringUTF16.getChars(value, index, buf);
return StringUTF16.getChars(value, (int)indexCoder, buf) | UTF16;
}
}
@ -248,17 +234,17 @@ final class StringConcatHelper {
* Prepends the stringly representation of long value into buffer,
* given the coder and final index. Index is measured in chars, not in bytes!
*
* @param index final char index in the buffer
* @param buf buffer to append to
* @param coder coder to add with
* @param value long value to encode
* @return new index
* @param indexCoder final char index in the buffer, along with coder packed
* into higher bits.
* @param buf buffer to append to
* @param value long value to encode
* @return updated index (coder value retained)
*/
static int prepend(int index, byte[] buf, byte coder, long value) {
if (coder == String.LATIN1) {
return Long.getChars(value, index, buf);
static long prepend(long indexCoder, byte[] buf, long value) {
if (indexCoder < UTF16) {
return Long.getChars(value, (int)indexCoder, buf);
} else {
return StringUTF16.getChars(value, index, buf);
return StringUTF16.getChars(value, (int)indexCoder, buf) | UTF16;
}
}
@ -266,39 +252,49 @@ final class StringConcatHelper {
* Prepends the stringly representation of String value into buffer,
* given the coder and final index. Index is measured in chars, not in bytes!
*
* @param index final char index in the buffer
* @param buf buffer to append to
* @param coder coder to add with
* @param value String value to encode
* @return new index
* @param indexCoder final char index in the buffer, along with coder packed
* into higher bits.
* @param buf buffer to append to
* @param value String value to encode
* @return updated index (coder value retained)
*/
static int prepend(int index, byte[] buf, byte coder, String value) {
index -= value.length();
value.getBytes(buf, index, coder);
return index;
static long prepend(long indexCoder, byte[] buf, String value) {
indexCoder -= value.length();
if (indexCoder < UTF16) {
value.getBytes(buf, (int)indexCoder, String.LATIN1);
} else {
value.getBytes(buf, (int)indexCoder, String.UTF16);
}
return indexCoder;
}
/**
* Instantiates the String with given buffer and coder
* @param buf buffer to use
* @param index remaining index
* @param coder coder to use
* @return String resulting string
* @param buf buffer to use
* @param indexCoder remaining index (should be zero) and coder
* @return String resulting string
*/
static String newString(byte[] buf, int index, byte coder) {
static String newString(byte[] buf, long indexCoder) {
// Use the private, non-copying constructor (unsafe!)
if (index != 0) {
throw new InternalError("Storage is not completely initialized, " + index + " bytes left");
if (indexCoder == LATIN1) {
return new String(buf, String.LATIN1);
} else if (indexCoder == UTF16) {
return new String(buf, String.UTF16);
} else {
throw new InternalError("Storage is not completely initialized, " + (int)indexCoder + " bytes left");
}
return new String(buf, coder);
}
private static final long LATIN1 = (long)String.LATIN1 << 32;
private static final long UTF16 = (long)String.UTF16 << 32;
/**
* Provides the initial coder for the String.
* @return initial coder
* @return initial coder, adjusted into the upper half
*/
static byte initialCoder() {
return String.COMPACT_STRINGS ? String.LATIN1 : String.UTF16;
static long initialCoder() {
return String.COMPACT_STRINGS ? LATIN1 : UTF16;
}
}

View File

@ -317,6 +317,13 @@ public final class StringConcatFactory {
return elements.equals(recipe.elements);
}
@Override
public String toString() {
return "Recipe{" +
"elements=" + elements +
'}';
}
@Override
public int hashCode() {
return elements.hashCode();
@ -367,6 +374,15 @@ public final class StringConcatFactory {
return true;
}
@Override
public String toString() {
return "RecipeElement{" +
"value='" + value + '\'' +
", argPos=" + argPos +
", tag=" + tag +
'}';
}
@Override
public int hashCode() {
return (int)tag;
@ -1520,24 +1536,24 @@ public final class StringConcatFactory {
}
// Start building the combinator tree. The tree "starts" with (<parameters>)String, and "finishes"
// with the (int, byte[], byte)String in String helper. The combinators are assembled bottom-up,
// which makes the code arguably hard to read.
// with the (byte[], long)String shape to invoke newString in StringConcatHelper. The combinators are
// assembled bottom-up, which makes the code arguably hard to read.
// Drop all remaining parameter types, leave only helper arguments:
MethodHandle mh;
mh = MethodHandles.dropArguments(NEW_STRING, 3, ptypes);
mh = MethodHandles.dropArguments(NEW_STRING, 2, ptypes);
// Mix in prependers. This happens when (byte[], int, byte) = (storage, index, coder) is already
// known from the combinators below. We are assembling the string backwards, so "index" is the
// *ending* index.
// Mix in prependers. This happens when (byte[], long) = (storage, indexCoder) is already
// known from the combinators below. We are assembling the string backwards, so the index coded
// into indexCoder is the *ending* index.
for (RecipeElement el : recipe.getElements()) {
// Do the prepend, and put "new" index at index 1
switch (el.getTag()) {
case TAG_CONST: {
MethodHandle prepender = MethodHandles.insertArguments(prepender(String.class), 3, el.getValue());
MethodHandle prepender = MethodHandles.insertArguments(prepender(String.class), 2, el.getValue());
mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, prepender,
1, 0, 2 // index, storage, coder
1, 0 // indexCoder, storage
);
break;
}
@ -1545,8 +1561,8 @@ public final class StringConcatFactory {
int pos = el.getArgPos();
MethodHandle prepender = prepender(ptypes[pos]);
mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, prepender,
1, 0, 2, // index, storage, coder
3 + pos // selected argument
1, 0, // indexCoder, storage
2 + pos // selected argument
);
break;
}
@ -1557,7 +1573,7 @@ public final class StringConcatFactory {
// Fold in byte[] instantiation at argument 0
mh = MethodHandles.foldArgumentsWithCombiner(mh, 0, NEW_ARRAY,
1, 2 // index, coder
1 // index
);
// Start combining length and coder mixers.
@ -1569,47 +1585,28 @@ public final class StringConcatFactory {
// Coders are more interesting. Only Object, String and char arguments (and constants)
// can have non-Latin1 encoding. It is easier to blindly convert constants to String,
// and deduce the coder from there. Arguments would be either converted to Strings
// during the initial filtering, or handled by primitive specializations in CODER_MIXERS.
// during the initial filtering, or handled by specializations in MIXERS.
//
// The method handle shape before and after all length and coder mixers is:
// (int, byte, <args>)String = ("index", "coder", <args>)
byte initialCoder = INITIAL_CODER;
int initialLen = 0; // initial length, in characters
// The method handle shape before and after all mixers are combined in is:
// (long, <args>)String = ("indexCoder", <args>)
long initialLengthCoder = INITIAL_CODER;
for (RecipeElement el : recipe.getElements()) {
switch (el.getTag()) {
case TAG_CONST:
String constant = el.getValue();
initialCoder = (byte) coderMixer(String.class).invoke(initialCoder, constant);
initialLen += constant.length();
initialLengthCoder = (long)mixer(String.class).invoke(initialLengthCoder, constant);
break;
case TAG_ARG:
int ac = el.getArgPos();
Class<?> argClass = ptypes[ac];
MethodHandle lm = lengthMixer(argClass);
MethodHandle mix = mixer(argClass);
if (argClass.isPrimitive() && argClass != char.class) {
// Compute new "index" in-place using old value plus the appropriate argument.
mh = MethodHandles.filterArgumentsWithCombiner(mh, 0, lm,
0, // old-index
2 + ac // selected argument
);
} else {
MethodHandle cm = coderMixer(argClass);
// Compute new "index" in-place using old value plus the appropriate argument.
mh = MethodHandles.filterArgumentsWithCombiner(mh, 0, lm,
0, // old-index
2 + ac // selected argument
);
// Compute new "coder" in-place using old value plus the appropriate argument.
mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, cm,
1, // old-coder
2 + ac // selected argument
);
}
// Compute new "index" in-place using old value plus the appropriate argument.
mh = MethodHandles.filterArgumentsWithCombiner(mh, 0, mix,
0, // old-index
1 + ac // selected argument
);
break;
default:
@ -1617,9 +1614,9 @@ public final class StringConcatFactory {
}
}
// Insert initial lengths and coders here.
// Insert initial length and coder value here.
// The method handle shape here is (<args>).
mh = MethodHandles.insertArguments(mh, 0, initialLen, initialCoder);
mh = MethodHandles.insertArguments(mh, 0, initialLengthCoder);
// Apply filters, converting the arguments:
if (filters != null) {
@ -1630,45 +1627,34 @@ public final class StringConcatFactory {
}
@ForceInline
private static byte[] newArray(int length, byte coder) {
return (byte[]) UNSAFE.allocateUninitializedArray(byte.class, length << coder);
private static byte[] newArray(long indexCoder) {
byte coder = (byte)(indexCoder >> 32);
int index = ((int)indexCoder & 0x7FFFFFFF);
return (byte[]) UNSAFE.allocateUninitializedArray(byte.class, index << coder);
}
private static MethodHandle prepender(Class<?> cl) {
return PREPENDERS.computeIfAbsent(cl, PREPEND);
}
private static MethodHandle coderMixer(Class<?> cl) {
return CODER_MIXERS.computeIfAbsent(cl, CODER_MIX);
}
private static MethodHandle lengthMixer(Class<?> cl) {
return LENGTH_MIXERS.computeIfAbsent(cl, LENGTH_MIX);
private static MethodHandle mixer(Class<?> cl) {
return MIXERS.computeIfAbsent(cl, MIX);
}
// This one is deliberately non-lambdified to optimize startup time:
private static final Function<Class<?>, MethodHandle> PREPEND = new Function<Class<?>, MethodHandle>() {
@Override
public MethodHandle apply(Class<?> c) {
return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "prepend", int.class, int.class, byte[].class, byte.class,
return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "prepend", long.class, long.class, byte[].class,
Wrapper.asPrimitiveType(c));
}
};
// This one is deliberately non-lambdified to optimize startup time:
private static final Function<Class<?>, MethodHandle> CODER_MIX = new Function<Class<?>, MethodHandle>() {
private static final Function<Class<?>, MethodHandle> MIX = new Function<Class<?>, MethodHandle>() {
@Override
public MethodHandle apply(Class<?> c) {
return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mixCoder", byte.class, byte.class,
Wrapper.asPrimitiveType(c));
}
};
// This one is deliberately non-lambdified to optimize startup time:
private static final Function<Class<?>, MethodHandle> LENGTH_MIX = new Function<Class<?>, MethodHandle>() {
@Override
public MethodHandle apply(Class<?> c) {
return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mixLen", int.class, int.class,
return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mix", long.class, long.class,
Wrapper.asPrimitiveType(c));
}
};
@ -1676,26 +1662,24 @@ public final class StringConcatFactory {
private static final MethodHandle NEW_STRING;
private static final MethodHandle NEW_ARRAY;
private static final ConcurrentMap<Class<?>, MethodHandle> PREPENDERS;
private static final ConcurrentMap<Class<?>, MethodHandle> LENGTH_MIXERS;
private static final ConcurrentMap<Class<?>, MethodHandle> CODER_MIXERS;
private static final byte INITIAL_CODER;
private static final ConcurrentMap<Class<?>, MethodHandle> MIXERS;
private static final long INITIAL_CODER;
static final Class<?> STRING_HELPER;
static {
try {
STRING_HELPER = Class.forName("java.lang.StringConcatHelper");
MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", byte.class);
INITIAL_CODER = (byte) initCoder.invoke();
MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", long.class);
INITIAL_CODER = (long) initCoder.invoke();
} catch (Throwable e) {
throw new AssertionError(e);
}
PREPENDERS = new ConcurrentHashMap<>();
LENGTH_MIXERS = new ConcurrentHashMap<>();
CODER_MIXERS = new ConcurrentHashMap<>();
MIXERS = new ConcurrentHashMap<>();
NEW_STRING = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newString", String.class, byte[].class, int.class, byte.class);
NEW_ARRAY = lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleInlineCopyStrategy.class, "newArray", byte[].class, int.class, byte.class);
NEW_STRING = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newString", String.class, byte[].class, long.class);
NEW_ARRAY = lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleInlineCopyStrategy.class, "newArray", byte[].class, long.class);
}
}

View File

@ -92,6 +92,11 @@ import sun.util.calendar.CalendarDate;
* Japan introduced the Gregorian calendar starting with Meiji 6.
* Only Meiji and later eras are supported;
* dates before Meiji 6, January 1 are not supported.
* The number of the valid eras may increase, as new eras may be
* defined by the Japanese government. Once an era is defined,
* subsequent versions of this class will add a singleton instance
* for it. The defined era is expected to have a consecutive integer
* associated with it.
*
* @implSpec
* This class is immutable and thread-safe.
@ -195,9 +200,13 @@ public final class JapaneseEra
/**
* Obtains an instance of {@code JapaneseEra} from an {@code int} value.
* <p>
* The {@link #SHOWA} era that contains 1970-01-01 (ISO calendar system) has the value 1
* The {@link #SHOWA} era that contains 1970-01-01 (ISO calendar system) has the value 1.
* Later era is numbered 2 ({@link #HEISEI}). Earlier eras are numbered 0 ({@link #TAISHO}),
* -1 ({@link #MEIJI}), only Meiji and later eras are supported.
* <p>
* In addition to the known era singletons, values for additional
* eras may be defined. Those values are the {@link Era#getValue()}
* of corresponding eras from the {@link #values()} method.
*
* @param japaneseEra the era to represent
* @return the {@code JapaneseEra} singleton, not null
@ -216,6 +225,8 @@ public final class JapaneseEra
* <p>
* The string must match exactly the name of the era.
* (Extraneous whitespace characters are not permitted.)
* <p>
* Valid era names are the names of eras returned from {@link #values()}.
*
* @param japaneseEra the japaneseEra name; non-null
* @return the {@code JapaneseEra} singleton, never null
@ -232,7 +243,9 @@ public final class JapaneseEra
}
/**
* Returns an array of JapaneseEras.
* Returns an array of JapaneseEras. The array may contain eras defined
* by the Japanese government beyond the known era singletons.
*
* <p>
* This method may be used to iterate over the JapaneseEras as follows:
* <pre>

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2018, 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
@ -582,16 +582,22 @@ public class KeyAgreement {
/**
* Generates the shared secret and returns it in a new buffer.
*
* <p>This method resets this {@code KeyAgreement} object, so that it
* can be reused for further key agreements. Unless this key agreement is
* reinitialized with one of the {@code init} methods, the same
* private information and algorithm parameters will be used for
* subsequent key agreements.
* <p>This method resets this {@code KeyAgreement} object to the state that
* it was in after the most recent call to one of the {@code init} methods.
* After a call to {@code generateSecret}, the object can be reused for
* further key agreement operations by calling {@code doPhase} to supply
* new keys, and then calling {@code generateSecret} to produce a new
* secret. In this case, the private information and algorithm parameters
* supplied to {@code init} will be used for multiple key agreement
* operations. The {@code init} method can be called after
* {@code generateSecret} to change the private information used in
* subsequent operations.
*
* @return the new buffer with the shared secret
*
* @exception IllegalStateException if this key agreement has not been
* completed yet
* initialized or if {@code doPhase} has not been called to supply the
* keys for all parties in the agreement
*/
public final byte[] generateSecret() throws IllegalStateException {
chooseFirstProvider();
@ -606,11 +612,16 @@ public class KeyAgreement {
* result, a {@code ShortBufferException} is thrown.
* In this case, this call should be repeated with a larger output buffer.
*
* <p>This method resets this {@code KeyAgreement} object, so that it
* can be reused for further key agreements. Unless this key agreement is
* reinitialized with one of the {@code init} methods, the same
* private information and algorithm parameters will be used for
* subsequent key agreements.
* <p>This method resets this {@code KeyAgreement} object to the state that
* it was in after the most recent call to one of the {@code init} methods.
* After a call to {@code generateSecret}, the object can be reused for
* further key agreement operations by calling {@code doPhase} to supply
* new keys, and then calling {@code generateSecret} to produce a new
* secret. In this case, the private information and algorithm parameters
* supplied to {@code init} will be used for multiple key agreement
* operations. The {@code init} method can be called after
* {@code generateSecret} to change the private information used in
* subsequent operations.
*
* @param sharedSecret the buffer for the shared secret
* @param offset the offset in {@code sharedSecret} where the
@ -619,7 +630,8 @@ public class KeyAgreement {
* @return the number of bytes placed into {@code sharedSecret}
*
* @exception IllegalStateException if this key agreement has not been
* completed yet
* initialized or if {@code doPhase} has not been called to supply the
* keys for all parties in the agreement
* @exception ShortBufferException if the given output buffer is too small
* to hold the secret
*/
@ -634,18 +646,24 @@ public class KeyAgreement {
* Creates the shared secret and returns it as a {@code SecretKey}
* object of the specified algorithm.
*
* <p>This method resets this {@code KeyAgreement} object, so that it
* can be reused for further key agreements. Unless this key agreement is
* reinitialized with one of the {@code init} methods, the same
* private information and algorithm parameters will be used for
* subsequent key agreements.
* <p>This method resets this {@code KeyAgreement} object to the state that
* it was in after the most recent call to one of the {@code init} methods.
* After a call to {@code generateSecret}, the object can be reused for
* further key agreement operations by calling {@code doPhase} to supply
* new keys, and then calling {@code generateSecret} to produce a new
* secret. In this case, the private information and algorithm parameters
* supplied to {@code init} will be used for multiple key agreement
* operations. The {@code init} method can be called after
* {@code generateSecret} to change the private information used in
* subsequent operations.
*
* @param algorithm the requested secret-key algorithm
*
* @return the shared secret key
*
* @exception IllegalStateException if this key agreement has not been
* completed yet
* initialized or if {@code doPhase} has not been called to supply the
* keys for all parties in the agreement
* @exception NoSuchAlgorithmException if the specified secret-key
* algorithm is not available
* @exception InvalidKeyException if the shared secret-key material cannot

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2018, 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
@ -130,17 +130,22 @@ public abstract class KeyAgreementSpi {
/**
* Generates the shared secret and returns it in a new buffer.
*
* <p>This method resets this <code>KeyAgreementSpi</code> object,
* so that it
* can be reused for further key agreements. Unless this key agreement is
* reinitialized with one of the <code>engineInit</code> methods, the same
* private information and algorithm parameters will be used for
* subsequent key agreements.
* <p>This method resets this {@code KeyAgreementSpi} object to the state
* that it was in after the most recent call to one of the {@code init}
* methods. After a call to {@code generateSecret}, the object can be reused
* for further key agreement operations by calling {@code doPhase} to supply
* new keys, and then calling {@code generateSecret} to produce a new
* secret. In this case, the private information and algorithm parameters
* supplied to {@code init} will be used for multiple key agreement
* operations. The {@code init} method can be called after
* {@code generateSecret} to change the private information used in
* subsequent operations.
*
* @return the new buffer with the shared secret
*
* @exception IllegalStateException if this key agreement has not been
* completed yet
* initialized or if {@code doPhase} has not been called to supply the
* keys for all parties in the agreement
*/
protected abstract byte[] engineGenerateSecret()
throws IllegalStateException;
@ -153,12 +158,16 @@ public abstract class KeyAgreementSpi {
* result, a <code>ShortBufferException</code> is thrown.
* In this case, this call should be repeated with a larger output buffer.
*
* <p>This method resets this <code>KeyAgreementSpi</code> object,
* so that it
* can be reused for further key agreements. Unless this key agreement is
* reinitialized with one of the <code>engineInit</code> methods, the same
* private information and algorithm parameters will be used for
* subsequent key agreements.
* <p>This method resets this {@code KeyAgreementSpi} object to the state
* that it was in after the most recent call to one of the {@code init}
* methods. After a call to {@code generateSecret}, the object can be reused
* for further key agreement operations by calling {@code doPhase} to supply
* new keys, and then calling {@code generateSecret} to produce a new
* secret. In this case, the private information and algorithm parameters
* supplied to {@code init} will be used for multiple key agreement
* operations. The {@code init} method can be called after
* {@code generateSecret} to change the private information used in
* subsequent operations.
*
* @param sharedSecret the buffer for the shared secret
* @param offset the offset in <code>sharedSecret</code> where the
@ -167,7 +176,8 @@ public abstract class KeyAgreementSpi {
* @return the number of bytes placed into <code>sharedSecret</code>
*
* @exception IllegalStateException if this key agreement has not been
* completed yet
* initialized or if {@code doPhase} has not been called to supply the
* keys for all parties in the agreement
* @exception ShortBufferException if the given output buffer is too small
* to hold the secret
*/
@ -179,19 +189,24 @@ public abstract class KeyAgreementSpi {
* Creates the shared secret and returns it as a secret key object
* of the requested algorithm type.
*
* <p>This method resets this <code>KeyAgreementSpi</code> object,
* so that it
* can be reused for further key agreements. Unless this key agreement is
* reinitialized with one of the <code>engineInit</code> methods, the same
* private information and algorithm parameters will be used for
* subsequent key agreements.
* <p>This method resets this {@code KeyAgreementSpi} object to the state
* that it was in after the most recent call to one of the {@code init}
* methods. After a call to {@code generateSecret}, the object can be reused
* for further key agreement operations by calling {@code doPhase} to supply
* new keys, and then calling {@code generateSecret} to produce a new
* secret. In this case, the private information and algorithm parameters
* supplied to {@code init} will be used for multiple key agreement
* operations. The {@code init} method can be called after
* {@code generateSecret} to change the private information used in
* subsequent operations.
*
* @param algorithm the requested secret key algorithm
*
* @return the shared secret key
*
* @exception IllegalStateException if this key agreement has not been
* completed yet
* initialized or if {@code doPhase} has not been called to supply the
* keys for all parties in the agreement
* @exception NoSuchAlgorithmException if the requested secret key
* algorithm is not available
* @exception InvalidKeyException if the shared secret key material cannot

View File

@ -0,0 +1,94 @@
/*
* Copyright (c) 2018, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 jdk.internal.event;
/**
* Base class for events, to be subclassed in order to define events and their
* fields.
*/
public abstract class Event {
/**
* Sole constructor, for invocation by subclass constructors, typically
* implicit.
*/
protected Event() {
}
/**
* Starts the timing of this event.
*/
public void begin() {
}
/**
* Ends the timing of this event.
*
* The {@code end} method must be invoked after the {@code begin} method.
*/
public void end() {
}
/**
* Writes the field values, time stamp, and event duration.
* <p>
* If the event starts with an invocation of the {@code begin} method, but does
* not end with an explicit invocation of the {@code end} method, then the event
* ends when the {@code commit} method is invoked.
*/
public void commit() {
}
/**
* Returns {@code true} if the event is enabled, {@code false} otherwise
*
* @return {@code true} if event is enabled, {@code false} otherwise
*/
public boolean isEnabled() {
return false;
}
/**
* Returns {@code true} if the event is enabled and if the duration is within
* the threshold for the event, {@code false} otherwise.
*
* @return {@code true} if the event can be written, {@code false} otherwise
*/
public boolean shouldCommit() {
return false;
}
/**
* Sets a field value.
*
* @param index the index of the field to set
* @param value value to set, can be {@code null}
* @throws UnsupportedOperationException if functionality is not supported
* @throws IndexOutOfBoundsException if {@code index} is less than {@code 0} or
* greater than or equal to the number of fields specified for the event
*/
public void set(int index, Object value) {
}
}

View File

@ -136,6 +136,8 @@ module java.base {
java.security.sasl;
exports jdk.internal to
jdk.jfr;
exports jdk.internal.event to
jdk.jfr;
exports jdk.internal.jimage to
jdk.jlink;
exports jdk.internal.jimage.decompressor to

View File

@ -63,7 +63,7 @@ enum SignatureScheme {
"EC",
NamedGroup.SECP384_R1,
ProtocolVersion.PROTOCOLS_TO_13),
ECDSA_SECP512R1_SHA512 (0x0603, "ecdsa_secp512r1_sha512",
ECDSA_SECP521R1_SHA512 (0x0603, "ecdsa_secp521r1_sha512",
"SHA512withECDSA",
"EC",
NamedGroup.SECP521_R1,

View File

@ -49,7 +49,10 @@ extern int errno;
#define ERR_ARGS 3
void error (int fd, int err) {
write (fd, &err, sizeof(err));
if (write (fd, &err, sizeof(err)) != sizeof(err)) {
/* Not sure what to do here. I have no one to speak to. */
exit(0x80 + err);
}
exit (1);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2018, 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
@ -53,13 +53,13 @@
* and an <a href="http://www.iso.org">ISO (International Organization
* for Standardization)</a> standard.
* </li>
* <li><strong><a href="http://www.schematron.com/">Schematron</a></strong> -
* <li><strong><a href="http://standards.iso.org/ittf/PubliclyAvailableStandards/c055982_ISO_IEC_19757-3_2016.zip">Schematron</a></strong> -
* a rules-based XML schema language. Whereas DTD, WXS, and RNG are designed
* to express the structure of a content model, Schematron is designed to
* enforce individual rules that are difficult or impossible to express
* with other schema languages. Schematron is intended to supplement a
* schema written in structural schema language such as the aforementioned.
* Schematron is in the process of becoming an ISO standard.
* Schematron is <a href="http://standards.iso.org/ittf/PubliclyAvailableStandards/index.html">an ISO standard</a>.
* </li>
* </ul>
* <p>

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2009, 2018, 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
@ -127,7 +127,9 @@ public final class ECDHKeyAgreement extends KeyAgreementSpi {
try {
return deriveKey(s, publicValue, encodedParams);
byte[] result = deriveKey(s, publicValue, encodedParams);
publicValue = null;
return result;
} catch (GeneralSecurityException e) {
throw new ProviderException("Could not derive key", e);

View File

@ -61,9 +61,7 @@ public class ValueTaglet extends BaseTaglet {
* Construct a new ValueTaglet.
*/
public ValueTaglet() {
super(VALUE.tagName, true,
EnumSet.of(Site.OVERVIEW, Site.PACKAGE, Site.TYPE, Site.CONSTRUCTOR,
Site.METHOD, Site.FIELD)); // not Site.MODULE at this time!
super(VALUE.tagName, true, EnumSet.allOf(Site.class));
}
/**

View File

@ -88,7 +88,7 @@ package jdk.jfr;
@Enabled(true)
@StackTrace(true)
@Registered(true)
abstract public class Event {
abstract public class Event extends jdk.internal.event.Event {
/**
* Sole constructor, for invocation by subclass constructors, typically
* implicit.

View File

@ -41,7 +41,6 @@ import java.util.Set;
import jdk.internal.module.Modules;
import jdk.jfr.AnnotationElement;
import jdk.jfr.Enabled;
import jdk.jfr.Event;
import jdk.jfr.Name;
import jdk.jfr.Period;
import jdk.jfr.SettingControl;
@ -109,7 +108,7 @@ public final class EventControl {
}
}
EventControl(PlatformEventType es, Class<? extends Event> eventClass) {
EventControl(PlatformEventType es, Class<? extends jdk.internal.event.Event> eventClass) {
this(es);
defineSettings(eventClass);
}

View File

@ -93,11 +93,11 @@ final class EventHandlerCreator {
return EventHandler.class.getName() + id + SUFFIX;
}
public EventHandlerCreator(long id, List<SettingInfo> settingInfos, EventType type, Class<? extends Event> eventClass) {
public EventHandlerCreator(long id, List<SettingInfo> settingInfos, EventType type, Class<? extends jdk.internal.event.Event> eventClass) {
this(id, settingInfos, createFieldInfos(eventClass, type));
}
private static List<FieldInfo> createFieldInfos(Class<? extends Event> eventClass, EventType type) throws Error {
private static List<FieldInfo> createFieldInfos(Class<? extends jdk.internal.event.Event> eventClass, EventType type) throws Error {
List<FieldInfo> fieldInfos = new ArrayList<>();
for (ValueDescriptor v : type.getFields()) {
// Only value descriptors that are not fields on the event class.

View File

@ -102,6 +102,7 @@ public final class EventInstrumentation {
private static final Type ANNOTATION_TYPE_ENABLED = Type.getType(Enabled.class);
private static final Type TYPE_EVENT_HANDLER = Type.getType(EventHandler.class);
private static final Type TYPE_SETTING_CONTROL = Type.getType(SettingControl.class);
private static final Type TYPE_OBJECT = Type.getType(Object.class);
private static final Method METHOD_COMMIT = new Method("commit", Type.VOID_TYPE, new Type[0]);
private static final Method METHOD_BEGIN = new Method("begin", Type.VOID_TYPE, new Type[0]);
private static final Method METHOD_END = new Method("end", Type.VOID_TYPE, new Type[0]);
@ -117,6 +118,7 @@ public final class EventInstrumentation {
private final Method writeMethod;
private final String eventHandlerXInternalName;
private final String eventName;
private final boolean untypedEventHandler;
private boolean guardHandlerReference;
private Class<?> superClass;
@ -125,11 +127,20 @@ public final class EventInstrumentation {
this.classNode = createClassNode(bytes);
this.settingInfos = buildSettingInfos(superClass, classNode);
this.fieldInfos = buildFieldInfos(superClass, classNode);
this.untypedEventHandler = hasUntypedHandler();
this.writeMethod = makeWriteMethod(fieldInfos);
this.eventHandlerXInternalName = ASMToolkit.getInternalName(EventHandlerCreator.makeEventHandlerName(id));
String n = annotationValue(classNode, ANNOTATION_TYPE_NAME.getDescriptor(), String.class);
this.eventName = n == null ? classNode.name.replace("/", ".") : n;
}
private boolean hasUntypedHandler() {
for (FieldNode field : classNode.fields) {
if (FIELD_EVENT_HANDLER.equals(field.name)) {
return field.desc.equals(TYPE_OBJECT.getDescriptor());
}
}
throw new InternalError("Class missing handler field");
}
public String getClassName() {
@ -225,7 +236,7 @@ public final class EventInstrumentation {
}
}
}
for (Class<?> c = superClass; c != Event.class; c = c.getSuperclass()) {
for (Class<?> c = superClass; c != jdk.internal.event.Event.class; c = c.getSuperclass()) {
for (java.lang.reflect.Method method : c.getDeclaredMethods()) {
if (!methodSet.contains(method.getName())) {
// skip private method in base classes
@ -249,7 +260,6 @@ public final class EventInstrumentation {
}
}
return settingInfos;
}
private static List<FieldInfo> buildFieldInfos(Class<?> superClass, ClassNode classNode) {
@ -264,14 +274,13 @@ public final class EventInstrumentation {
fieldInfos.add(new FieldInfo("startTime", Type.LONG_TYPE.getDescriptor(), classNode.name));
fieldInfos.add(new FieldInfo("duration", Type.LONG_TYPE.getDescriptor(), classNode.name));
for (FieldNode field : classNode.fields) {
String className = Type.getType(field.desc).getClassName();
if (!fieldSet.contains(field.name) && isValidField(field.access, className)) {
if (!fieldSet.contains(field.name) && isValidField(field.access, Type.getType(field.desc).getClassName())) {
FieldInfo fi = new FieldInfo(field.name, field.desc, classNode.name);
fieldInfos.add(fi);
fieldSet.add(field.name);
}
}
for (Class<?> c = superClass; c != Event.class; c = c.getSuperclass()) {
for (Class<?> c = superClass; c != jdk.internal.event.Event.class; c = c.getSuperclass()) {
for (Field field : c.getDeclaredFields()) {
// skip private field in base classes
if (!Modifier.isPrivate(field.getModifiers())) {
@ -321,10 +330,10 @@ public final class EventInstrumentation {
updateMethod(METHOD_IS_ENABLED, methodVisitor -> {
Label nullLabel = new Label();
if (guardHandlerReference) {
methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, TYPE_EVENT_HANDLER.getDescriptor());
getEventHandler(methodVisitor);
methodVisitor.visitJumpInsn(Opcodes.IFNULL, nullLabel);
}
methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, TYPE_EVENT_HANDLER.getDescriptor());
getEventHandler(methodVisitor);
ASMToolkit.invokeVirtual(methodVisitor, TYPE_EVENT_HANDLER.getInternalName(), METHOD_IS_ENABLED);
methodVisitor.visitInsn(Opcodes.IRETURN);
if (guardHandlerReference) {
@ -408,7 +417,7 @@ public final class EventInstrumentation {
// eventHandler.write(...);
// }
methodVisitor.visitJumpInsn(Opcodes.IFEQ, end);
methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(EventHandler.class));
getEventHandler(methodVisitor);
methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, eventHandlerXInternalName);
for (FieldInfo fi : fieldInfos) {
@ -426,8 +435,8 @@ public final class EventInstrumentation {
// MyEvent#shouldCommit()
updateMethod(METHOD_EVENT_SHOULD_COMMIT, methodVisitor -> {
Label fail = new Label();
// if (!eventHandler.shoouldCommit(duration) goto fail;
methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(EventHandler.class));
// if (!eventHandler.shouldCommit(duration) goto fail;
getEventHandler(methodVisitor);
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_DURATION, "J");
ASMToolkit.invokeVirtual(methodVisitor, TYPE_EVENT_HANDLER.getInternalName(), METHOD_EVENT_HANDLER_SHOULD_COMMIT);
@ -435,7 +444,11 @@ public final class EventInstrumentation {
for (SettingInfo si : settingInfos) {
// if (!settingsMethod(eventHandler.settingX)) goto fail;
methodVisitor.visitIntInsn(Opcodes.ALOAD, 0);
methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(EventHandler.class));
if (untypedEventHandler) {
methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, TYPE_OBJECT.getDescriptor());
} else {
methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(EventHandler.class));
}
methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, eventHandlerXInternalName);
methodVisitor.visitFieldInsn(Opcodes.GETFIELD, eventHandlerXInternalName, si.fieldName, TYPE_SETTING_CONTROL.getDescriptor());
methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, si.internalSettingName);
@ -452,6 +465,15 @@ public final class EventInstrumentation {
});
}
private void getEventHandler(MethodVisitor methodVisitor) {
if (untypedEventHandler) {
methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, TYPE_OBJECT.getDescriptor());
methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, TYPE_EVENT_HANDLER.getInternalName());
} else {
methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(EventHandler.class));
}
}
private void makeUninstrumented() {
updateExistingWithReturnFalse(METHOD_EVENT_SHOULD_COMMIT);
updateExistingWithReturnFalse(METHOD_IS_ENABLED);

View File

@ -106,11 +106,11 @@ public final class JVM {
public native void endRecording();
/**
* Return a list of all classes deriving from {@link Event}
* Return a list of all classes deriving from {@link jdk.internal.event.Event}
*
* @return list of event classes.
*/
public native List<Class<? extends Event>> getAllEventClasses();
public native List<Class<? extends jdk.internal.event.Event>> getAllEventClasses();
/**
* Return a count of the number of unloaded classes deriving from {@link Event}

View File

@ -26,7 +26,6 @@ package jdk.jfr.internal;
import java.lang.reflect.Modifier;
import jdk.jfr.Event;
import jdk.jfr.internal.handlers.EventHandler;
import jdk.jfr.internal.instrument.JDKEvents;
@ -53,8 +52,8 @@ final class JVMUpcalls {
*/
static byte[] onRetransform(long traceId, boolean dummy, Class<?> clazz, byte[] oldBytes) throws Throwable {
try {
if (Event.class.isAssignableFrom(clazz) && !Modifier.isAbstract(clazz.getModifiers())) {
EventHandler handler = Utils.getHandler(clazz.asSubclass(Event.class));
if (jdk.internal.event.Event.class.isAssignableFrom(clazz) && !Modifier.isAbstract(clazz.getModifiers())) {
EventHandler handler = Utils.getHandler(clazz.asSubclass(jdk.internal.event.Event.class));
if (handler == null) {
Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, "No event handler found for " + clazz.getName() + ". Ignoring instrumentation request.");
// Probably triggered by some other agent

View File

@ -33,6 +33,7 @@ import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@ -56,6 +57,7 @@ public final class MetadataRepository {
private final List<EventControl> nativeControls = new ArrayList<EventControl>(100);
private final TypeLibrary typeLibrary = TypeLibrary.getInstance();
private final SettingsManager settingsManager = new SettingsManager();
private final Map<String, Class<? extends Event>> mirrors = new HashMap<>();
private boolean staleMetadata = true;
private boolean unregistered;
private long lastUnloaded = -1;
@ -105,7 +107,7 @@ public final class MetadataRepository {
return eventTypes;
}
public synchronized EventType getEventType(Class<? extends Event> eventClass) {
public synchronized EventType getEventType(Class<? extends jdk.internal.event.Event> eventClass) {
EventHandler h = getHandler(eventClass);
if (h != null && h.isRegistered()) {
return h.getEventType();
@ -121,15 +123,20 @@ public final class MetadataRepository {
}
// never registered, ignore call
}
public synchronized EventType register(Class<? extends Event> eventClass) {
public synchronized EventType register(Class<? extends jdk.internal.event.Event> eventClass) {
return register(eventClass, Collections.emptyList(), Collections.emptyList());
}
public synchronized EventType register(Class<? extends Event> eventClass, List<AnnotationElement> dynamicAnnotations, List<ValueDescriptor> dynamicFields) {
public synchronized EventType register(Class<? extends jdk.internal.event.Event> eventClass, List<AnnotationElement> dynamicAnnotations, List<ValueDescriptor> dynamicFields) {
Utils.checkRegisterPermission();
EventHandler handler = getHandler(eventClass);
if (handler == null) {
handler = makeHandler(eventClass, dynamicAnnotations, dynamicFields);
if (eventClass.getAnnotation(MirrorEvent.class) != null) {
// don't register mirrors
return null;
}
PlatformEventType pe = findMirrorType(eventClass);
handler = makeHandler(eventClass, pe, dynamicAnnotations, dynamicFields);
}
handler.setRegistered(true);
typeLibrary.addType(handler.getPlatformEventType());
@ -143,16 +150,32 @@ public final class MetadataRepository {
return handler.getEventType();
}
private EventHandler getHandler(Class<? extends Event> eventClass) {
private PlatformEventType findMirrorType(Class<? extends jdk.internal.event.Event> eventClass) throws InternalError {
String fullName = eventClass.getModule().getName() + ":" + eventClass.getName();
Class<? extends Event> mirrorClass = mirrors.get(fullName);
if (mirrorClass == null) {
return null; // not a mirror
}
Utils.verifyMirror(mirrorClass, eventClass);
PlatformEventType et = (PlatformEventType) TypeLibrary.createType(mirrorClass);
typeLibrary.removeType(et.getId());
long id = Type.getTypeId(eventClass);
et.setId(id);
return et;
}
private EventHandler getHandler(Class<? extends jdk.internal.event.Event> eventClass) {
Utils.ensureValidEventSubclass(eventClass);
SecuritySupport.makeVisibleToJFR(eventClass);
Utils.ensureInitialized(eventClass);
return Utils.getHandler(eventClass);
}
private EventHandler makeHandler(Class<? extends Event> eventClass, List<AnnotationElement> dynamicAnnotations, List<ValueDescriptor> dynamicFields) throws InternalError {
private EventHandler makeHandler(Class<? extends jdk.internal.event.Event> eventClass, PlatformEventType pEventType, List<AnnotationElement> dynamicAnnotations, List<ValueDescriptor> dynamicFields) throws InternalError {
SecuritySupport.addHandlerExport(eventClass);
PlatformEventType pEventType = (PlatformEventType) TypeLibrary.createType(eventClass, dynamicAnnotations, dynamicFields);
if (pEventType == null) {
pEventType = (PlatformEventType) TypeLibrary.createType(eventClass, dynamicAnnotations, dynamicFields);
}
EventType eventType = PrivateAccess.getInstance().newEventType(pEventType);
EventControl ec = new EventControl(pEventType, eventClass);
Class<? extends EventHandler> handlerClass = null;
@ -198,9 +221,9 @@ public final class MetadataRepository {
}
private static List<EventHandler> getEventHandlers() {
List<Class<? extends Event>> allEventClasses = jvm.getAllEventClasses();
List<Class<? extends jdk.internal.event.Event>> allEventClasses = jvm.getAllEventClasses();
List<EventHandler> eventHandlers = new ArrayList<>(allEventClasses.size());
for (Class<? extends Event> clazz : allEventClasses) {
for (Class<? extends jdk.internal.event.Event> clazz : allEventClasses) {
EventHandler eh = Utils.getHandler(clazz);
if (eh != null) {
eventHandlers.add(eh);
@ -252,9 +275,9 @@ public final class MetadataRepository {
long unloaded = jvm.getUnloadedEventClassCount();
if (this.lastUnloaded != unloaded) {
this.lastUnloaded = unloaded;
List<Class<? extends Event>> eventClasses = jvm.getAllEventClasses();
List<Class<? extends jdk.internal.event.Event>> eventClasses = jvm.getAllEventClasses();
HashSet<Long> knownIds = new HashSet<>(eventClasses.size());
for (Class<? extends Event> ec: eventClasses) {
for (Class<? extends jdk.internal.event.Event> ec: eventClasses) {
knownIds.add(Type.getTypeId(ec));
}
for (Type type : typeLibrary.getTypes()) {
@ -270,8 +293,18 @@ public final class MetadataRepository {
}
}
synchronized public void setUnregistered() {
synchronized void setUnregistered() {
unregistered = true;
}
public synchronized void registerMirror(Class<? extends Event> eventClass) {
MirrorEvent me = eventClass.getAnnotation(MirrorEvent.class);
if (me != null) {
String fullName = me.module() + ":" + me.className();
mirrors.put(fullName, eventClass);
return;
}
throw new InternalError("Mirror class must have annotation " + MirrorEvent.class.getName());
}
}

View File

@ -0,0 +1,25 @@
package jdk.jfr.internal;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.ElementType;
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
public @interface MirrorEvent {
/**
* Fully qualified name of the class to mirror metadata for (for example,
* {@code "jdk.internal.event.Example"})
*
* @return the fully qualified class name of the event
*/
String className();
/**
* The module where the event is located, by default {@code "java.base"}.
*
* @return the module name
*/
String module() default "java.base";
}

View File

@ -81,9 +81,7 @@ public final class PlatformRecorder {
Logger.log(JFR_SYSTEM, INFO, "Registered JDK events");
JDKEvents.addInstrumentation();
startDiskMonitor();
SecuritySupport.registerEvent(ActiveRecordingEvent.class);
activeRecordingEvent = EventType.getEventType(ActiveRecordingEvent.class);
SecuritySupport.registerEvent(ActiveSettingEvent.class);
activeSettingEvent = EventType.getEventType(ActiveSettingEvent.class);
shutdownHook = SecuritySupport.createThreadWitNoPermissions("JFR: Shutdown Hook", new ShutdownHook(this));
SecuritySupport.setUncaughtExceptionHandler(shutdownHook, new ShutdownHook.ExceptionHandler());
@ -91,6 +89,7 @@ public final class PlatformRecorder {
timer = createTimer();
}
private static Timer createTimer() {
try {
List<Timer> result = new CopyOnWriteArrayList<>();

View File

@ -262,8 +262,12 @@ public final class SecuritySupport {
Modules.addExports(JFR_MODULE, Utils.HANDLERS_PACKAGE_NAME, clazz.getModule());
}
public static void registerEvent(Class<? extends Event> eventClass) {
doPrivileged(() -> FlightRecorder.register(eventClass), new FlightRecorderPermission(Utils.REGISTER_EVENT));
public static void registerEvent(Class<? extends jdk.internal.event.Event> eventClass) {
doPrivileged(() -> MetadataRepository.getInstance().register(eventClass), new FlightRecorderPermission(Utils.REGISTER_EVENT));
}
public static void registerMirror(Class<? extends Event> eventClass) {
doPrivileged(() -> MetadataRepository.getInstance().registerMirror(eventClass), new FlightRecorderPermission(Utils.REGISTER_EVENT));
}
static boolean getBooleanProperty(String propertyName) {

View File

@ -37,7 +37,6 @@ import java.util.Map.Entry;
import java.util.Set;
import java.util.StringJoiner;
import jdk.jfr.Event;
import jdk.jfr.internal.handlers.EventHandler;
final class SettingsManager {
@ -152,9 +151,9 @@ final class SettingsManager {
}
}
public void updateRetransform(List<Class<? extends Event>> eventClasses) {
public void updateRetransform(List<Class<? extends jdk.internal.event.Event>> eventClasses) {
List<Class<?>> classes = new ArrayList<>();
for(Class<? extends Event> eventClass: eventClasses) {
for(Class<? extends jdk.internal.event.Event> eventClass: eventClasses) {
EventHandler eh = Utils.getHandler(eventClass);
if (eh != null ) {
PlatformEventType eventType = eh.getPlatformEventType();

View File

@ -71,10 +71,11 @@ public class Type implements Comparable<Type> {
private final String name;
private final String superType;
private final boolean constantPool;
private final long id;
private final ArrayList<ValueDescriptor> fields = new ArrayList<>();
private Boolean simpleType; // calculated lazy
private boolean remove = true;
private long id;
/**
* Creates a type
*
@ -318,4 +319,8 @@ public class Type implements Comparable<Type> {
public boolean getRemove() {
return remove;
}
public void setId(long id) {
this.id = id;
}
}

View File

@ -49,7 +49,6 @@ import java.util.stream.Stream;
import jdk.jfr.AnnotationElement;
import jdk.jfr.Description;
import jdk.jfr.Event;
import jdk.jfr.Label;
import jdk.jfr.MetadataDefinition;
import jdk.jfr.Name;
@ -240,7 +239,7 @@ public final class TypeLibrary {
// STRUCT
String superType = null;
boolean eventType = false;
if (Event.class.isAssignableFrom(clazz)) {
if (jdk.internal.event.Event.class.isAssignableFrom(clazz)) {
superType = Type.SUPER_TYPE_EVENT;
eventType= true;
}
@ -489,4 +488,8 @@ public final class TypeLibrary {
aQ.addAll(ae.getAnnotationElements());
}
}
public void removeType(long id) {
types.remove(id);
}
}

View File

@ -267,7 +267,7 @@ public final class Utils {
return (long) (nanos * JVM.getJVM().getTimeConversionFactor());
}
static synchronized EventHandler getHandler(Class<? extends Event> eventClass) {
static synchronized EventHandler getHandler(Class<? extends jdk.internal.event.Event> eventClass) {
Utils.ensureValidEventSubclass(eventClass);
try {
Field f = eventClass.getDeclaredField(EventInstrumentation.FIELD_EVENT_HANDLER);
@ -278,7 +278,7 @@ public final class Utils {
}
}
static synchronized void setHandler(Class<? extends Event> eventClass, EventHandler handler) {
static synchronized void setHandler(Class<? extends jdk.internal.event.Event> eventClass, EventHandler handler) {
Utils.ensureValidEventSubclass(eventClass);
try {
Field field = eventClass.getDeclaredField(EventInstrumentation.FIELD_EVENT_HANDLER);
@ -322,7 +322,7 @@ public final class Utils {
static List<Field> getVisibleEventFields(Class<?> clazz) {
Utils.ensureValidEventSubclass(clazz);
List<Field> fields = new ArrayList<>();
for (Class<?> c = clazz; c != Event.class; c = c.getSuperclass()) {
for (Class<?> c = clazz; c != jdk.internal.event.Event.class; c = c.getSuperclass()) {
for (Field field : c.getDeclaredFields()) {
// skip private field in base classes
if (c == clazz || !Modifier.isPrivate(field.getModifiers())) {
@ -334,10 +334,10 @@ public final class Utils {
}
public static void ensureValidEventSubclass(Class<?> eventClass) {
if (Event.class.isAssignableFrom(eventClass) && Modifier.isAbstract(eventClass.getModifiers())) {
if (jdk.internal.event.Event.class.isAssignableFrom(eventClass) && Modifier.isAbstract(eventClass.getModifiers())) {
throw new IllegalArgumentException("Abstract event classes are not allowed");
}
if (eventClass == Event.class || !Event.class.isAssignableFrom(eventClass)) {
if (eventClass == Event.class || eventClass == jdk.internal.event.Event.class || !jdk.internal.event.Event.class.isAssignableFrom(eventClass)) {
throw new IllegalArgumentException("Must be a subclass to " + Event.class.getName());
}
}
@ -366,7 +366,7 @@ public final class Utils {
}
}
public static void ensureInitialized(Class<? extends Event> eventClass) {
public static void ensureInitialized(Class<? extends jdk.internal.event.Event> eventClass) {
SecuritySupport.ensureClassIsInitialized(eventClass);
}
@ -499,6 +499,50 @@ public final class Utils {
return eventName;
}
public static void verifyMirror(Class<?> mirror, Class<?> real) {
Class<?> cMirror = Objects.requireNonNull(mirror);
Class<?> cReal = Objects.requireNonNull(real);
while (cReal != null) {
Map<String, Field> mirrorFields = new HashMap<>();
if (cMirror != null) {
for (Field f : cMirror.getDeclaredFields()) {
if (isSupportedType(f.getType())) {
mirrorFields.put(f.getName(), f);
}
}
}
for (Field realField : cReal.getDeclaredFields()) {
if (isSupportedType(realField.getType())) {
String fieldName = realField.getName();
Field mirrorField = mirrorFields.get(fieldName);
if (mirrorField == null) {
throw new InternalError("Missing mirror field for " + cReal.getName() + "#" + fieldName);
}
if (realField.getModifiers() != mirrorField.getModifiers()) {
throw new InternalError("Incorrect modifier for mirror field "+ cMirror.getName() + "#" + fieldName);
}
mirrorFields.remove(fieldName);
}
}
if (!mirrorFields.isEmpty()) {
throw new InternalError(
"Found additional fields in mirror class " + cMirror.getName() + " " + mirrorFields.keySet());
}
if (cMirror != null) {
cMirror = cMirror.getSuperclass();
}
cReal = cReal.getSuperclass();
}
}
private static boolean isSupportedType(Class<?> type) {
if (Modifier.isTransient(type.getModifiers()) || Modifier.isStatic(type.getModifiers())) {
return false;
}
return Type.isValidJavaFieldType(type.getName());
}
public static String makeFilename(Recording recording) {
String pid = JVM.getJVM().getPid();
String date = Repository.REPO_DATE_FORMAT.format(LocalDateTime.now());

View File

@ -51,6 +51,9 @@ import jdk.jfr.internal.Utils;
public final class JDKEvents {
private static final Class<?>[] mirrorEventClasses = {
};
private static final Class<?>[] eventClasses = {
FileForceEvent.class,
FileReadEvent.class,
@ -90,6 +93,9 @@ public final class JDKEvents {
Modules.addExports(jdkJfrModule, Utils.EVENTS_PACKAGE_NAME, javaBaseModule);
Modules.addExports(jdkJfrModule, Utils.INSTRUMENT_PACKAGE_NAME, javaBaseModule);
Modules.addExports(jdkJfrModule, Utils.HANDLERS_PACKAGE_NAME, javaBaseModule);
for (Class<?> mirrorEventClass : mirrorEventClasses) {
SecuritySupport.registerMirror(((Class<? extends Event>)mirrorEventClass));
}
for (Class<?> eventClass : eventClasses) {
SecuritySupport.registerEvent((Class<? extends Event>) eventClass);
}

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2010, 2018, 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
@ -27,11 +27,16 @@
# Makefile to run tests from multiple sibling directories
#
$(info WARNING: This way of running tests ("cd test && make") is deprecated)
$(info Please use "make test TEST=..." instead. See doc/testing.md for details)
# Macro to run a test target in a subdir
define SUBDIR_TEST # subdirectory target
if [ -d $1 ] ; then \
if [ -r $1/Makefile ] ; then \
$(MAKE) --no-print-directory -k -C $1 $2 ; \
echo 'WARNING: This way of running tests ("cd test && make") is deprecated' ; \
echo 'Please use "make test TEST=..." instead. See doc/testing.md for details' ; \
else \
echo "ERROR: File does not exist: $1/Makefile"; \
exit 1; \

View File

@ -0,0 +1,238 @@
/*
* Copyright (c) 2001, 2018, 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 "gc/shared/gcTimer.hpp"
#include "utilities/ticks.hpp"
#include "unittest.hpp"
class GCTimerTest {
public:
static void register_gc_start(GCTimer* const timer, jlong ticks) {
timer->register_gc_start(Ticks(ticks));
}
static void register_gc_end(GCTimer* const timer, jlong ticks) {
timer->register_gc_end(Ticks(ticks));
}
};
TEST(GCTimer, start) {
GCTimer gc_timer;
GCTimerTest::register_gc_start(&gc_timer, 1);
EXPECT_EQ(1, gc_timer.gc_start().value());
}
TEST(GCTimer, end) {
GCTimer gc_timer;
GCTimerTest::register_gc_start(&gc_timer, 1);
GCTimerTest::register_gc_end(&gc_timer, 2);
EXPECT_EQ(2, gc_timer.gc_end().value());
}
class TimePartitionPhasesIteratorTest {
public:
static void validate_gc_phase(GCPhase* phase, int level, const char* name, const jlong& start, const jlong& end) {
EXPECT_EQ(level, phase->level());
EXPECT_STREQ(name, phase->name());
EXPECT_EQ(start, phase->start().value());
EXPECT_EQ(end, phase->end().value());
}
static void validate_pauses(const TimePartitions& time_partitions, const Tickspan& expected_sum_of_pauses, const Tickspan& expected_longest_pause) {
EXPECT_EQ(expected_sum_of_pauses, time_partitions.sum_of_pauses());
EXPECT_EQ(expected_longest_pause, time_partitions.longest_pause());
}
static void validate_pauses(const TimePartitions& time_partitions, const Tickspan& expected_pause) {
TimePartitionPhasesIteratorTest::validate_pauses(time_partitions, expected_pause, expected_pause);
}
static void validate_pauses(const TimePartitions& time_partitions, jlong end, jlong start) {
TimePartitionPhasesIteratorTest::validate_pauses(time_partitions, Ticks(end) - Ticks(start));
}
static void validate_pauses(const TimePartitions& time_partitions, jlong all_end, jlong all_start, jlong longest_end, jlong longest_start) {
TimePartitionPhasesIteratorTest::validate_pauses(time_partitions, Ticks(all_end) - Ticks(all_start), Ticks(longest_end) - Ticks(longest_start));
}
static void report_gc_phase_start(TimePartitions* const partitions, const char* name, jlong ticks, GCPhase::PhaseType type=GCPhase::PausePhaseType) {
partitions->report_gc_phase_start(name, Ticks(ticks), type);
}
static void report_gc_phase_end(TimePartitions* const partitions, jlong ticks, GCPhase::PhaseType type=GCPhase::PausePhaseType) {
partitions->report_gc_phase_end(Ticks(ticks), type);
}
};
TEST(TimePartitionPhasesIterator, one_pause) {
TimePartitions time_partitions;
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "PausePhase", 2);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 8);
TimePartitionPhasesIterator iter(&time_partitions);
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 0, "PausePhase", 2, 8));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_pauses(time_partitions, 8, 2));
EXPECT_FALSE(iter.has_next()) << "Too many elements";
}
TEST(TimePartitionPhasesIterator, two_pauses) {
TimePartitions time_partitions;
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "PausePhase1", 2);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 3);
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "PausePhase2", 4);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 6);
TimePartitionPhasesIterator iter(&time_partitions);
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 0, "PausePhase1", 2, 3));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 0, "PausePhase2", 4, 6));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_pauses(time_partitions, 3, 0, 2, 0));
EXPECT_FALSE(iter.has_next()) << "Too many elements";
}
TEST(TimePartitionPhasesIterator, one_sub_pause_phase) {
TimePartitions time_partitions;
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "PausePhase", 2);
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "SubPhase", 3);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 4);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 5);
TimePartitionPhasesIterator iter(&time_partitions);
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 0, "PausePhase", 2, 5));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 1, "SubPhase", 3, 4));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_pauses(time_partitions, 3, 0));
EXPECT_FALSE(iter.has_next()) << "Too many elements";
}
TEST(TimePartitionPhasesIterator, max_nested_pause_phases) {
TimePartitions time_partitions;
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "PausePhase", 2);
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "SubPhase1", 3);
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "SubPhase2", 4);
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "SubPhase3", 5);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 6);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 7);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 8);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 9);
TimePartitionPhasesIterator iter(&time_partitions);
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 0, "PausePhase", 2, 9));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 1, "SubPhase1", 3, 8));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 2, "SubPhase2", 4, 7));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 3, "SubPhase3", 5, 6));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_pauses(time_partitions, 7, 0));
EXPECT_FALSE(iter.has_next()) << "Too many elements";
}
TEST(TimePartitionPhasesIterator, many_sub_pause_phases) {
TimePartitions time_partitions;
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "PausePhase", 2);
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "SubPhase1", 3);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 4);
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "SubPhase2", 5);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 6);
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "SubPhase3", 7);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 8);
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "SubPhase4", 9);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 10);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 11);
TimePartitionPhasesIterator iter(&time_partitions);
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 0, "PausePhase", 2, 11));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 1, "SubPhase1", 3, 4));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 1, "SubPhase2", 5, 6));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 1, "SubPhase3", 7, 8));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 1, "SubPhase4", 9, 10));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_pauses(time_partitions, 9, 0));
EXPECT_FALSE(iter.has_next()) << "Too many elements";
}
TEST(TimePartitionPhasesIterator, many_sub_pause_phases2) {
TimePartitions time_partitions;
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "PausePhase", 2);
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "SubPhase1", 3);
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "SubPhase11", 4);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 5);
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "SubPhase12", 6);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 7);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 8);
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "SubPhase2", 9);
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "SubPhase21", 10);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 11);
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "SubPhase22", 12);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 13);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 14);
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "SubPhase3", 15);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 16);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 17);
TimePartitionPhasesIterator iter(&time_partitions);
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 0, "PausePhase", 2, 17));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 1, "SubPhase1", 3, 8));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 2, "SubPhase11", 4, 5));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 2, "SubPhase12", 6, 7));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 1, "SubPhase2", 9, 14));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 2, "SubPhase21", 10, 11));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 2, "SubPhase22", 12, 13));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 1, "SubPhase3", 15, 16));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_pauses(time_partitions, 15, 0));
EXPECT_FALSE(iter.has_next()) << "Too many elements";
}
TEST(TimePartitionPhasesIterator, one_concurrent) {
TimePartitions time_partitions;
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "ConcurrentPhase", 2, GCPhase::ConcurrentPhaseType);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 8, GCPhase::ConcurrentPhaseType);
TimePartitionPhasesIterator iter(&time_partitions);
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 0, "ConcurrentPhase", 2, 8));
// ConcurrentPhaseType should not affect to both 'sum_of_pauses()' and 'longest_pause()'.
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_pauses(time_partitions, Tickspan()));
EXPECT_FALSE(iter.has_next()) << "Too many elements";
}

View File

@ -0,0 +1,86 @@
/*
* Copyright (c) 2018, 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 "memory/metaspace.hpp"
#include "memory/metaspace/virtualSpaceList.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/os.hpp"
#include "unittest.hpp"
using namespace metaspace;
TEST_VM(MetaspaceUtils, reserved) {
size_t reserved = MetaspaceUtils::reserved_bytes();
EXPECT_GT(reserved, 0UL);
size_t reserved_metadata = MetaspaceUtils::reserved_bytes(Metaspace::NonClassType);
EXPECT_GT(reserved_metadata, 0UL);
EXPECT_LE(reserved_metadata, reserved);
}
TEST_VM(MetaspaceUtils, reserved_compressed_class_pointers) {
if (!UseCompressedClassPointers) {
return;
}
size_t reserved = MetaspaceUtils::reserved_bytes();
EXPECT_GT(reserved, 0UL);
size_t reserved_class = MetaspaceUtils::reserved_bytes(Metaspace::ClassType);
EXPECT_GT(reserved_class, 0UL);
EXPECT_LE(reserved_class, reserved);
}
TEST_VM(MetaspaceUtils, committed) {
size_t committed = MetaspaceUtils::committed_bytes();
EXPECT_GT(committed, 0UL);
size_t reserved = MetaspaceUtils::reserved_bytes();
EXPECT_LE(committed, reserved);
size_t committed_metadata = MetaspaceUtils::committed_bytes(Metaspace::NonClassType);
EXPECT_GT(committed_metadata, 0UL);
EXPECT_LE(committed_metadata, committed);
}
TEST_VM(MetaspaceUtils, committed_compressed_class_pointers) {
if (!UseCompressedClassPointers) {
return;
}
size_t committed = MetaspaceUtils::committed_bytes();
EXPECT_GT(committed, 0UL);
size_t committed_class = MetaspaceUtils::committed_bytes(Metaspace::ClassType);
EXPECT_GT(committed_class, 0UL);
EXPECT_LE(committed_class, committed);
}
TEST_VM(MetaspaceUtils, virtual_space_list_large_chunk) {
VirtualSpaceList* vs_list = new VirtualSpaceList(os::vm_allocation_granularity());
MutexLockerEx cl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag);
// A size larger than VirtualSpaceSize (256k) and add one page to make it _not_ be
// vm_allocation_granularity aligned on Windows.
size_t large_size = (size_t)(2*256*K + (os::vm_page_size() / BytesPerWord));
large_size += (os::vm_page_size() / BytesPerWord);
vs_list->get_new_chunk(large_size, 0);
}

View File

@ -47,11 +47,11 @@ public:
void main_run() {
_wrt_start->signal();
while (!_exit) {
GlobalCounter::critical_section_begin(this);
GlobalCounter::CSContext cs_context = GlobalCounter::critical_section_begin(this);
volatile TestData* test = OrderAccess::load_acquire(_test);
long value = OrderAccess::load_acquire(&test->test_value);
ASSERT_EQ(value, GOOD_VALUE);
GlobalCounter::critical_section_end(this);
GlobalCounter::critical_section_end(this, cs_context);
{
GlobalCounter::CriticalSection cs(this);
volatile TestData* test = OrderAccess::load_acquire(_test);

View File

@ -0,0 +1,208 @@
/*
* Copyright (c) 2018, 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 "metaprogramming/isRegisteredEnum.hpp"
#include "runtime/atomic.hpp"
#include "runtime/orderAccess.hpp"
#include "runtime/os.hpp"
#include "utilities/globalCounter.hpp"
#include "utilities/globalCounter.inline.hpp"
#include "utilities/spinYield.hpp"
#include "threadHelper.inline.hpp"
enum NestedTestState {
START,
START_WAIT,
OUTER_ENTERED,
INNER_ENTERED,
INNER_EXITED,
OUTER_EXITED,
SYNCHRONIZING,
SYNCHRONIZED
};
template<> struct IsRegisteredEnum<NestedTestState> : public TrueType {};
class RCUNestedThread : public JavaTestThread {
volatile NestedTestState _state;
volatile bool _proceed;
protected:
RCUNestedThread(Semaphore* post) :
JavaTestThread(post),
_state(START),
_proceed(false)
{}
~RCUNestedThread() {}
void set_state(NestedTestState new_state) {
OrderAccess::release_store(&_state, new_state);
}
void wait_with_state(NestedTestState new_state) {
SpinYield spinner;
OrderAccess::release_store(&_state, new_state);
while (!OrderAccess::load_acquire(&_proceed)) {
spinner.wait();
}
OrderAccess::release_store(&_proceed, false);
}
public:
NestedTestState state() const {
return OrderAccess::load_acquire(&_state);
}
void wait_for_state(NestedTestState goal) {
SpinYield spinner;
while (state() != goal) {
spinner.wait();
}
}
void proceed() {
OrderAccess::release_store(&_proceed, true);
}
};
class RCUNestedReaderThread : public RCUNestedThread {
public:
RCUNestedReaderThread(Semaphore* post) :
RCUNestedThread(post)
{}
virtual void main_run();
};
void RCUNestedReaderThread::main_run() {
wait_with_state(START_WAIT);
{
GlobalCounter::CriticalSection outer(Thread::current());
wait_with_state(OUTER_ENTERED);
{
GlobalCounter::CriticalSection inner(Thread::current());
wait_with_state(INNER_ENTERED);
}
wait_with_state(INNER_EXITED);
}
wait_with_state(OUTER_EXITED);
}
class RCUNestedWriterThread : public RCUNestedThread {
public:
RCUNestedWriterThread(Semaphore* post) :
RCUNestedThread(post)
{}
virtual void main_run();
};
void RCUNestedWriterThread::main_run() {
wait_with_state(START_WAIT);
set_state(SYNCHRONIZING);
GlobalCounter::write_synchronize();
wait_with_state(SYNCHRONIZED);
}
TEST_VM(GlobalCounter, nested_critical_section) {
Semaphore post;
RCUNestedReaderThread* reader = new RCUNestedReaderThread(&post);
RCUNestedWriterThread* outer = new RCUNestedWriterThread(&post);
RCUNestedWriterThread* inner = new RCUNestedWriterThread(&post);
reader->doit();
outer->doit();
inner->doit();
reader->wait_for_state(START_WAIT);
outer->wait_for_state(START_WAIT);
inner->wait_for_state(START_WAIT);
EXPECT_EQ(START_WAIT, reader->state());
EXPECT_EQ(START_WAIT, outer->state());
EXPECT_EQ(START_WAIT, inner->state());
reader->proceed();
reader->wait_for_state(OUTER_ENTERED);
EXPECT_EQ(OUTER_ENTERED, reader->state());
EXPECT_EQ(START_WAIT, outer->state());
EXPECT_EQ(START_WAIT, inner->state());
outer->proceed();
outer->wait_for_state(SYNCHRONIZING);
EXPECT_EQ(OUTER_ENTERED, reader->state());
EXPECT_EQ(SYNCHRONIZING, outer->state());
EXPECT_EQ(START_WAIT, inner->state());
os::naked_short_sleep(100); // Give outer time in synchronization.
EXPECT_EQ(OUTER_ENTERED, reader->state());
EXPECT_EQ(SYNCHRONIZING, outer->state());
EXPECT_EQ(START_WAIT, inner->state());
reader->proceed();
reader->wait_for_state(INNER_ENTERED);
EXPECT_EQ(INNER_ENTERED, reader->state());
EXPECT_EQ(SYNCHRONIZING, outer->state());
EXPECT_EQ(START_WAIT, inner->state());
inner->proceed();
inner->wait_for_state(SYNCHRONIZING);
EXPECT_EQ(INNER_ENTERED, reader->state());
EXPECT_EQ(SYNCHRONIZING, outer->state());
EXPECT_EQ(SYNCHRONIZING, inner->state());
os::naked_short_sleep(100); // Give writers time in synchronization.
EXPECT_EQ(INNER_ENTERED, reader->state());
EXPECT_EQ(SYNCHRONIZING, outer->state());
EXPECT_EQ(SYNCHRONIZING, inner->state());
reader->proceed();
reader->wait_for_state(INNER_EXITED);
// inner does *not* complete synchronization here.
EXPECT_EQ(INNER_EXITED, reader->state());
EXPECT_EQ(SYNCHRONIZING, outer->state());
EXPECT_EQ(SYNCHRONIZING, inner->state());
os::naked_short_sleep(100); // Give writers more time in synchronization.
EXPECT_EQ(INNER_EXITED, reader->state());
EXPECT_EQ(SYNCHRONIZING, outer->state());
EXPECT_EQ(SYNCHRONIZING, inner->state());
reader->proceed();
reader->wait_for_state(OUTER_EXITED);
// Both inner and outer can synchronize now.
outer->wait_for_state(SYNCHRONIZED);
inner->wait_for_state(SYNCHRONIZED);
EXPECT_EQ(OUTER_EXITED, reader->state());
EXPECT_EQ(SYNCHRONIZED, outer->state());
EXPECT_EQ(SYNCHRONIZED, inner->state());
// Wait for reader, outer, and inner to complete.
reader->proceed();
outer->proceed();
inner->proceed();
for (uint i = 0; i < 3; ++i) {
post.wait();
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2018, 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
@ -56,29 +56,29 @@ import java.util.stream.Collectors;
* @run driver ClassFileInstaller sun.hotspot.WhiteBox
* sun.hotspot.WhiteBox$WhiteBoxPermission
*
* @run main/othervm -Xms200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.
* @run main/othervm -Xms200M -Xmx200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.
* -XX:+UnlockExperimentalVMOptions -XX:MaxGCPauseMillis=30000 -XX:G1MixedGCLiveThresholdPercent=100 -XX:G1HeapWastePercent=0
* -XX:G1HeapRegionSize=1M -Xlog:gc=info:file=TestObjectGraphAfterGC_MIXED_GC.gc.log -XX:MaxTenuringThreshold=1
* -XX:G1MixedGCCountTarget=1 -XX:G1OldCSetRegionThresholdPercent=100 -XX:SurvivorRatio=1 -XX:InitiatingHeapOccupancyPercent=0
* gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC MIXED_GC
*
* @run main/othervm -Xms200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.
* @run main/othervm -Xms200M -Xmx200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.
* -XX:G1HeapRegionSize=1M -Xlog:gc*=debug:file=TestObjectGraphAfterGC_YOUNG_GC.gc.log
* gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC YOUNG_GC
*
* @run main/othervm -Xms200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.
* @run main/othervm -Xms200M -Xmx200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.
* -XX:G1HeapRegionSize=1M -Xlog:gc=info:file=TestObjectGraphAfterGC_FULL_GC.gc.log
* gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC FULL_GC
*
* @run main/othervm -Xms200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.
* @run main/othervm -Xms200M -Xmx200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.
* -XX:G1HeapRegionSize=1M -Xlog:gc=info:file=TestObjectGraphAfterGC_FULL_GC_MEMORY_PRESSURE.gc.log
* gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC FULL_GC_MEMORY_PRESSURE
*
* @run main/othervm -Xms200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.
* @run main/othervm -Xms200M -Xmx200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.
* -XX:G1HeapRegionSize=1M -Xlog:gc=info:file=TestObjectGraphAfterGC_CMC.gc.log -XX:MaxTenuringThreshold=16
* gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC CMC
*
* @run main/othervm -Xms200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.
* @run main/othervm -Xms200M -Xmx200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.
* -XX:G1HeapRegionSize=1M -Xlog:gc=info:file=TestObjectGraphAfterGC_CMC_NO_SURV_ROOTS.gc.log -XX:MaxTenuringThreshold=1
* gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC CMC_NO_SURV_ROOTS
*

View File

@ -0,0 +1,222 @@
/*
* Copyright (c) 2018, 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.
*
*/
/*
* @test
* @summary Tests how CDS works when critical library classes are replaced with JVMTI ClassFileLoadHook
* @library /test/lib
* @requires vm.cds
* @build sun.hotspot.WhiteBox
* @run driver ClassFileInstaller -jar whitebox.jar sun.hotspot.WhiteBox
* @run main/othervm/native ReplaceCriticalClasses
*/
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import jdk.test.lib.cds.CDSTestUtils;
import jdk.test.lib.cds.CDSOptions;
import jdk.test.lib.process.OutputAnalyzer;
import sun.hotspot.WhiteBox;
public class ReplaceCriticalClasses {
public static void main(String args[]) throws Throwable {
if (args.length == 0) {
launchChildProcesses();
} else if (args.length == 3 && args[0].equals("child")) {
Class klass = Class.forName(args[2].replace("/", "."));
if (args[1].equals("-shared")) {
testInChild(true, klass);
} else if (args[1].equals("-notshared")) {
testInChild(false, klass);
} else {
throw new RuntimeException("Unknown child exec option " + args[1]);
}
return;
} else {
throw new RuntimeException("Usage: @run main/othervm/native ReplaceCriticalClasses");
}
}
static void launchChildProcesses() throws Throwable {
String tests[] = {
// CDS should be disabled -- these critical classes will be replaced
// because JvmtiExport::early_class_hook_env() is true.
"-early -notshared java/lang/Object",
"-early -notshared java/lang/String",
"-early -notshared java/lang/Cloneable",
"-early -notshared java/io/Serializable",
// CDS should not be disabled -- these critical classes cannot be replaced because
// JvmtiExport::early_class_hook_env() is false.
"java/lang/Object",
"java/lang/String",
"java/lang/Cloneable",
"java/io/Serializable",
// Try to replace classes that are used by the archived subgraph graphs.
"-subgraph java/util/ArrayList",
"-subgraph java/lang/module/ResolvedModule",
// Replace classes that are loaded after JVMTI_PHASE_PRIMORDIAL. It's OK to replace such
// classes even when CDS is enabled. Nothing bad should happen.
"-notshared jdk/internal/vm/PostVMInitHook",
"-notshared java/util/Locale",
"-notshared sun/util/locale/BaseLocale",
"-notshared java/lang/Readable",
};
int n = 0;
for (String s : tests) {
System.out.println("Test case[" + (n++) + "] = \"" + s + "\"");
String args[] = s.split("\\s+"); // split by space character
launchChild(args);
}
}
static void launchChild(String args[]) throws Throwable {
if (args.length < 1) {
throw new RuntimeException("Invalid test case. Should be <-early> <-subgraph> <-notshared> klassName");
}
String klassName = null;
String early = "";
boolean subgraph = false;
String shared = "-shared";
for (int i=0; i<args.length-1; i++) {
String opt = args[i];
if (opt.equals("-early")) {
early = "-early,";
} else if (opt.equals("-subgraph")) {
subgraph = true;
} else if (opt.equals("-notshared")) {
shared = opt;
} else {
throw new RuntimeException("Unknown option: " + opt);
}
}
klassName = args[args.length-1];
Class.forName(klassName.replace("/", ".")); // make sure it's a valid class
// We will pass an option like "-agentlib:SimpleClassFileLoadHook=java/util/Locale,XXX,XXX".
// The SimpleClassFileLoadHook agent would attempt to hook the java/util/Locale class
// but leave the class file bytes unchanged (it replaces all bytes "XXX" with "XXX", i.e.,
// a no-op). JVMTI doesn't check the class file bytes returned by the agent, so as long
// as the agent returns a buffer, it will not load the class from CDS, and will instead
// load the class by parsing the buffer.
//
// Note that for safety we don't change the contents of the class file bytes. If in the
// future JVMTI starts checking the contents of the class file bytes, this test would need
// to be updated. (You'd see the test case with java/util/Locale staring to fail).
String agent = "-agentlib:SimpleClassFileLoadHook=" + early + klassName + ",XXX,XXX";
CDSOptions opts = (new CDSOptions())
.setXShareMode("auto")
.setUseSystemArchive(true)
.setUseVersion(false)
.addSuffix("-showversion",
"-Xlog:cds",
"-XX:+UnlockDiagnosticVMOptions",
agent,
"-XX:+WhiteBoxAPI",
"-Xbootclasspath/a:" + ClassFileInstaller.getJarPath("whitebox.jar"));
if (subgraph) {
opts.addSuffix("-Xlog:cds+heap",
"-Xlog:class+load");
}
opts.addSuffix("ReplaceCriticalClasses",
"child",
shared,
klassName);
final boolean expectDisable = !early.equals("");
final boolean checkSubgraph = subgraph;
CDSTestUtils.run(opts).assertNormalExit(out -> {
if (expectDisable) {
out.shouldContain("UseSharedSpaces: CDS is disabled because early JVMTI ClassFileLoadHook is in use.");
System.out.println("CDS disabled as expected");
}
if (checkSubgraph) {
// As of 2018/10/21 the classes in the archived subgraphs won't be
// replaced because all archived subgraphs were loaded in JVMTI_PHASE_PRIMORDIAL.
//
// This is the first class to be loaded after JVMTI has exited JVMTI_PHASE_PRIMORDIAL.
// Make sure no subgraphs are loaded afterwards.
//
// Can't use out.shouldNotMatch() because that doesn't match across multiple lines.
String firstNonPrimordialClass = "jdk.jfr.internal.EventWriter";
String regexp = firstNonPrimordialClass + ".*initialize_from_archived_subgraph";
Pattern regex = Pattern.compile(regexp, Pattern.DOTALL);
Matcher matcher = regex.matcher(out.getStdout());
if (matcher.find()) {
out.reportDiagnosticSummary();
throw new RuntimeException("'" + regexp
+ "' found in stdout: '" + matcher.group() + "' \n");
}
}
});
}
static void testInChild(boolean shouldBeShared, Class klass) {
WhiteBox wb = WhiteBox.getWhiteBox();
if (shouldBeShared && !wb.isSharedClass(klass)) {
throw new RuntimeException(klass + " should be shared but but actually is not.");
}
if (!shouldBeShared && wb.isSharedClass(klass)) {
throw new RuntimeException(klass + " should not be shared but actually is.");
}
System.out.println("wb.isSharedClass(klass): " + wb.isSharedClass(klass) + " == " + shouldBeShared);
String strings[] = {
// interned strings from j.l.Object
"@",
"nanosecond timeout value out of range",
"timeoutMillis value is negative",
// interned strings from j.l.Integer
"0",
"0X",
"0x",
"int"
};
// Make sure the interned string table is same
for (String s : strings) {
String i = s.intern();
if (s != i) {
throw new RuntimeException("Interned string mismatch: \"" + s + "\" @ " + System.identityHashCode(s) +
" vs \"" + i + "\" @ " + System.identityHashCode(i));
}
}
// We have tried to use ClassFileLoadHook to replace critical library classes (which may
// may not have succeeded, depending on whether the agent has requested
// can_generate_all_class_hook_events/can_generate_early_class_hook_events capabilities).
//
// In any case, the JVM should have started properly (perhaps with CDS disabled) and
// the above operations should succeed.
System.out.println("If I can come to here without crashing, things should be OK");
}
}

View File

@ -52,7 +52,8 @@ public class CheckArchivedModuleApp {
boolean expectArchivedConfiguration = "yes".equals(args[1]);
// -XX:+EnableJVMCI adds extra system modules, in which case the system
// module objects are not archived.
if (wb.getBooleanVMFlag("EnableJVMCI")) {
Boolean enableJVMCI = wb.getBooleanVMFlag("EnableJVMCI");
if (enableJVMCI != null && enableJVMCI) {
expectArchivedDescriptors = false;
expectArchivedConfiguration = false;
}

View File

@ -0,0 +1,93 @@
/*
* Copyright (c) 2018, 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.
*
*/
/*
* @test HandshakeWalkSuspendExitTest
* @summary This test tries to stress the handshakes with new and exiting threads while suspending them.
* @library /testlibrary /test/lib
* @build HandshakeWalkSuspendExitTest
* @run driver ClassFileInstaller sun.hotspot.WhiteBox
* sun.hotspot.WhiteBox$WhiteBoxPermission
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI HandshakeWalkSuspendExitTest
*/
import jdk.test.lib.Asserts;
import sun.hotspot.WhiteBox;
public class HandshakeWalkSuspendExitTest implements Runnable {
static final int _test_threads = 8;
static final int _test_exit_threads = 128;
static Thread[] _threads = new Thread[_test_threads];
static volatile boolean exit_now = false;
static java.util.concurrent.Semaphore _sem = new java.util.concurrent.Semaphore(0);
@Override
public void run() {
WhiteBox wb = WhiteBox.getWhiteBox();
while (!exit_now) {
_sem.release();
// We only suspend threads on even index and not ourself.
// Otherwise we can accidentially suspend all threads.
for (int i = 0; i < _threads.length; i += 2) {
wb.handshakeWalkStack(null /* ignored */, true /* stackwalk all threads */);
if (Thread.currentThread() != _threads[i]) {
_threads[i].suspend();
_threads[i].resume();
}
}
for (int i = 0; i < _threads.length; i += 2) {
wb.handshakeWalkStack(_threads[i] /* thread to stackwalk */, false /* stackwalk one thread */);
if (Thread.currentThread() != _threads[i]) {
_threads[i].suspend();
_threads[i].resume();
}
}
}
}
public static void main(String... args) throws Exception {
HandshakeWalkSuspendExitTest test = new HandshakeWalkSuspendExitTest();
for (int i = 0; i < _threads.length; i++) {
_threads[i] = new Thread(test);
_threads[i].start();
}
for (int i = 0; i < _test_threads; i++) {
_sem.acquire();
}
Thread[] exit_threads = new Thread[_test_exit_threads];
for (int i = 0; i < _test_exit_threads; i++) {
exit_threads[i] = new Thread(new Runnable() { public void run() {} });
exit_threads[i].start();
}
exit_now = true;
for (int i = 0; i < _threads.length; i++) {
_threads[i].join();
}
for (int i = 0; i < exit_threads.length; i++) {
exit_threads[i].join();
}
}
}

Some files were not shown because too many files have changed in this diff Show More