Merge
This commit is contained in:
commit
f161afb806
@ -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 "make test" (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 "make test" (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 "run-test" uses the jdk-image as the tested product. There is also an alternate target "exploded-run-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.</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 && make</code>.</p>
|
||||
<p>Some example command-lines:</p>
|
||||
<pre><code>$ 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</code></pre>
|
||||
<pre><code>$ 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</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=<path to jtreg home></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="tier1"</code>, but the latter is more tab-completion friendly. For more complex test runs, the <code>run-test TEST="x"</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="tier1"</code>, but the latter is more tab-completion friendly. For more complex test runs, the <code>test TEST="x"</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>/<variant></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>>> ... <<</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>
|
||||
|
@ -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
|
||||
|
@ -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)&id=homepage
|
||||
JAVADOC_BASE_URL := https://docs.oracle.com/pls/topic/lookup?ctx=javase$(VERSION_NUMBER)&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
|
||||
|
@ -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.))
|
||||
|
101
make/Main.gmk
101
make/Main.gmk
@ -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
|
||||
|
||||
################################################################################
|
||||
################################################################################
|
||||
#
|
||||
|
@ -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)
|
||||
|
@ -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 \
|
||||
|
@ -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
|
||||
|
@ -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 + "&id=";
|
||||
|
||||
static final Pattern TAG_PATTERN = Pattern.compile("(?s)(\\s*)(?<name>\\w+)(\\s+)(?<desc>.*)$");
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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).
|
||||
|
@ -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.)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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) {
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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
|
||||
|
@ -129,7 +129,6 @@ class PhasesIterator {
|
||||
};
|
||||
|
||||
class GCTimer : public ResourceObj {
|
||||
NOT_PRODUCT(friend class GCTimerTest;)
|
||||
protected:
|
||||
Ticks _gc_start;
|
||||
Ticks _gc_end;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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()) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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.
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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 {
|
||||
|
@ -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();
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
|
@ -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];
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
94
src/java.base/share/classes/jdk/internal/event/Event.java
Normal file
94
src/java.base/share/classes/jdk/internal/event/Event.java
Normal 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) {
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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}
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
25
src/jdk.jfr/share/classes/jdk/jfr/internal/MirrorEvent.java
Normal file
25
src/jdk.jfr/share/classes/jdk/jfr/internal/MirrorEvent.java
Normal 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";
|
||||
}
|
@ -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<>();
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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; \
|
||||
|
238
test/hotspot/gtest/gc/shared/test_gcTimer.cpp
Normal file
238
test/hotspot/gtest/gc/shared/test_gcTimer.cpp
Normal 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";
|
||||
}
|
86
test/hotspot/gtest/memory/test_metaspace.cpp
Normal file
86
test/hotspot/gtest/memory/test_metaspace.cpp
Normal 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);
|
||||
}
|
@ -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);
|
||||
|
208
test/hotspot/gtest/utilities/test_globalCounter_nested.cpp
Normal file
208
test/hotspot/gtest/utilities/test_globalCounter_nested.cpp
Normal 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();
|
||||
}
|
||||
}
|
@ -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
|
||||
*
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user