--- a/doc/testing.html Tue Oct 30 10:39:19 2018 -0400
+++ b/doc/testing.html Wed Oct 31 10:48:13 2018 -0400
@@ -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 @@
<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 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>
--- a/doc/testing.md Tue Oct 30 10:39:19 2018 -0400
+++ b/doc/testing.md Wed Oct 31 10:48:13 2018 -0400
@@ -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 @@
## 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 @@
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 @@
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
--- a/make/Docs.gmk Tue Oct 30 10:39:19 2018 -0400
+++ b/make/Docs.gmk Wed Oct 31 10:48:13 2018 -0400
@@ -61,7 +61,7 @@
$(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
--- a/make/Help.gmk Tue Oct 30 10:39:19 2018 -0400
+++ b/make/Help.gmk Wed Oct 31 10:48:13 2018 -0400
@@ -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 @@
$(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 @@
$(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.))
--- a/make/Main.gmk Tue Oct 30 10:39:19 2018 -0400
+++ b/make/Main.gmk Wed Oct 31 10:48:13 2018 -0400
@@ -473,10 +473,10 @@
#
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 @@
# 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 @@
+($(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 @@
+($(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 @@
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))
-
-test-hotspot-jtreg:
- $(call RunTests, "hotspot_all", $(JDK_IMAGE_DIR))
-
-test-hotspot-jtreg-native:
- $(call RunTests, "hotspot_native_sanity", $(JDK_IMAGE_DIR))
-
-test-hotspot-internal:
- $(call RunTests, "hotspot_internal", $(JDK_OUTPUTDIR))
+ +($(CD) $(TOPDIR)/make && $(MAKE) $(MAKE_ARGS) -f RunTests.gmk run-test TEST="$(TEST)")
-test-hotspot-gtest:
- $(call RunTests, "hotspot_gtest", $(JDK_OUTPUTDIR))
-
-test-jdk-jtreg-native:
- $(call RunTests, "jdk_native_sanity", $(JDK_IMAGE_DIR))
+exploded-test:
+ +($(CD) $(TOPDIR)/make && $(MAKE) $(MAKE_ARGS) -f RunTests.gmk run-test \
+ TEST="$(TEST)" JDK_IMAGE_DIR=$(JDK_OUTPUTDIR))
-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 @@
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 @@
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 @@
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_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
+
################################################################################
################################################################################
#
--- a/make/MainSupport.gmk Tue Oct 30 10:39:19 2018 -0400
+++ b/make/MainSupport.gmk Wed Oct 31 10:48:13 2018 -0400
@@ -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)
--- a/make/RunTests.gmk Tue Oct 30 10:39:19 2018 -0400
+++ b/make/RunTests.gmk Wed Oct 31 10:48:13 2018 -0400
@@ -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 @@
# 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 @@
$$(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 \
--- a/make/common/FindTests.gmk Tue Oct 30 10:39:19 2018 -0400
+++ b/make/common/FindTests.gmk Wed Oct 31 10:48:13 2018 -0400
@@ -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 @@
# 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
--- a/make/jdk/src/classes/build/tools/taglet/ExtLink.java Tue Oct 30 10:39:19 2018 -0400
+++ b/make/jdk/src/classes/build/tools/taglet/ExtLink.java Wed Oct 31 10:48:13 2018 -0400
@@ -48,7 +48,7 @@
* 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 @@
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>.*)$");
--- a/src/hotspot/share/aot/aotLoader.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/aot/aotLoader.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -47,13 +47,8 @@
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 @@
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;
--- a/src/hotspot/share/c1/c1_Compiler.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/c1/c1_Compiler.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -79,7 +79,6 @@
}
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 @@
// 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);
}
--- a/src/hotspot/share/classfile/classLoaderDataGraph.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/classfile/classLoaderDataGraph.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -287,54 +287,50 @@
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::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::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::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 @@
}
#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 @@
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);
}
}
--- a/src/hotspot/share/classfile/dictionary.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/classfile/dictionary.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -468,7 +468,7 @@
#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();
--- a/src/hotspot/share/classfile/javaClasses.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/classfile/javaClasses.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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;
}
--- a/src/hotspot/share/classfile/systemDictionary.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/classfile/systemDictionary.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -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 @@
}
-// 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 @@
!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 @@
// 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 @@
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 @@
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 @@
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 @@
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).
--- a/src/hotspot/share/classfile/systemDictionary.hpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/classfile/systemDictionary.hpp Wed Oct 31 10:48:13 2018 -0400
@@ -85,19 +85,20 @@
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
+#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.
//
-// These klasses must all have names defined in vmSymbols.
-
-#define WK_KLASS_ENUM_NAME(kname) kname##_knum
-
// 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 @@
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 @@
/* 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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
InstanceKlass* k, Handle loader,
TRAPS);
- // Variables holding commonly used klasses (preloaded)
static InstanceKlass* _well_known_klasses[];
// table of box klasses (int_klass, etc.)
--- a/src/hotspot/share/code/codeCache.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/code/codeCache.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -274,10 +274,10 @@
}
// 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
--- a/src/hotspot/share/compiler/compileBroker.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/compiler/compileBroker.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -894,14 +894,19 @@
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 @@
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 @@
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 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
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -1043,7 +1043,7 @@
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 @@
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 @@
// 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 @@
// 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(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 @@
}
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 @@
_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 {
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp Wed Oct 31 10:48:13 2018 -0400
@@ -468,9 +468,6 @@
// 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 @@
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 @@
// 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 @@
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
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp Wed Oct 31 10:48:13 2018 -0400
@@ -60,6 +60,9 @@
// 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 @@
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);
}
--- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -1178,6 +1178,8 @@
ClassLoaderDataGraph::purge();
}
+ _g1h->resize_heap_if_necessary();
+
compute_new_sizes();
verify_during_pause(G1HeapVerifier::G1VerifyRemark, VerifyOption_G1UsePrevMarking, "Remark after");
--- a/src/hotspot/share/gc/g1/g1OopClosures.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/gc/g1/g1OopClosures.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -38,7 +38,7 @@
{ }
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) {
--- a/src/hotspot/share/gc/g1/g1OopClosures.hpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/gc/g1/g1OopClosures.hpp Wed Oct 31 10:48:13 2018 -0400
@@ -36,6 +36,7 @@
class DirtyCardToOopClosure;
class G1CMBitMap;
class G1ParScanThreadState;
+class G1ScanEvacuatedObjClosure;
class G1CMTask;
class ReferenceProcessor;
@@ -43,7 +44,6 @@
protected:
G1CollectedHeap* _g1h;
G1ParScanThreadState* _par_scan_state;
- HeapRegion* _from;
G1ScanClosureBase(G1CollectedHeap* g1h, G1ParScanThreadState* par_scan_state);
~G1ScanClosureBase() { }
@@ -56,24 +56,19 @@
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 @@
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 @@
}
};
+// 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:
--- a/src/hotspot/share/gc/g1/g1OopClosures.inline.hpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/gc/g1/g1OopClosures.inline.hpp Wed Oct 31 10:48:13 2018 -0400
@@ -82,12 +82,13 @@
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 @@
// 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 @@
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);
}
}
--- a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -311,8 +311,7 @@
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 @@
_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;
--- a/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp Wed Oct 31 10:48:13 2018 -0400
@@ -104,17 +104,16 @@
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));
}
}
--- a/src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp Wed Oct 31 10:48:13 2018 -0400
@@ -61,9 +61,12 @@
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 @@
// 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
--- a/src/hotspot/share/gc/g1/g1RemSet.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/gc/g1/g1RemSet.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -133,10 +133,10 @@
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 @@
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::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 @@
_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 @@
}
_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 @@
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 @@
{
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 @@
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 @@
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 @@
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 @@
// 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 @@
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;
--- a/src/hotspot/share/gc/g1/heapRegionManager.hpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/gc/g1/heapRegionManager.hpp Wed Oct 31 10:48:13 2018 -0400
@@ -123,10 +123,7 @@
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 @@
// 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;
--- a/src/hotspot/share/gc/g1/heapRegionManager.inline.hpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/gc/g1/heapRegionManager.inline.hpp Wed Oct 31 10:48:13 2018 -0400
@@ -47,6 +47,16 @@
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");
--- a/src/hotspot/share/gc/g1/heapRegionRemSet.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/gc/g1/heapRegionRemSet.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -239,10 +239,9 @@
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 @@
_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 @@
}
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 @@
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 @@
}
_n_fine_entries = 0;
_n_coarse_entries = 0;
-
- clear_fcc();
}
bool OtherRegionsTable::contains_reference(OopOrNarrowOopStar from) const {
@@ -627,11 +611,16 @@
: _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 @@
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 @@
_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 @@
_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
--- a/src/hotspot/share/gc/g1/heapRegionRemSet.hpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/gc/g1/heapRegionRemSet.hpp Wed Oct 31 10:48:13 2018 -0400
@@ -76,7 +76,6 @@
G1CollectedHeap* _g1h;
Mutex* _m;
- HeapRegion* _hr;
// These are protected by "_m".
CHeapBitMap _coarse_map;
@@ -124,11 +123,8 @@
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 @@
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 @@
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 @@
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 @@
};
class HeapRegionRemSetIterator : public StackObj {
- private:
+private:
// The region RSet over which we are iterating.
HeapRegionRemSet* _hrrs;
@@ -401,7 +414,7 @@
// 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
--- a/src/hotspot/share/gc/g1/sparsePRT.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/gc/g1/sparsePRT.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -361,8 +361,8 @@
}
-SparsePRT::SparsePRT(HeapRegion* hr) :
- _hr(hr), _expanded(false), _next_expanded(NULL)
+SparsePRT::SparsePRT() :
+ _expanded(false), _next_expanded(NULL)
{
_cur = new RSHashTable(InitialCapacity);
_next = _cur;
--- a/src/hotspot/share/gc/g1/sparsePRT.hpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/gc/g1/sparsePRT.hpp Wed Oct 31 10:48:13 2018 -0400
@@ -231,8 +231,6 @@
RSHashTable* _cur;
RSHashTable* _next;
- HeapRegion* _hr;
-
enum SomeAdditionalPrivateConstants {
InitialCapacity = 16
};
@@ -254,7 +252,7 @@
static SparsePRT* volatile _head_expanded_list;
public:
- SparsePRT(HeapRegion* hr);
+ SparsePRT();
~SparsePRT();
--- a/src/hotspot/share/gc/shared/gcTimer.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/gc/shared/gcTimer.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -182,214 +182,3 @@
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
--- a/src/hotspot/share/gc/shared/gcTimer.hpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/gc/shared/gcTimer.hpp Wed Oct 31 10:48:13 2018 -0400
@@ -129,7 +129,6 @@
};
class GCTimer : public ResourceObj {
- NOT_PRODUCT(friend class GCTimerTest;)
protected:
Ticks _gc_start;
Ticks _gc_end;
--- a/src/hotspot/share/jfr/instrumentation/jfrEventClassTransformer.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/jfr/instrumentation/jfrEventClassTransformer.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -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 @@
"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 @@
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 @@
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 @@
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 @@
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* 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;
}
- InstanceKlass* super = InstanceKlass::cast(ik->super());
- return registered_annotation_value(super, registered_symbol);
+ 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 @@
// 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 @@
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 @@
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 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 @@
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 @@
// 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 @@
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 @@
// 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 @@
// 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 @@
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
--- a/src/hotspot/share/jfr/jni/jfrGetAllEventClasses.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/jfr/jni/jfrGetAllEventClasses.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -132,7 +132,7 @@
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);
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -76,22 +76,43 @@
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;
+ }
}
}
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp Wed Oct 31 10:48:13 2018 -0400
@@ -112,10 +112,8 @@
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 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");
--- a/src/hotspot/share/memory/filemap.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/memory/filemap.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -914,6 +914,19 @@
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::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;
}
--- a/src/hotspot/share/memory/heapShared.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/memory/heapShared.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -417,6 +417,11 @@
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()) {
--- a/src/hotspot/share/opto/c2compiler.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/opto/c2compiler.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -604,7 +604,9 @@
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;
}
--- a/src/hotspot/share/opto/c2compiler.hpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/opto/c2compiler.hpp Wed Oct 31 10:48:13 2018 -0400
@@ -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 @@
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
--- a/src/hotspot/share/opto/compile.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/opto/compile.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -544,9 +544,7 @@
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);
--- a/src/hotspot/share/opto/graphKit.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/opto/graphKit.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -2116,8 +2116,17 @@
// 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;
}
--- a/src/hotspot/share/prims/jvmtiExport.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/prims/jvmtiExport.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -994,6 +994,20 @@
}
};
+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
--- a/src/hotspot/share/prims/jvmtiExport.hpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/prims/jvmtiExport.hpp Wed Oct 31 10:48:13 2018 -0400
@@ -328,6 +328,8 @@
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,
--- a/src/hotspot/share/runtime/compilationPolicy.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/runtime/compilationPolicy.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -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 @@
// 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;
--- a/src/hotspot/share/runtime/handshake.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/runtime/handshake.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -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 @@
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 @@
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 @@
// 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 @@
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 @@
// 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 @@
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::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 @@
_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 @@
// 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 @@
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 @@
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 @@
// 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);
--- a/src/hotspot/share/runtime/handshake.hpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/runtime/handshake.hpp Wed Oct 31 10:48:13 2018 -0400
@@ -60,7 +60,6 @@
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 @@
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);
};
--- a/src/hotspot/share/runtime/thread.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/runtime/thread.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -4276,9 +4276,6 @@
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.
{
--- a/src/hotspot/share/runtime/thread.hpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/runtime/thread.hpp Wed Oct 31 10:48:13 2018 -0400
@@ -1271,10 +1271,6 @@
return _handshake.has_operation();
}
- void cancel_handshake() {
- _handshake.cancel(this);
- }
-
void handshake_process_by_self() {
_handshake.process_by_self(this);
}
--- a/src/hotspot/share/runtime/threadSMR.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/runtime/threadSMR.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -989,11 +989,6 @@
// 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();
--- a/src/hotspot/share/runtime/tieredThresholdPolicy.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/runtime/tieredThresholdPolicy.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -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::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 @@
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 @@
}
#endif
- if (TieredStopAtLevel < CompLevel_full_optimization) {
+ if (c1_only) {
// No C2 compiler thread required
set_c1_count(count);
} else {
--- a/src/hotspot/share/utilities/concurrentHashTable.hpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/utilities/concurrentHashTable.hpp Wed Oct 31 10:48:13 2018 -0400
@@ -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 @@
protected:
Thread* _thread;
ConcurrentHashTable<VALUE, CONFIG, F>* _cht;
+ GlobalCounter::CSContext _cs_context;
public:
ScopedCS(Thread* thread, ConcurrentHashTable<VALUE, CONFIG, F>* cht);
~ScopedCS();
--- a/src/hotspot/share/utilities/concurrentHashTable.inline.hpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/utilities/concurrentHashTable.inline.hpp Wed Oct 31 10:48:13 2018 -0400
@@ -208,9 +208,10 @@
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 @@
inline ConcurrentHashTable<VALUE, CONFIG, F>::
ScopedCS::~ScopedCS()
{
- GlobalCounter::critical_section_end(_thread);
+ GlobalCounter::critical_section_end(_thread, _cs_context);
}
// BaseConfig
@@ -502,7 +503,7 @@
// 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 @@
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 @@
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>
--- a/src/hotspot/share/utilities/globalCounter.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/utilities/globalCounter.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -59,8 +59,8 @@
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;
--- a/src/hotspot/share/utilities/globalCounter.hpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/utilities/globalCounter.hpp Wed Oct 31 10:48:13 2018 -0400
@@ -33,8 +33,9 @@
// 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 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;
};
--- a/src/hotspot/share/utilities/globalCounter.inline.hpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/utilities/globalCounter.inline.hpp Wed Oct 31 10:48:13 2018 -0400
@@ -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);
}
};
--- a/src/hotspot/share/utilities/internalVMTests.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/utilities/internalVMTests.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -43,8 +43,6 @@
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");
}
--- a/src/hotspot/share/utilities/singleWriterSynchronizer.hpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/utilities/singleWriterSynchronizer.hpp Wed Oct 31 10:48:13 2018 -0400
@@ -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];
--- a/src/hotspot/share/utilities/ticks.hpp Tue Oct 30 10:39:19 2018 -0400
+++ b/src/hotspot/share/utilities/ticks.hpp Wed Oct 31 10:48:13 2018 -0400
@@ -233,7 +233,7 @@
TimeInstant(jlong ticks) : Rep<TimeSource>(ticks) {}
friend class GranularTimer;
friend class ObjectSample;
- // GC VM tests
+ // GC unit tests
friend class TimePartitionPhasesIteratorTest;
friend class GCTimerTest;
};
--- a/src/java.base/share/classes/java/lang/StringConcatHelper.java Tue Oct 30 10:39:19 2018 -0400
+++ b/src/java.base/share/classes/java/lang/StringConcatHelper.java Wed Oct 31 10:48:13 2018 -0400
@@ -37,119 +37,104 @@
}
/**
- * 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
- */
- 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
+ * @return new length and 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 @@
buf[--index] = 'a';
buf[--index] = 'f';
}
+ return index;
} else {
if (value) {
StringUTF16.putChar(buf, --index, 'e');
@@ -175,72 +161,72 @@
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 @@
* 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 @@
* 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;
}
}
--- a/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java Tue Oct 30 10:39:19 2018 -0400
+++ b/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java Wed Oct 31 10:48:13 2018 -0400
@@ -318,6 +318,13 @@
}
@Override
+ public String toString() {
+ return "Recipe{" +
+ "elements=" + elements +
+ '}';
+ }
+
+ @Override
public int hashCode() {
return elements.hashCode();
}
@@ -368,6 +375,15 @@
}
@Override
+ public String toString() {
+ return "RecipeElement{" +
+ "value='" + value + '\'' +
+ ", argPos=" + argPos +
+ ", tag=" + tag +
+ '}';
+ }
+
+ @Override
public int hashCode() {
return (int)tag;
}
@@ -1520,24 +1536,24 @@
}
// 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 @@
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 @@
// 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 @@
// 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);
-
- 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);
+ MethodHandle mix = mixer(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 @@
}
}
- // 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 @@
}
@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 @@
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);
}
}
--- a/src/java.base/share/classes/java/time/chrono/JapaneseEra.java Tue Oct 30 10:39:19 2018 -0400
+++ b/src/java.base/share/classes/java/time/chrono/JapaneseEra.java Wed Oct 31 10:48:13 2018 -0400
@@ -92,6 +92,11 @@
* 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 @@
/**
* 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 @@
* <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 @@
}
/**
- * 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>
--- a/src/java.base/share/classes/javax/crypto/KeyAgreement.java Tue Oct 30 10:39:19 2018 -0400
+++ b/src/java.base/share/classes/javax/crypto/KeyAgreement.java Wed Oct 31 10:48:13 2018 -0400
@@ -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 @@
/**
* 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 @@
* 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 @@
* @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 @@
* 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
--- a/src/java.base/share/classes/javax/crypto/KeyAgreementSpi.java Tue Oct 30 10:39:19 2018 -0400
+++ b/src/java.base/share/classes/javax/crypto/KeyAgreementSpi.java Wed Oct 31 10:48:13 2018 -0400
@@ -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 @@
/**
* 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 @@
* 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 @@
* @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 @@
* 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/jdk/internal/event/Event.java Wed Oct 31 10:48:13 2018 -0400
@@ -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) {
+ }
+}
--- a/src/java.base/share/classes/module-info.java Tue Oct 30 10:39:19 2018 -0400
+++ b/src/java.base/share/classes/module-info.java Wed Oct 31 10:48:13 2018 -0400
@@ -136,6 +136,8 @@
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
--- a/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java Tue Oct 30 10:39:19 2018 -0400
+++ b/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java Wed Oct 31 10:48:13 2018 -0400
@@ -63,7 +63,7 @@
"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,
--- a/src/java.base/unix/native/jspawnhelper/jspawnhelper.c Tue Oct 30 10:39:19 2018 -0400
+++ b/src/java.base/unix/native/jspawnhelper/jspawnhelper.c Wed Oct 31 10:48:13 2018 -0400
@@ -49,7 +49,10 @@
#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);
}
--- a/src/java.xml/share/classes/javax/xml/validation/package-info.java Tue Oct 30 10:39:19 2018 -0400
+++ b/src/java.xml/share/classes/javax/xml/validation/package-info.java Wed Oct 31 10:48:13 2018 -0400
@@ -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>
--- a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDHKeyAgreement.java Tue Oct 30 10:39:19 2018 -0400
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDHKeyAgreement.java Wed Oct 31 10:48:13 2018 -0400
@@ -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 @@
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);
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/ValueTaglet.java Tue Oct 30 10:39:19 2018 -0400
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/ValueTaglet.java Wed Oct 31 10:48:13 2018 -0400
@@ -61,9 +61,7 @@
* 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));
}
/**
--- a/src/jdk.jfr/share/classes/jdk/jfr/Event.java Tue Oct 30 10:39:19 2018 -0400
+++ b/src/jdk.jfr/share/classes/jdk/jfr/Event.java Wed Oct 31 10:48:13 2018 -0400
@@ -88,7 +88,7 @@
@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.
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java Tue Oct 30 10:39:19 2018 -0400
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java Wed Oct 31 10:48:13 2018 -0400
@@ -41,7 +41,6 @@
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 @@
}
}
- EventControl(PlatformEventType es, Class<? extends Event> eventClass) {
+ EventControl(PlatformEventType es, Class<? extends jdk.internal.event.Event> eventClass) {
this(es);
defineSettings(eventClass);
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventHandlerCreator.java Tue Oct 30 10:39:19 2018 -0400
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventHandlerCreator.java Wed Oct 31 10:48:13 2018 -0400
@@ -93,11 +93,11 @@
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.
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventInstrumentation.java Tue Oct 30 10:39:19 2018 -0400
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventInstrumentation.java Wed Oct 31 10:48:13 2018 -0400
@@ -102,6 +102,7 @@
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 @@
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 @@
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 @@
}
}
}
- 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 @@
}
}
return settingInfos;
-
}
private static List<FieldInfo> buildFieldInfos(Class<?> superClass, ClassNode classNode) {
@@ -264,14 +274,13 @@
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 @@
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 @@
// 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 @@
// 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 @@
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 @@
});
}
+ 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);
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java Tue Oct 30 10:39:19 2018 -0400
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java Wed Oct 31 10:48:13 2018 -0400
@@ -106,11 +106,11 @@
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}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/JVMUpcalls.java Tue Oct 30 10:39:19 2018 -0400
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/JVMUpcalls.java Wed Oct 31 10:48:13 2018 -0400
@@ -26,7 +26,6 @@
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 @@
*/
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
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataRepository.java Tue Oct 30 10:39:19 2018 -0400
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataRepository.java Wed Oct 31 10:48:13 2018 -0400
@@ -33,6 +33,7 @@
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 @@
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 @@
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 @@
}
// 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 @@
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 @@
}
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 @@
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 @@
}
}
- 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());
+ }
+
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/MirrorEvent.java Wed Oct 31 10:48:13 2018 -0400
@@ -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";
+}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformRecorder.java Tue Oct 30 10:39:19 2018 -0400
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformRecorder.java Wed Oct 31 10:48:13 2018 -0400
@@ -81,9 +81,7 @@
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 @@
timer = createTimer();
}
+
private static Timer createTimer() {
try {
List<Timer> result = new CopyOnWriteArrayList<>();
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/SecuritySupport.java Tue Oct 30 10:39:19 2018 -0400
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/SecuritySupport.java Wed Oct 31 10:48:13 2018 -0400
@@ -262,8 +262,12 @@
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) {
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/SettingsManager.java Tue Oct 30 10:39:19 2018 -0400
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/SettingsManager.java Wed Oct 31 10:48:13 2018 -0400
@@ -37,7 +37,6 @@
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 @@
}
}
- 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();
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/Type.java Tue Oct 30 10:39:19 2018 -0400
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/Type.java Wed Oct 31 10:48:13 2018 -0400
@@ -71,10 +71,11 @@
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 boolean getRemove() {
return remove;
}
+
+ public void setId(long id) {
+ this.id = id;
+ }
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/TypeLibrary.java Tue Oct 30 10:39:19 2018 -0400
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/TypeLibrary.java Wed Oct 31 10:48:13 2018 -0400
@@ -49,7 +49,6 @@
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 @@
// 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 @@
aQ.addAll(ae.getAnnotationElements());
}
}
+
+ public void removeType(long id) {
+ types.remove(id);
+ }
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/Utils.java Tue Oct 30 10:39:19 2018 -0400
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/Utils.java Wed Oct 31 10:48:13 2018 -0400
@@ -267,7 +267,7 @@
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 @@
}
}
- 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 @@
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 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 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 @@
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());
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/JDKEvents.java Tue Oct 30 10:39:19 2018 -0400
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/JDKEvents.java Wed Oct 31 10:48:13 2018 -0400
@@ -51,6 +51,9 @@
public final class JDKEvents {
+ private static final Class<?>[] mirrorEventClasses = {
+ };
+
private static final Class<?>[] eventClasses = {
FileForceEvent.class,
FileReadEvent.class,
@@ -90,6 +93,9 @@
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);
}
--- a/test/Makefile Tue Oct 30 10:39:19 2018 -0400
+++ b/test/Makefile Wed Oct 31 10:48:13 2018 -0400
@@ -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; \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/gtest/gc/shared/test_gcTimer.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -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";
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/gtest/memory/test_metaspace.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -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);
+}
--- a/test/hotspot/gtest/utilities/test_globalCounter.cpp Tue Oct 30 10:39:19 2018 -0400
+++ b/test/hotspot/gtest/utilities/test_globalCounter.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -47,11 +47,11 @@
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);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/gtest/utilities/test_globalCounter_nested.cpp Wed Oct 31 10:48:13 2018 -0400
@@ -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();
+ }
+}
--- a/test/hotspot/jtreg/gc/g1/humongousObjects/objectGraphTest/TestObjectGraphAfterGC.java Tue Oct 30 10:39:19 2018 -0400
+++ b/test/hotspot/jtreg/gc/g1/humongousObjects/objectGraphTest/TestObjectGraphAfterGC.java Wed Oct 31 10:48:13 2018 -0400
@@ -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 @@
* @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
*
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/SharedArchiveFile/serviceability/ReplaceCriticalClasses.java Wed Oct 31 10:48:13 2018 -0400
@@ -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");
+ }
+}
--- a/test/hotspot/jtreg/runtime/appcds/cacheObject/CheckArchivedModuleApp.java Tue Oct 30 10:39:19 2018 -0400
+++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/CheckArchivedModuleApp.java Wed Oct 31 10:48:13 2018 -0400
@@ -52,7 +52,8 @@
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;
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/handshake/HandshakeWalkSuspendExitTest.java Wed Oct 31 10:48:13 2018 -0400
@@ -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();
+ }
+ }
+}
--- a/test/hotspot/jtreg/testlibrary/jvmti/libSimpleClassFileLoadHook.c Tue Oct 30 10:39:19 2018 -0400
+++ b/test/hotspot/jtreg/testlibrary/jvmti/libSimpleClassFileLoadHook.c Wed Oct 31 10:48:13 2018 -0400
@@ -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
@@ -91,17 +91,23 @@
*new_class_data_len = class_data_len;
*new_class_data = new_data;
- fprintf(stderr, "Rewriting done. Replaced %d occurrence(s)\n", count);
+ fprintf(stderr, "Rewriting done. Replaced %d occurrence(s) of \"%s\" to \"%s\"\n", count, FROM, TO);
}
}
}
+static int early = 0;
+
static jint init_options(char *options) {
char* class_name;
char* from;
char* to;
fprintf(stderr, "Agent library loaded with options = %s\n", options);
+ if (options != NULL && strncmp(options, "-early,", 7) == 0) {
+ early = 1;
+ options += 7;
+ }
if ((class_name = options) != NULL &&
(from = strchr(class_name, ',')) != NULL && (from[1] != 0)) {
*from = 0;
@@ -132,6 +138,7 @@
static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
int rc;
+ jvmtiCapabilities caps;
if ((rc = (*jvm)->GetEnv(jvm, (void **)&jvmti, JVMTI_VERSION_1_1)) != JNI_OK) {
fprintf(stderr, "Unable to create jvmtiEnv, GetEnv failed, error = %d\n", rc);
@@ -141,6 +148,19 @@
return JNI_ERR;
}
+ memset(&caps, 0, sizeof(caps));
+
+ caps.can_redefine_classes = 1;
+ if (early) {
+ fprintf(stderr, "can_generate_all_class_hook_events/can_generate_early_vmstart/can_generate_early_class_hook_events == 1\n");
+ caps.can_generate_all_class_hook_events = 1;
+ caps.can_generate_early_class_hook_events = 1;
+ }
+ if ((rc = (*jvmti)->AddCapabilities(jvmti, &caps)) != JNI_OK) {
+ fprintf(stderr, "AddCapabilities failed, error = %d\n", rc);
+ return JNI_ERR;
+ }
+
(void) memset(&callbacks, 0, sizeof(callbacks));
callbacks.ClassFileLoadHook = &ClassFileLoadHook;
if ((rc = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks))) != JNI_OK) {
--- a/test/jdk/java/security/KeyAgreement/KeyAgreementTest.java Tue Oct 30 10:39:19 2018 -0400
+++ b/test/jdk/java/security/KeyAgreement/KeyAgreementTest.java Wed Oct 31 10:48:13 2018 -0400
@@ -23,7 +23,7 @@
/*
* @test
- * @bug 4936763 8184359
+ * @bug 4936763 8184359 8205476
* @summary KeyAgreement Test with all supported algorithms from JCE.
* Arguments order <KeyExchangeAlgorithm> <KeyGenAlgorithm> <Provider>
* It removes com/sun/crypto/provider/KeyAgreement/DHGenSecretKey.java
@@ -150,5 +150,17 @@
if (!Arrays.equals(secret1, secret2)) {
throw new Exception("KeyAgreement secret mismatch.");
}
+
+ // ensure that a new secret cannot be produced before the next doPhase
+ try {
+ ka2.generateSecret();
+ throw new RuntimeException("state not reset");
+ } catch (IllegalStateException ex) {
+ // this is expected
+ }
+
+ // calling doPhase and then generateSecret should succeed
+ ka2.doPhase(kp1.getPublic(), true);
+ ka2.generateSecret();
}
}
--- a/test/jdk/jdk/jfr/jvm/TestGetAllEventClasses.java Tue Oct 30 10:39:19 2018 -0400
+++ b/test/jdk/jdk/jfr/jvm/TestGetAllEventClasses.java Wed Oct 31 10:48:13 2018 -0400
@@ -104,8 +104,9 @@
}
@SafeVarargs
+ @SuppressWarnings("rawtypes")
private static void assertEvents(JVM jvm, boolean inclusion, Class<? extends Event>... targetEvents) {
- final List<Class<? extends Event>> list = jvm.getAllEventClasses();
+ final List list = jvm.getAllEventClasses();
for (Class<? extends Event> ev : targetEvents) {
if (list.contains(ev)) {
if (inclusion) {
--- a/test/langtools/ProblemList.txt Tue Oct 30 10:39:19 2018 -0400
+++ b/test/langtools/ProblemList.txt Wed Oct 31 10:48:13 2018 -0400
@@ -56,11 +56,6 @@
tools/javac/modules/SourceInSymlinkTest.java 8180263 windows-all fails when run on a subst drive
tools/javac/importscope/T8193717.java 8203925 generic-all the test requires too much memory
-
-tools/javac/options/smokeTests/OptionSmokeTest.java 8205493 generic-all hard-coded release values in strings
-
-tools/javac/file/zip/8003512/LoadClassFromJava6CreatedJarTest.java 8206874 generic-all test requires a JDK 6 environment to be useful
-
###########################################################################
#
# javap
--- a/test/langtools/jdk/javadoc/doclet/testTaglets/TestTaglets.out Tue Oct 30 10:39:19 2018 -0400
+++ b/test/langtools/jdk/javadoc/doclet/testTaglets/TestTaglets.out Wed Oct 31 10:48:13 2018 -0400
@@ -26,5 +26,5 @@
@throws: ........ ...... ....... .... constructor method ..... ...... ........
@treatAsPrivate: ........ ...... ....... type ........... method field ...... ........
@uses: ........ module ....... .... ........... ...... ..... ...... ........
- {@value}: overview ...... package type constructor method field inline ........
+ {@value}: overview module package type constructor method field inline ........
@version: overview module package type ........... ...... ..... ...... disabled
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/jdk/javadoc/doclet/testValueTag/TestValueTagInModule.java Wed Oct 31 10:48:13 2018 -0400
@@ -0,0 +1,80 @@
+/*
+ * 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
+ * @bug 8210244
+ * @summary {@value} should be permitted in module documentation
+ * @library /tools/lib ../lib
+ * @modules jdk.javadoc/jdk.javadoc.internal.tool
+ * @build JavadocTester
+ * @run main TestValueTagInModule
+ */
+
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import toolbox.ModuleBuilder;
+import toolbox.ToolBox;
+
+public class TestValueTagInModule extends JavadocTester {
+
+ final ToolBox tb;
+
+ public static void main(String... args) throws Exception {
+ TestValueTagInModule tester = new TestValueTagInModule();
+ tester.runTests(m -> new Object[]{Paths.get(m.getName())});
+ }
+
+ TestValueTagInModule() {
+ tb = new ToolBox();
+ }
+
+ @Test
+ void test(Path base) throws Exception {
+ Path srcDir = base.resolve("src");
+ createTestClass(srcDir);
+
+ Path outDir = base.resolve("out");
+ javadoc("-d", outDir.toString(),
+ "--module-source-path", srcDir.toString(),
+ "--module", "m1");
+
+ checkExit(Exit.OK);
+
+ checkOutput("m1/module-summary.html", true,
+ "<a id=\"module.description\">\n"
+ + "<!-- -->\n"
+ + "</a>\n"
+ + "<div class=\"block\">value of field CONS : <a href=\"pkg/A.html#CONS\">100</a></div>");
+ }
+
+ void createTestClass(Path srcDir) throws Exception {
+ new ModuleBuilder(tb, "m1")
+ .comment("value of field CONS : {@value pkg.A#CONS}")
+ .exports("pkg")
+ .classes("package pkg; public class A{ public static final int CONS = 100;}")
+ .write(srcDir);
+ }
+}
--- a/test/langtools/tools/javac/options/smokeTests/OptionSmokeTest.java Tue Oct 30 10:39:19 2018 -0400
+++ b/test/langtools/tools/javac/options/smokeTests/OptionSmokeTest.java Wed Oct 31 10:48:13 2018 -0400
@@ -32,9 +32,11 @@
* jdk.compiler/com.sun.tools.javac.util
* jdk.jdeps/com.sun.tools.javap
* @build toolbox.ToolBox toolbox.JavacTask toolbox.TestRunner
- * @run main OptionSmokeTest
+ * @run main/othervm OptionSmokeTest
*/
+import java.util.Locale;
+
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -51,6 +53,7 @@
public OptionSmokeTest() {
super(System.err);
+ Locale.setDefault(Locale.US);
}
protected void runTests() throws Exception {
@@ -117,7 +120,7 @@
@Test
public void requiresArg(Path base) throws Exception {
- doTestNoSource(base, "error: -target requires an argument", "-target");
+ doTestNoSource(base, "error: --target requires an argument", "-target");
}
@Test
@@ -209,7 +212,7 @@
@Test
public void optionCantBeUsedWithRelease(Path base) throws Exception {
- doTestNoSource(base, "error: option -source cannot be used together with --release",
+ doTestNoSource(base, "error: option --source cannot be used together with --release",
String.format("--release %s -source %s", Source.DEFAULT.name, Source.DEFAULT.name));
}
@@ -232,9 +235,9 @@
String.format("--release %s -endorseddirs any", Source.DEFAULT.name));
doTestNoSource(base, "error: option -extdirs cannot be used together with --release",
String.format("--release %s -extdirs any", Source.DEFAULT.name));
- doTestNoSource(base, "error: option -source cannot be used together with --release",
+ doTestNoSource(base, "error: option --source cannot be used together with --release",
String.format("--release %s -source %s", Source.MIN.name, Source.DEFAULT.name));
- doTestNoSource(base, "error: option -target cannot be used together with --release",
+ doTestNoSource(base, "error: option --target cannot be used together with --release",
String.format("--release %s -target %s", Source.MIN.name, Source.DEFAULT.name));
doTestNoSource(base, "error: option --system cannot be used together with --release",
String.format("--release %s --system none", Source.DEFAULT.name));
--- a/test/lib/jdk/test/lib/cds/CDSOptions.java Tue Oct 30 10:39:19 2018 -0400
+++ b/test/lib/jdk/test/lib/cds/CDSOptions.java Wed Oct 31 10:48:13 2018 -0400
@@ -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
@@ -31,6 +31,7 @@
public String archiveName;
public ArrayList<String> prefix = new ArrayList<String>();
public ArrayList<String> suffix = new ArrayList<String>();
+ public boolean useSystemArchive = false;
// Indicate whether to append "-version" when using CDS Archive.
// Most of tests will use '-version'
@@ -68,4 +69,9 @@
this.useVersion = use;
return this;
}
+
+ public CDSOptions setUseSystemArchive(boolean use) {
+ this.useSystemArchive = use;
+ return this;
+ }
}
--- a/test/lib/jdk/test/lib/cds/CDSTestUtils.java Tue Oct 30 10:39:19 2018 -0400
+++ b/test/lib/jdk/test/lib/cds/CDSTestUtils.java Wed Oct 31 10:48:13 2018 -0400
@@ -82,21 +82,21 @@
*
* Instead, the test case should be written as
*
- * CCDSTestUtils.run(args).assertNormalExit("Hi");
+ * CDSTestUtils.run(args).assertNormalExit("Hi");
*
* EXAMPLES/HOWTO
*
* 1. For simple substring matching:
*
- * CCDSTestUtils.run(args).assertNormalExit("Hi");
- * CCDSTestUtils.run(args).assertNormalExit("a", "b", "x");
- * CCDSTestUtils.run(args).assertAbnormalExit("failure 1", "failure2");
+ * CDSTestUtils.run(args).assertNormalExit("Hi");
+ * CDSTestUtils.run(args).assertNormalExit("a", "b", "x");
+ * CDSTestUtils.run(args).assertAbnormalExit("failure 1", "failure2");
*
* 2. For more complex output matching: using Lambda expressions
*
- * CCDSTestUtils.run(args)
+ * CDSTestUtils.run(args)
* .assertNormalExit(output -> output.shouldNotContain("this should not be printed");
- * CCDSTestUtils.run(args)
+ * CDSTestUtils.run(args)
* .assertAbnormalExit(output -> {
* output.shouldNotContain("this should not be printed");
* output.shouldHaveExitValue(123);
@@ -104,13 +104,13 @@
*
* 3. Chaining several checks:
*
- * CCDSTestUtils.run(args)
+ * CDSTestUtils.run(args)
* .assertNormalExit(output -> output.shouldNotContain("this should not be printed")
* .assertNormalExit("should have this", "should have that");
*
* 4. [Rare use case] if a test sometimes exit normally, and sometimes abnormally:
*
- * CCDSTestUtils.run(args)
+ * CDSTestUtils.run(args)
* .ifNormalExit("ths string is printed when exiting with 0")
* .ifAbNormalExit("ths string is printed when exiting with 1");
*
@@ -388,9 +388,11 @@
cmd.add("-Xshare:" + opts.xShareMode);
cmd.add("-Dtest.timeout.factor=" + TestTimeoutFactor);
- if (opts.archiveName == null)
- opts.archiveName = getDefaultArchiveName();
- cmd.add("-XX:SharedArchiveFile=" + opts.archiveName);
+ if (!opts.useSystemArchive) {
+ if (opts.archiveName == null)
+ opts.archiveName = getDefaultArchiveName();
+ cmd.add("-XX:SharedArchiveFile=" + opts.archiveName);
+ }
if (opts.useVersion)
cmd.add("-version");
--- a/test/make/TestMake.gmk Tue Oct 30 10:39:19 2018 -0400
+++ b/test/make/TestMake.gmk Wed Oct 31 10:48:13 2018 -0400
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2014, 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
@@ -36,13 +36,17 @@
copy-files:
+$(MAKE) -f TestCopyFiles.gmk $(TEST_SUBTARGET)
-test-idea:
+idea:
+$(MAKE) -f TestIdea.gmk $(TEST_SUBTARGET)
-test-compile-commands:
+compile-commands:
+$(MAKE) -f TestCompileCommands.gmk $(TEST_SUBTARGET)
+TARGETS += make-base java-compilation copy-files idea compile-commands
-all: make-base java-compilation copy-files test-idea
+all: $(TARGETS)
-.PHONY: default all make-base java-compilation copy-files test-idea test-compile-commands
+print-targets:
+ $(ECHO) "$(TARGETS)"
+
+.PHONY: default all $(TARGETS)
--- a/test/make/TestMakeBase.gmk Tue Oct 30 10:39:19 2018 -0400
+++ b/test/make/TestMakeBase.gmk Wed Oct 31 10:48:13 2018 -0400
@@ -344,7 +344,7 @@
KWBASE := APA=banan;GURKA=tomat;COUNT=1%202%203%204%205;SUM=1+2+3+4+5;MANY_WORDS=I have the best words.
$(eval $(call ParseKeywordVariable, KWBASE, \
- KEYWORDS := APA GURKA SUM, \
+ SINGLE_KEYWORDS := APA GURKA SUM, \
STRING_KEYWORDS := COUNT MANY_WORDS, \
))
@@ -377,7 +377,7 @@
KWBASE_WEIRD := ;;APA=banan;;;GURKA=apelsin;APA=skansen;;
$(eval $(call ParseKeywordVariable, KWBASE_WEIRD, \
- KEYWORDS := APA GURKA SUM, \
+ SINGLE_KEYWORDS := APA GURKA SUM, \
STRING_KEYWORDS := COUNT, \
))