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

View File

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

View File

@ -1,26 +1,32 @@
% Testing the JDK % 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 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, 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 and more complex ad-hoc combination of tests is possible. The user interface is
forgiving, and clearly report errors it cannot resolve. forgiving, and clearly report errors it cannot resolve.
The main target "run-test" uses the jdk-image as the tested product. There is The main target `test` uses the jdk-image as the tested product. There is
also an alternate target "exploded-run-test" that uses the exploded image also an alternate target `exploded-test` that uses the exploded image
instead. Not all tests will run successfully on the exploded image, but using instead. Not all tests will run successfully on the exploded image, but using
this target can greatly improve rebuild times for certain workflows. 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: Some example command-lines:
$ make run-test-tier1 $ make test-tier1
$ make run-test-jdk_lang JTREG="JOBS=8" $ make test-jdk_lang JTREG="JOBS=8"
$ make run-test TEST=jdk_lang $ make test TEST=jdk_lang
$ make run-test-only TEST="gtest:LogTagSet gtest:LogTagSetDescriptions" GTEST="REPEAT=-1" $ make 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 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 test TEST="jtreg:test/hotspot:hotspot_gc test/hotspot/jtreg/native_sanity/JniVersion.java"
$ make exploded-run-test TEST=tier2 $ make exploded-test TEST=tier2
### Configuration ### Configuration
@ -33,16 +39,16 @@ environment variable to point to the JTReg home before running `configure`.)
## Test selection ## Test selection
All functionality is available using the run-test make target. In this use All functionality is available using the `test` make target. In this use case,
case, the test or tests to be executed is controlled using the `TEST` variable. the test or tests to be executed is controlled using the `TEST` variable. To
To speed up subsequent test runs with no source code changes, run-test-only can speed up subsequent test runs with no source code changes, `test-only` can be
be used instead, which do not depend on the source and test image build. 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 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 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="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 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 descriptors, which clearly and unambigously show which tests will be run. As an
@ -98,6 +104,27 @@ is defined by adding `/<variant>` to the test descriptor, e.g.
variant present (e.g. server, client). So if you only have the server JVM 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`. 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 ## Test results and summary
At the end of the test run, a summary of all tests run will be presented. This At the end of the test run, a summary of all tests run will be presented. This
@ -123,7 +150,7 @@ the summary, ERROR is used as a catch-all for tests that neither passed nor are
classified as failed by the framework. This might indicate test framework classified as failed by the framework. This might indicate test framework
error, timeout or other problems. 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`, 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 where TEST_ID is a path-safe conversion from the fully qualified test

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
# #
# Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. # Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
# #
# This code is free software; you can redistribute it and/or modify it # This code is free software; you can redistribute it and/or modify it
@ -30,17 +30,6 @@
ifndef _MAINSUPPORT_GMK ifndef _MAINSUPPORT_GMK
_MAINSUPPORT_GMK := 1 _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 define CleanDocs
@$(PRINTF) "Cleaning docs ..." @$(PRINTF) "Cleaning docs ..."
@$(PRINTF) "\n" $(LOG_DEBUG) @$(PRINTF) "\n" $(LOG_DEBUG)

View File

@ -1,5 +1,5 @@
# #
# Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. # Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
# #
# This code is free software; you can redistribute it and/or modify it # This code is free software; you can redistribute it and/or modify it
@ -300,16 +300,32 @@ endef
# Helper function to determine if a test specification is a special test # 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 define ParseSpecialTestSelection
$(if $(filter special:%, $1), \ $(if $(filter special:%, $1), \
$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 endef
ifeq ($(TEST), ) ifeq ($(TEST), )
$(info No test selection given in 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) $(info See doc/testing.[md|html] for help)
$(error Cannot continue) $(error Cannot continue)
endif endif
@ -663,9 +679,13 @@ define SetupRunSpecialTestBody
$$(JDK_IMAGE_DIR)/bin/java -XX:+ExecuteInternalVMTests \ $$(JDK_IMAGE_DIR)/bin/java -XX:+ExecuteInternalVMTests \
-XX:+ShowMessageBoxOnError -version -XX:+ShowMessageBoxOnError -version
else ifeq ($$($1_TEST_NAME), failure-handler) else ifeq ($$($1_TEST_NAME), failure-handler)
$1_TEST_COMMAND_LINE := \ ifeq ($(BUILD_FAILURE_HANDLER), true)
($(CD) $(TOPDIR)/make/test && $(MAKE) $(MAKE_ARGS) -f \ $1_TEST_COMMAND_LINE := \
BuildFailureHandler.gmk test) ($(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) else ifeq ($$($1_TEST_NAME), make)
$1_TEST_COMMAND_LINE := \ $1_TEST_COMMAND_LINE := \
($(CD) $(TOPDIR)/test/make && $(MAKE) $(MAKE_ARGS) -f \ ($(CD) $(TOPDIR)/test/make && $(MAKE) $(MAKE_ARGS) -f \

View File

@ -1,5 +1,5 @@
# #
# Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. # Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
# #
# This code is free software; you can redistribute it and/or modify it # This code is free software; you can redistribute it and/or modify it
@ -72,6 +72,15 @@ ALL_NAMED_TESTS += $(JTREG_TEST_GROUPS)
# Add Gtest # Add Gtest
ALL_NAMED_TESTS += 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 endif # _FIND_TESTS_GMK

View File

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

View File

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

View File

@ -79,7 +79,6 @@ void Compiler::initialize() {
} }
int Compiler::code_buffer_size() { 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(); return Compilation::desired_max_code_buffer_size() + Compilation::desired_max_constant_size();
} }
@ -90,10 +89,7 @@ BufferBlob* Compiler::init_buffer_blob() {
// setup CodeBuffer. Preallocate a BufferBlob of size // setup CodeBuffer. Preallocate a BufferBlob of size
// NMethodSizeLimit plus some extra space for constants. // NMethodSizeLimit plus some extra space for constants.
int code_buffer_size = Compilation::desired_max_code_buffer_size() + BufferBlob* buffer_blob = BufferBlob::create("C1 temporary CodeBuffer", code_buffer_size());
Compilation::desired_max_constant_size();
BufferBlob* buffer_blob = BufferBlob::create("C1 temporary CodeBuffer", code_buffer_size);
if (buffer_blob != NULL) { if (buffer_blob != NULL) {
CompilerThread::current()->set_buffer_blob(buffer_blob); CompilerThread::current()->set_buffer_blob(buffer_blob);
} }

View File

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

View File

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

View File

@ -50,6 +50,7 @@
#include "oops/oop.inline.hpp" #include "oops/oop.inline.hpp"
#include "oops/symbol.hpp" #include "oops/symbol.hpp"
#include "oops/typeArrayOop.inline.hpp" #include "oops/typeArrayOop.inline.hpp"
#include "prims/jvmtiExport.hpp"
#include "prims/resolvedMethodTable.hpp" #include "prims/resolvedMethodTable.hpp"
#include "runtime/fieldDescriptor.inline.hpp" #include "runtime/fieldDescriptor.inline.hpp"
#include "runtime/frame.inline.hpp" #include "runtime/frame.inline.hpp"
@ -125,7 +126,7 @@ static void compute_offset(int &dest_offset,
if (ik == NULL) { if (ik == NULL) {
ResourceMark rm; ResourceMark rm;
log_error(class)("Mismatch JDK version for field: %s type: %s", name_symbol->as_C_string(), signature_symbol->as_C_string()); 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) { if (!ik->find_local_field(name_symbol, signature_symbol, &fd) || fd.is_static() != is_static) {
@ -138,7 +139,7 @@ static void compute_offset(int &dest_offset,
LogStream ls(lt.error()); LogStream ls(lt.error());
ik->print_on(&ls); ik->print_on(&ls);
#endif //PRODUCT #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(); dest_offset = fd.offset();
} }
@ -151,7 +152,7 @@ static void compute_offset(int& dest_offset, InstanceKlass* ik,
if (name == NULL) { if (name == NULL) {
ResourceMark rm; ResourceMark rm;
log_error(class)("Name %s should be in the SymbolTable since its class is loaded", name_string); 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); compute_offset(dest_offset, ik, name, signature_symbol, is_static);
} }
@ -1196,7 +1197,7 @@ bool java_lang_Class::restore_archived_mirror(Klass *k,
Handle class_loader, Handle module, Handle class_loader, Handle module,
Handle protection_domain, TRAPS) { Handle protection_domain, TRAPS) {
// Postpone restoring archived mirror until java.lang.Class is loaded. Please // 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()) { if (!SystemDictionary::Class_klass_loaded()) {
assert(fixup_mirror_list() != NULL, "fixup_mirror_list not initialized"); assert(fixup_mirror_list() != NULL, "fixup_mirror_list not initialized");
fixup_mirror_list()->push(k); fixup_mirror_list()->push(k);
@ -4250,12 +4251,19 @@ void JavaClasses::compute_hard_coded_offsets() {
// Compute non-hard-coded field offsets of all the classes in this file // Compute non-hard-coded field offsets of all the classes in this file
void JavaClasses::compute_offsets() { void JavaClasses::compute_offsets() {
if (UseSharedSpaces) { 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 // We have already called the compute_offsets() of the
// BASIC_JAVA_CLASSES_DO_PART1 classes (java_lang_String and java_lang_Class) // 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); BASIC_JAVA_CLASSES_DO_PART2(DO_COMPUTE_OFFSETS);
// generated interpreter code wants to know about the offsets we just computed: // generated interpreter code wants to know about the offsets we just computed:
@ -4356,7 +4364,7 @@ int InjectedField::compute_offset() {
tty->print_cr(" name: %s, sig: %s, flags: %08x", fs.name()->as_C_string(), fs.signature()->as_C_string(), fs.access_flags().as_int()); 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 #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; return -1;
} }

View File

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

View File

@ -85,19 +85,20 @@ class ProtectionDomainCacheEntry;
class GCTimer; class GCTimer;
class OopStorage; class OopStorage;
// Certain classes are preloaded, such as java.lang.Object and java.lang.String.
// They are all "well-known", in the sense that no class loader is allowed
// to provide a different definition.
//
// These klasses must all have names defined in vmSymbols.
#define WK_KLASS_ENUM_NAME(kname) kname##_knum #define WK_KLASS_ENUM_NAME(kname) kname##_knum
// Certain classes, such as java.lang.Object and java.lang.String,
// are "well-known", in the sense that no class loader is allowed
// to provide a different definition.
//
// Each well-known class has a short klass name (like object_klass), // Each well-known class has a short klass name (like object_klass),
// and a vmSymbol name (like java_lang_Object). // 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) \ #define WK_KLASSES_DO(do_klass) \
/* well-known classes */ \ /* well-known classes */ \
do_klass(Object_klass, java_lang_Object ) \ do_klass(Object_klass, java_lang_Object ) \
@ -127,7 +128,7 @@ class OopStorage;
do_klass(IllegalMonitorStateException_klass, java_lang_IllegalMonitorStateException ) \ do_klass(IllegalMonitorStateException_klass, java_lang_IllegalMonitorStateException ) \
do_klass(Reference_klass, java_lang_ref_Reference ) \ 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(SoftReference_klass, java_lang_ref_SoftReference ) \
do_klass(WeakReference_klass, java_lang_ref_WeakReference ) \ do_klass(WeakReference_klass, java_lang_ref_WeakReference ) \
do_klass(FinalReference_klass, java_lang_ref_FinalReference ) \ do_klass(FinalReference_klass, java_lang_ref_FinalReference ) \
@ -200,7 +201,7 @@ class OopStorage;
/* support for stack dump lock analysis */ \ /* support for stack dump lock analysis */ \
do_klass(java_util_concurrent_locks_AbstractOwnableSynchronizer_klass, java_util_concurrent_locks_AbstractOwnableSynchronizer) \ 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(Boolean_klass, java_lang_Boolean ) \
do_klass(Character_klass, java_lang_Character ) \ do_klass(Character_klass, java_lang_Character ) \
do_klass(Float_klass, java_lang_Float ) \ do_klass(Float_klass, java_lang_Float ) \
@ -391,7 +392,8 @@ public:
// Initialization // Initialization
static void initialize(TRAPS); 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) { static InstanceKlass* check_klass(InstanceKlass* k) {
assert(k != NULL, "klass not loaded"); assert(k != NULL, "klass not loaded");
return k; return k;
@ -435,6 +437,12 @@ public:
return check_klass(_box_klasses[t]); return check_klass(_box_klasses[t]);
} }
static BasicType box_klass_type(Klass* k); // inverse of box_klass 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: protected:
// Returns the class loader data to be used when looking up/updating the // Returns the class loader data to be used when looking up/updating the
@ -643,6 +651,8 @@ protected:
Handle class_loader, Handle class_loader,
Handle protection_domain, Handle protection_domain,
TRAPS); TRAPS);
static InstanceKlass* load_shared_boot_class(Symbol* class_name,
TRAPS);
static InstanceKlass* load_instance_class(Symbol* class_name, Handle class_loader, TRAPS); static InstanceKlass* load_instance_class(Symbol* class_name, Handle class_loader, TRAPS);
static Handle compute_loader_lock_object(Handle class_loader, TRAPS); static Handle compute_loader_lock_object(Handle class_loader, TRAPS);
static void check_loader_lock_contention(Handle loader_lock, TRAPS); static void check_loader_lock_contention(Handle loader_lock, TRAPS);
@ -650,9 +660,6 @@ protected:
static bool is_parallelDefine(Handle class_loader); static bool is_parallelDefine(Handle class_loader);
public: 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_system_class_loader(oop class_loader);
static bool is_platform_class_loader(oop class_loader); static bool is_platform_class_loader(oop class_loader);
static void clear_invoke_method_table(); static void clear_invoke_method_table();
@ -695,8 +702,8 @@ protected:
ClassLoaderData* loader_data, ClassLoaderData* loader_data,
TRAPS); TRAPS);
// Resolve preloaded classes so they can be used like SystemDictionary::String_klass() // Resolve well-known classes so they can be used like SystemDictionary::String_klass()
static void resolve_preloaded_classes(TRAPS); static void resolve_well_known_classes(TRAPS);
// Class loader constraints // Class loader constraints
static void check_constraints(unsigned int hash, static void check_constraints(unsigned int hash,
@ -707,7 +714,6 @@ protected:
InstanceKlass* k, Handle loader, InstanceKlass* k, Handle loader,
TRAPS); TRAPS);
// Variables holding commonly used klasses (preloaded)
static InstanceKlass* _well_known_klasses[]; static InstanceKlass* _well_known_klasses[];
// table of box klasses (int_klass, etc.) // table of box klasses (int_klass, etc.)

View File

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

View File

@ -894,14 +894,19 @@ void CompileBroker::possibly_add_compiler_threads() {
EXCEPTION_MARK; EXCEPTION_MARK;
julong available_memory = os::available_memory(); 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. // Only do attempt to start additional threads if the lock is free.
if (!CompileThread_lock->try_lock()) return; if (!CompileThread_lock->try_lock()) return;
if (_c2_compile_queue != NULL) { if (_c2_compile_queue != NULL) {
int old_c2_count = _compilers[1]->num_compiler_threads(); 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, _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++) { 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); JavaThread *ct = make_thread(compiler2_object(i), _c2_compile_queue, _compilers[1], true, CHECK);
@ -910,17 +915,18 @@ void CompileBroker::possibly_add_compiler_threads() {
if (TraceCompilerThreads) { if (TraceCompilerThreads) {
ResourceMark rm; ResourceMark rm;
MutexLocker mu(Threads_lock); MutexLocker mu(Threads_lock);
tty->print_cr("Added compiler thread %s (available memory: %dMB)", tty->print_cr("Added compiler thread %s (available memory: %dMB, available non-profiled code cache: %dMB)",
ct->get_thread_name(), (int)(available_memory/M)); ct->get_thread_name(), (int)(available_memory/M), (int)(available_cc_np/M));
} }
} }
} }
if (_c1_compile_queue != NULL) { if (_c1_compile_queue != NULL) {
int old_c1_count = _compilers[0]->num_compiler_threads(); 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, _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++) { 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); JavaThread *ct = make_thread(compiler1_object(i), _c1_compile_queue, _compilers[0], true, CHECK);
@ -929,8 +935,8 @@ void CompileBroker::possibly_add_compiler_threads() {
if (TraceCompilerThreads) { if (TraceCompilerThreads) {
ResourceMark rm; ResourceMark rm;
MutexLocker mu(Threads_lock); MutexLocker mu(Threads_lock);
tty->print_cr("Added compiler thread %s (available memory: %dMB)", tty->print_cr("Added compiler thread %s (available memory: %dMB, available profiled code cache: %dMB)",
ct->get_thread_name(), (int)(available_memory/M)); ct->get_thread_name(), (int)(available_memory/M), (int)(available_cc_p/M));
} }
} }
} }
@ -1501,8 +1507,9 @@ bool CompileBroker::wait_for_jvmci_completion(JVMCICompiler* jvmci, CompileTask*
bool progress; bool progress;
if (jvmci_compiler_thread != NULL) { if (jvmci_compiler_thread != NULL) {
// If the JVMCI compiler thread is not blocked, we deem it to be making progress. // 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; progress = jvmci_compiler_thread->thread_state() != _thread_blocked &&
!jvmci_compiler_thread->is_external_suspend();
} else { } else {
// Still waiting on JVMCI compiler queue. This thread may be holding a lock // 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 // that all JVMCI compiler threads are blocked on. We use the counter for

View File

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

View File

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

View File

@ -60,6 +60,9 @@ size_t G1CollectedHeap::desired_plab_sz(InCSetState dest) {
// Return the region with the given index. It assumes the index is valid. // 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); } 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 { inline HeapRegion* G1CollectedHeap::next_region_in_humongous(HeapRegion* hr) const {
return _hrm.next_region_in_humongous(hr); return _hrm.next_region_in_humongous(hr);
} }
@ -84,6 +87,16 @@ inline HeapRegion* G1CollectedHeap::heap_region_containing(const T addr) const {
return _hrm.addr_to_region((HeapWord*) addr); 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) { inline void G1CollectedHeap::old_set_add(HeapRegion* hr) {
_old_set.add(hr); _old_set.add(hr);
} }

View File

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

View File

@ -38,7 +38,7 @@ G1ParCopyHelper::G1ParCopyHelper(G1CollectedHeap* g1h, G1ParScanThreadState* pa
{ } { }
G1ScanClosureBase::G1ScanClosureBase(G1CollectedHeap* g1h, G1ParScanThreadState* par_scan_state) : 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) { void G1CLDScanClosure::do_cld(ClassLoaderData* cld) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -47,6 +47,16 @@ inline HeapRegion* HeapRegionManager::at(uint index) const {
return hr; 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 { inline HeapRegion* HeapRegionManager::next_region_in_humongous(HeapRegion* hr) const {
uint index = hr->hrm_index(); uint index = hr->hrm_index();
assert(is_available(index), "pre-condition"); assert(is_available(index), "pre-condition");

View File

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

View File

@ -76,7 +76,6 @@ class OtherRegionsTable {
G1CollectedHeap* _g1h; G1CollectedHeap* _g1h;
Mutex* _m; Mutex* _m;
HeapRegion* _hr;
// These are protected by "_m". // These are protected by "_m".
CHeapBitMap _coarse_map; CHeapBitMap _coarse_map;
@ -124,11 +123,8 @@ class OtherRegionsTable {
bool contains_reference_locked(OopOrNarrowOopStar from) const; bool contains_reference_locked(OopOrNarrowOopStar from) const;
public: public:
// Clear the from_card_cache entries for this region. // Create a new remembered set. The given mutex is used to ensure consistency.
void clear_fcc(); OtherRegionsTable(Mutex* m);
// Create a new remembered set for the given heap region. The given mutex should
// be used to ensure consistency.
OtherRegionsTable(HeapRegion* hr, Mutex* m);
// Returns the card index of the given within_region pointer relative to the bottom // Returns the card index of the given within_region pointer relative to the bottom
// of the given heap region. // of the given heap region.
@ -182,13 +178,21 @@ private:
OtherRegionsTable _other_regions; OtherRegionsTable _other_regions;
HeapRegion* _hr;
void clear_fcc();
public: public:
HeapRegionRemSet(G1BlockOffsetTable* bot, HeapRegion* hr); HeapRegionRemSet(G1BlockOffsetTable* bot, HeapRegion* hr);
static void setup_remset_size(); static void setup_remset_size();
bool cardset_is_empty() const {
return _other_regions.is_empty();
}
bool is_empty() const { 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 { bool occupancy_less_or_equal_than(size_t occ) const {
@ -239,18 +243,18 @@ public:
if (_state == Untracked) { if (_state == Untracked) {
return; return;
} }
_other_regions.clear_fcc(); clear_fcc();
_state = Untracked; _state = Untracked;
} }
void set_state_updating() { 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()); 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; _state = Updating;
} }
void set_state_complete() { void set_state_complete() {
_other_regions.clear_fcc(); clear_fcc();
_state = Complete; _state = Complete;
} }
@ -265,6 +269,15 @@ public:
if (state == Untracked) { if (state == Untracked) {
return; 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); _other_regions.add_reference(from, tid);
} }
@ -353,7 +366,7 @@ public:
}; };
class HeapRegionRemSetIterator : public StackObj { class HeapRegionRemSetIterator : public StackObj {
private: private:
// The region RSet over which we are iterating. // The region RSet over which we are iterating.
HeapRegionRemSet* _hrrs; HeapRegionRemSet* _hrrs;
@ -401,7 +414,7 @@ class HeapRegionRemSetIterator : public StackObj {
// The Sparse remembered set iterator. // The Sparse remembered set iterator.
SparsePRTIter _sparse_iter; SparsePRTIter _sparse_iter;
public: public:
HeapRegionRemSetIterator(HeapRegionRemSet* hrrs); HeapRegionRemSetIterator(HeapRegionRemSet* hrrs);
// If there remains one or more cards to be yielded, returns true and // If there remains one or more cards to be yielded, returns true and

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -132,7 +132,7 @@ jobject JfrEventClasses::get_all_event_classes(TRAPS) {
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD)); DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
initialize(THREAD); initialize(THREAD);
assert(empty_java_util_arraylist != NULL, "should have been setup already!"); 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; 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); Symbol* const event_klass_name = SymbolTable::lookup_only(jdk_jfr_event_name, sizeof jdk_jfr_event_name - 1, unused_hash);

View File

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

View File

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

View File

@ -914,6 +914,19 @@ void FileMapInfo::map_heap_regions_impl() {
return; 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(); 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:", log_info(cds)("CDS archive was created with max heap size = " SIZE_FORMAT "M, and the following configuration:",
@ -1224,6 +1237,15 @@ bool FileMapInfo::_validating_shared_path_table = false;
bool FileMapInfo::initialize() { bool FileMapInfo::initialize() {
assert(UseSharedSpaces, "UseSharedSpaces expected."); 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()) { if (!open_for_read()) {
return false; return false;
} }

View File

@ -417,6 +417,11 @@ void HeapShared::initialize_from_archived_subgraph(Klass* k) {
Klass* resolved_k = SystemDictionary::resolve_or_null( Klass* resolved_k = SystemDictionary::resolve_or_null(
(obj_k)->name(), THREAD); (obj_k)->name(), THREAD);
if (resolved_k != obj_k) { 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; return;
} }
if ((obj_k)->is_instance_klass()) { if ((obj_k)->is_instance_klass()) {

View File

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

View File

@ -26,6 +26,7 @@
#define SHARE_VM_OPTO_C2COMPILER_HPP #define SHARE_VM_OPTO_C2COMPILER_HPP
#include "compiler/abstractCompiler.hpp" #include "compiler/abstractCompiler.hpp"
#include "opto/output.hpp"
class C2Compiler : public AbstractCompiler { class C2Compiler : public AbstractCompiler {
private: private:
@ -66,7 +67,7 @@ public:
virtual bool is_intrinsic_supported(const methodHandle& method, bool is_virtual); virtual bool is_intrinsic_supported(const methodHandle& method, bool is_virtual);
// Initial size of the code buffer (may be increased at runtime) // 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 #endif // SHARE_VM_OPTO_C2COMPILER_HPP

View File

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

View File

@ -2116,8 +2116,17 @@ void GraphKit::uncommon_trap(int trap_request,
// We use this to determine if an object is so "fresh" that // We use this to determine if an object is so "fresh" that
// it does not require card marks. // it does not require card marks.
Node* GraphKit::just_allocated_object(Node* current_control) { Node* GraphKit::just_allocated_object(Node* current_control) {
if (C->recent_alloc_ctl() == current_control) Node* ctrl = current_control;
return C->recent_alloc_obj(); // 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; return NULL;
} }

View File

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

View File

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

View File

@ -46,6 +46,13 @@
#include "utilities/events.hpp" #include "utilities/events.hpp"
#include "utilities/globalDefinitions.hpp" #include "utilities/globalDefinitions.hpp"
#ifdef COMPILER1
#include "c1/c1_Compiler.hpp"
#endif
#ifdef COMPILER2
#include "opto/c2compiler.hpp"
#endif
CompilationPolicy* CompilationPolicy::_policy; CompilationPolicy* CompilationPolicy::_policy;
elapsedTimer CompilationPolicy::_accumulated_time; elapsedTimer CompilationPolicy::_accumulated_time;
bool CompilationPolicy::_in_vm_startup; bool CompilationPolicy::_in_vm_startup;
@ -222,6 +229,19 @@ void NonTieredCompPolicy::initialize() {
// max(log2(8)-1,1) = 2 compiler threads on an 8-way machine. // max(log2(8)-1,1) = 2 compiler threads on an 8-way machine.
// May help big-app startup time. // May help big-app startup time.
_compiler_count = MAX2(log2_intptr(os::active_processor_count())-1,1); _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); FLAG_SET_ERGO(intx, CICompilerCount, _compiler_count);
} else { } else {
_compiler_count = CICompilerCount; _compiler_count = CICompilerCount;

View File

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

View File

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

View File

@ -4276,9 +4276,6 @@ bool Threads::destroy_vm() {
before_exit(thread); before_exit(thread);
thread->exit(true); 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. // Stop VM thread.
{ {

View File

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

View File

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

View File

@ -38,6 +38,9 @@
#ifdef TIERED #ifdef TIERED
#include "c1/c1_Compiler.hpp"
#include "opto/c2compiler.hpp"
template<CompLevel level> template<CompLevel level>
bool TieredThresholdPolicy::call_predicate_helper(int i, int b, double scale, Method* method) { bool TieredThresholdPolicy::call_predicate_helper(int i, int b, double scale, Method* method) {
double threshold_scaling; double threshold_scaling;
@ -215,6 +218,7 @@ void TieredThresholdPolicy::print_event(EventType type, const methodHandle& mh,
void TieredThresholdPolicy::initialize() { void TieredThresholdPolicy::initialize() {
int count = CICompilerCount; int count = CICompilerCount;
bool c1_only = TieredStopAtLevel < CompLevel_full_optimization;
#ifdef _LP64 #ifdef _LP64
// Turn on ergonomic compiler count selection // Turn on ergonomic compiler count selection
if (FLAG_IS_DEFAULT(CICompilerCountPerCPU) && FLAG_IS_DEFAULT(CICompilerCount)) { if (FLAG_IS_DEFAULT(CICompilerCountPerCPU) && FLAG_IS_DEFAULT(CICompilerCount)) {
@ -225,6 +229,15 @@ void TieredThresholdPolicy::initialize() {
int log_cpu = log2_intptr(os::active_processor_count()); int log_cpu = log2_intptr(os::active_processor_count());
int loglog_cpu = log2_intptr(MAX2(log_cpu, 1)); int loglog_cpu = log2_intptr(MAX2(log_cpu, 1));
count = MAX2(log_cpu * loglog_cpu * 3 / 2, 2); 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); FLAG_SET_ERGO(intx, CICompilerCount, count);
} }
#else #else
@ -241,7 +254,7 @@ void TieredThresholdPolicy::initialize() {
} }
#endif #endif
if (TieredStopAtLevel < CompLevel_full_optimization) { if (c1_only) {
// No C2 compiler thread required // No C2 compiler thread required
set_c1_count(count); set_c1_count(count);
} else { } else {

View File

@ -25,6 +25,10 @@
#ifndef SHARE_UTILITIES_CONCURRENT_HASH_TABLE_HPP #ifndef SHARE_UTILITIES_CONCURRENT_HASH_TABLE_HPP
#define 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 // 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 // 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. // type kept inside each Node and CONFIG contains hash and allocation methods.
@ -247,6 +251,7 @@ class ConcurrentHashTable : public CHeapObj<F> {
protected: protected:
Thread* _thread; Thread* _thread;
ConcurrentHashTable<VALUE, CONFIG, F>* _cht; ConcurrentHashTable<VALUE, CONFIG, F>* _cht;
GlobalCounter::CSContext _cs_context;
public: public:
ScopedCS(Thread* thread, ConcurrentHashTable<VALUE, CONFIG, F>* cht); ScopedCS(Thread* thread, ConcurrentHashTable<VALUE, CONFIG, F>* cht);
~ScopedCS(); ~ScopedCS();

View File

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

View File

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

View File

@ -33,8 +33,9 @@ class Thread;
// The GlobalCounter provides a synchronization mechanism between threads for // The GlobalCounter provides a synchronization mechanism between threads for
// safe memory reclamation and other ABA problems. All readers must call // safe memory reclamation and other ABA problems. All readers must call
// critical_section_begin before reading the volatile data and // critical_section_begin before reading the volatile data and
// critical_section_end afterwards. The write side must call write_synchronize // critical_section_end afterwards. Such read-side critical sections may
// before reclaming the memory. The read-path only does an uncontented store // 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 // 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 // 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 // all readers and wait until they have left the generation. (a system memory
@ -62,20 +63,26 @@ class GlobalCounter : public AllStatic {
class CounterThreadCheck; class CounterThreadCheck;
public: public:
// Must be called before accessing the data. Only threads accessible lock-free // The type of the critical section context passed from
// can used this. Those included now are all Threads on SMR ThreadsList and // critical_section_begin() to critical_section_end().
// the VMThread. Nesting is not yet supported. typedef uintx CSContext;
static void critical_section_begin(Thread *thread);
// Must be called after finished accessing the data. // Must be called before accessing the data. The result must be passed
// Do not provide fence, allows load/stores moving into the critical section. // to the associated call to critical_section_end(). Acts as a full
static void critical_section_end(Thread *thread); // 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 // 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(); static void write_synchronize();
// A scoped object for a reads-side critical-section. // A scoped object for a read-side critical-section.
class CriticalSection; class CriticalSection;
}; };

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -92,6 +92,11 @@ import sun.util.calendar.CalendarDate;
* Japan introduced the Gregorian calendar starting with Meiji 6. * Japan introduced the Gregorian calendar starting with Meiji 6.
* Only Meiji and later eras are supported; * Only Meiji and later eras are supported;
* dates before Meiji 6, January 1 are not 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 * @implSpec
* This class is immutable and thread-safe. * This class is immutable and thread-safe.
@ -195,9 +200,13 @@ public final class JapaneseEra
/** /**
* Obtains an instance of {@code JapaneseEra} from an {@code int} value. * Obtains an instance of {@code JapaneseEra} from an {@code int} value.
* <p> * <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}), * Later era is numbered 2 ({@link #HEISEI}). Earlier eras are numbered 0 ({@link #TAISHO}),
* -1 ({@link #MEIJI}), only Meiji and later eras are supported. * -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 * @param japaneseEra the era to represent
* @return the {@code JapaneseEra} singleton, not null * @return the {@code JapaneseEra} singleton, not null
@ -216,6 +225,8 @@ public final class JapaneseEra
* <p> * <p>
* The string must match exactly the name of the era. * The string must match exactly the name of the era.
* (Extraneous whitespace characters are not permitted.) * (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 * @param japaneseEra the japaneseEra name; non-null
* @return the {@code JapaneseEra} singleton, never null * @return the {@code JapaneseEra} singleton, never null
@ -232,7 +243,9 @@ public final class JapaneseEra
} }
/** /**
* Returns an array of JapaneseEras. * Returns an array of JapaneseEras. The array may contain eras defined
* by the Japanese government beyond the known era singletons.
*
* <p> * <p>
* This method may be used to iterate over the JapaneseEras as follows: * This method may be used to iterate over the JapaneseEras as follows:
* <pre> * <pre>

View File

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

View File

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

View File

@ -0,0 +1,94 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.event;
/**
* Base class for events, to be subclassed in order to define events and their
* fields.
*/
public abstract class Event {
/**
* Sole constructor, for invocation by subclass constructors, typically
* implicit.
*/
protected Event() {
}
/**
* Starts the timing of this event.
*/
public void begin() {
}
/**
* Ends the timing of this event.
*
* The {@code end} method must be invoked after the {@code begin} method.
*/
public void end() {
}
/**
* Writes the field values, time stamp, and event duration.
* <p>
* If the event starts with an invocation of the {@code begin} method, but does
* not end with an explicit invocation of the {@code end} method, then the event
* ends when the {@code commit} method is invoked.
*/
public void commit() {
}
/**
* Returns {@code true} if the event is enabled, {@code false} otherwise
*
* @return {@code true} if event is enabled, {@code false} otherwise
*/
public boolean isEnabled() {
return false;
}
/**
* Returns {@code true} if the event is enabled and if the duration is within
* the threshold for the event, {@code false} otherwise.
*
* @return {@code true} if the event can be written, {@code false} otherwise
*/
public boolean shouldCommit() {
return false;
}
/**
* Sets a field value.
*
* @param index the index of the field to set
* @param value value to set, can be {@code null}
* @throws UnsupportedOperationException if functionality is not supported
* @throws IndexOutOfBoundsException if {@code index} is less than {@code 0} or
* greater than or equal to the number of fields specified for the event
*/
public void set(int index, Object value) {
}
}

View File

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

View File

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

View File

@ -49,7 +49,10 @@ extern int errno;
#define ERR_ARGS 3 #define ERR_ARGS 3
void error (int fd, int err) { 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); exit (1);
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * 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 * and an <a href="http://www.iso.org">ISO (International Organization
* for Standardization)</a> standard. * for Standardization)</a> standard.
* </li> * </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 * 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 * to express the structure of a content model, Schematron is designed to
* enforce individual rules that are difficult or impossible to express * enforce individual rules that are difficult or impossible to express
* with other schema languages. Schematron is intended to supplement a * with other schema languages. Schematron is intended to supplement a
* schema written in structural schema language such as the aforementioned. * 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> * </li>
* </ul> * </ul>
* <p> * <p>

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -127,7 +127,9 @@ public final class ECDHKeyAgreement extends KeyAgreementSpi {
try { try {
return deriveKey(s, publicValue, encodedParams); byte[] result = deriveKey(s, publicValue, encodedParams);
publicValue = null;
return result;
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throw new ProviderException("Could not derive key", e); throw new ProviderException("Could not derive key", e);

View File

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

View File

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

View File

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

View File

@ -93,11 +93,11 @@ final class EventHandlerCreator {
return EventHandler.class.getName() + id + SUFFIX; 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)); 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<>(); List<FieldInfo> fieldInfos = new ArrayList<>();
for (ValueDescriptor v : type.getFields()) { for (ValueDescriptor v : type.getFields()) {
// Only value descriptors that are not fields on the event class. // Only value descriptors that are not fields on the event class.

View File

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

View File

@ -106,11 +106,11 @@ public final class JVM {
public native void endRecording(); 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. * @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} * Return a count of the number of unloaded classes deriving from {@link Event}

View File

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

View File

@ -33,6 +33,7 @@ import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -56,6 +57,7 @@ public final class MetadataRepository {
private final List<EventControl> nativeControls = new ArrayList<EventControl>(100); private final List<EventControl> nativeControls = new ArrayList<EventControl>(100);
private final TypeLibrary typeLibrary = TypeLibrary.getInstance(); private final TypeLibrary typeLibrary = TypeLibrary.getInstance();
private final SettingsManager settingsManager = new SettingsManager(); private final SettingsManager settingsManager = new SettingsManager();
private final Map<String, Class<? extends Event>> mirrors = new HashMap<>();
private boolean staleMetadata = true; private boolean staleMetadata = true;
private boolean unregistered; private boolean unregistered;
private long lastUnloaded = -1; private long lastUnloaded = -1;
@ -105,7 +107,7 @@ public final class MetadataRepository {
return eventTypes; 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); EventHandler h = getHandler(eventClass);
if (h != null && h.isRegistered()) { if (h != null && h.isRegistered()) {
return h.getEventType(); return h.getEventType();
@ -121,15 +123,20 @@ public final class MetadataRepository {
} }
// never registered, ignore call // 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()); 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(); Utils.checkRegisterPermission();
EventHandler handler = getHandler(eventClass); EventHandler handler = getHandler(eventClass);
if (handler == null) { 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); handler.setRegistered(true);
typeLibrary.addType(handler.getPlatformEventType()); typeLibrary.addType(handler.getPlatformEventType());
@ -143,16 +150,32 @@ public final class MetadataRepository {
return handler.getEventType(); 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); Utils.ensureValidEventSubclass(eventClass);
SecuritySupport.makeVisibleToJFR(eventClass); SecuritySupport.makeVisibleToJFR(eventClass);
Utils.ensureInitialized(eventClass); Utils.ensureInitialized(eventClass);
return Utils.getHandler(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); 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); EventType eventType = PrivateAccess.getInstance().newEventType(pEventType);
EventControl ec = new EventControl(pEventType, eventClass); EventControl ec = new EventControl(pEventType, eventClass);
Class<? extends EventHandler> handlerClass = null; Class<? extends EventHandler> handlerClass = null;
@ -198,9 +221,9 @@ public final class MetadataRepository {
} }
private static List<EventHandler> getEventHandlers() { 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()); 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); EventHandler eh = Utils.getHandler(clazz);
if (eh != null) { if (eh != null) {
eventHandlers.add(eh); eventHandlers.add(eh);
@ -252,9 +275,9 @@ public final class MetadataRepository {
long unloaded = jvm.getUnloadedEventClassCount(); long unloaded = jvm.getUnloadedEventClassCount();
if (this.lastUnloaded != unloaded) { if (this.lastUnloaded != unloaded) {
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()); 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)); knownIds.add(Type.getTypeId(ec));
} }
for (Type type : typeLibrary.getTypes()) { for (Type type : typeLibrary.getTypes()) {
@ -270,8 +293,18 @@ public final class MetadataRepository {
} }
} }
synchronized public void setUnregistered() { synchronized void setUnregistered() {
unregistered = true; unregistered = true;
} }
public synchronized void registerMirror(Class<? extends Event> eventClass) {
MirrorEvent me = eventClass.getAnnotation(MirrorEvent.class);
if (me != null) {
String fullName = me.module() + ":" + me.className();
mirrors.put(fullName, eventClass);
return;
}
throw new InternalError("Mirror class must have annotation " + MirrorEvent.class.getName());
}
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -267,7 +267,7 @@ public final class Utils {
return (long) (nanos * JVM.getJVM().getTimeConversionFactor()); 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); Utils.ensureValidEventSubclass(eventClass);
try { try {
Field f = eventClass.getDeclaredField(EventInstrumentation.FIELD_EVENT_HANDLER); Field f = eventClass.getDeclaredField(EventInstrumentation.FIELD_EVENT_HANDLER);
@ -278,7 +278,7 @@ public final class Utils {
} }
} }
static synchronized void setHandler(Class<? extends Event> eventClass, EventHandler handler) { static synchronized void setHandler(Class<? extends jdk.internal.event.Event> eventClass, EventHandler handler) {
Utils.ensureValidEventSubclass(eventClass); Utils.ensureValidEventSubclass(eventClass);
try { try {
Field field = eventClass.getDeclaredField(EventInstrumentation.FIELD_EVENT_HANDLER); Field field = eventClass.getDeclaredField(EventInstrumentation.FIELD_EVENT_HANDLER);
@ -322,7 +322,7 @@ public final class Utils {
static List<Field> getVisibleEventFields(Class<?> clazz) { static List<Field> getVisibleEventFields(Class<?> clazz) {
Utils.ensureValidEventSubclass(clazz); Utils.ensureValidEventSubclass(clazz);
List<Field> fields = new ArrayList<>(); 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()) { for (Field field : c.getDeclaredFields()) {
// skip private field in base classes // skip private field in base classes
if (c == clazz || !Modifier.isPrivate(field.getModifiers())) { if (c == clazz || !Modifier.isPrivate(field.getModifiers())) {
@ -334,10 +334,10 @@ public final class Utils {
} }
public static void ensureValidEventSubclass(Class<?> eventClass) { 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"); 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()); throw new IllegalArgumentException("Must be a subclass to " + Event.class.getName());
} }
} }
@ -366,7 +366,7 @@ public final class Utils {
} }
} }
public static void ensureInitialized(Class<? extends Event> eventClass) { public static void ensureInitialized(Class<? extends jdk.internal.event.Event> eventClass) {
SecuritySupport.ensureClassIsInitialized(eventClass); SecuritySupport.ensureClassIsInitialized(eventClass);
} }
@ -499,6 +499,50 @@ public final class Utils {
return eventName; 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) { public static String makeFilename(Recording recording) {
String pid = JVM.getJVM().getPid(); String pid = JVM.getJVM().getPid();
String date = Repository.REPO_DATE_FORMAT.format(LocalDateTime.now()); String date = Repository.REPO_DATE_FORMAT.format(LocalDateTime.now());

View File

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

View File

@ -1,5 +1,5 @@
# #
# Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. # Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
# #
# This code is free software; you can redistribute it and/or modify it # 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 # 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 # Macro to run a test target in a subdir
define SUBDIR_TEST # subdirectory target define SUBDIR_TEST # subdirectory target
if [ -d $1 ] ; then \ if [ -d $1 ] ; then \
if [ -r $1/Makefile ] ; then \ if [ -r $1/Makefile ] ; then \
$(MAKE) --no-print-directory -k -C $1 $2 ; \ $(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 \ else \
echo "ERROR: File does not exist: $1/Makefile"; \ echo "ERROR: File does not exist: $1/Makefile"; \
exit 1; \ exit 1; \

View File

@ -0,0 +1,238 @@
/*
* Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "precompiled.hpp"
#include "gc/shared/gcTimer.hpp"
#include "utilities/ticks.hpp"
#include "unittest.hpp"
class GCTimerTest {
public:
static void register_gc_start(GCTimer* const timer, jlong ticks) {
timer->register_gc_start(Ticks(ticks));
}
static void register_gc_end(GCTimer* const timer, jlong ticks) {
timer->register_gc_end(Ticks(ticks));
}
};
TEST(GCTimer, start) {
GCTimer gc_timer;
GCTimerTest::register_gc_start(&gc_timer, 1);
EXPECT_EQ(1, gc_timer.gc_start().value());
}
TEST(GCTimer, end) {
GCTimer gc_timer;
GCTimerTest::register_gc_start(&gc_timer, 1);
GCTimerTest::register_gc_end(&gc_timer, 2);
EXPECT_EQ(2, gc_timer.gc_end().value());
}
class TimePartitionPhasesIteratorTest {
public:
static void validate_gc_phase(GCPhase* phase, int level, const char* name, const jlong& start, const jlong& end) {
EXPECT_EQ(level, phase->level());
EXPECT_STREQ(name, phase->name());
EXPECT_EQ(start, phase->start().value());
EXPECT_EQ(end, phase->end().value());
}
static void validate_pauses(const TimePartitions& time_partitions, const Tickspan& expected_sum_of_pauses, const Tickspan& expected_longest_pause) {
EXPECT_EQ(expected_sum_of_pauses, time_partitions.sum_of_pauses());
EXPECT_EQ(expected_longest_pause, time_partitions.longest_pause());
}
static void validate_pauses(const TimePartitions& time_partitions, const Tickspan& expected_pause) {
TimePartitionPhasesIteratorTest::validate_pauses(time_partitions, expected_pause, expected_pause);
}
static void validate_pauses(const TimePartitions& time_partitions, jlong end, jlong start) {
TimePartitionPhasesIteratorTest::validate_pauses(time_partitions, Ticks(end) - Ticks(start));
}
static void validate_pauses(const TimePartitions& time_partitions, jlong all_end, jlong all_start, jlong longest_end, jlong longest_start) {
TimePartitionPhasesIteratorTest::validate_pauses(time_partitions, Ticks(all_end) - Ticks(all_start), Ticks(longest_end) - Ticks(longest_start));
}
static void report_gc_phase_start(TimePartitions* const partitions, const char* name, jlong ticks, GCPhase::PhaseType type=GCPhase::PausePhaseType) {
partitions->report_gc_phase_start(name, Ticks(ticks), type);
}
static void report_gc_phase_end(TimePartitions* const partitions, jlong ticks, GCPhase::PhaseType type=GCPhase::PausePhaseType) {
partitions->report_gc_phase_end(Ticks(ticks), type);
}
};
TEST(TimePartitionPhasesIterator, one_pause) {
TimePartitions time_partitions;
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "PausePhase", 2);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 8);
TimePartitionPhasesIterator iter(&time_partitions);
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 0, "PausePhase", 2, 8));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_pauses(time_partitions, 8, 2));
EXPECT_FALSE(iter.has_next()) << "Too many elements";
}
TEST(TimePartitionPhasesIterator, two_pauses) {
TimePartitions time_partitions;
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "PausePhase1", 2);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 3);
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "PausePhase2", 4);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 6);
TimePartitionPhasesIterator iter(&time_partitions);
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 0, "PausePhase1", 2, 3));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 0, "PausePhase2", 4, 6));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_pauses(time_partitions, 3, 0, 2, 0));
EXPECT_FALSE(iter.has_next()) << "Too many elements";
}
TEST(TimePartitionPhasesIterator, one_sub_pause_phase) {
TimePartitions time_partitions;
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "PausePhase", 2);
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "SubPhase", 3);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 4);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 5);
TimePartitionPhasesIterator iter(&time_partitions);
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 0, "PausePhase", 2, 5));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 1, "SubPhase", 3, 4));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_pauses(time_partitions, 3, 0));
EXPECT_FALSE(iter.has_next()) << "Too many elements";
}
TEST(TimePartitionPhasesIterator, max_nested_pause_phases) {
TimePartitions time_partitions;
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "PausePhase", 2);
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "SubPhase1", 3);
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "SubPhase2", 4);
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "SubPhase3", 5);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 6);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 7);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 8);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 9);
TimePartitionPhasesIterator iter(&time_partitions);
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 0, "PausePhase", 2, 9));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 1, "SubPhase1", 3, 8));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 2, "SubPhase2", 4, 7));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 3, "SubPhase3", 5, 6));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_pauses(time_partitions, 7, 0));
EXPECT_FALSE(iter.has_next()) << "Too many elements";
}
TEST(TimePartitionPhasesIterator, many_sub_pause_phases) {
TimePartitions time_partitions;
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "PausePhase", 2);
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "SubPhase1", 3);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 4);
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "SubPhase2", 5);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 6);
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "SubPhase3", 7);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 8);
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "SubPhase4", 9);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 10);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 11);
TimePartitionPhasesIterator iter(&time_partitions);
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 0, "PausePhase", 2, 11));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 1, "SubPhase1", 3, 4));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 1, "SubPhase2", 5, 6));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 1, "SubPhase3", 7, 8));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 1, "SubPhase4", 9, 10));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_pauses(time_partitions, 9, 0));
EXPECT_FALSE(iter.has_next()) << "Too many elements";
}
TEST(TimePartitionPhasesIterator, many_sub_pause_phases2) {
TimePartitions time_partitions;
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "PausePhase", 2);
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "SubPhase1", 3);
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "SubPhase11", 4);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 5);
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "SubPhase12", 6);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 7);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 8);
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "SubPhase2", 9);
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "SubPhase21", 10);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 11);
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "SubPhase22", 12);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 13);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 14);
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "SubPhase3", 15);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 16);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 17);
TimePartitionPhasesIterator iter(&time_partitions);
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 0, "PausePhase", 2, 17));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 1, "SubPhase1", 3, 8));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 2, "SubPhase11", 4, 5));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 2, "SubPhase12", 6, 7));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 1, "SubPhase2", 9, 14));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 2, "SubPhase21", 10, 11));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 2, "SubPhase22", 12, 13));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 1, "SubPhase3", 15, 16));
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_pauses(time_partitions, 15, 0));
EXPECT_FALSE(iter.has_next()) << "Too many elements";
}
TEST(TimePartitionPhasesIterator, one_concurrent) {
TimePartitions time_partitions;
TimePartitionPhasesIteratorTest::report_gc_phase_start(&time_partitions, "ConcurrentPhase", 2, GCPhase::ConcurrentPhaseType);
TimePartitionPhasesIteratorTest::report_gc_phase_end(&time_partitions, 8, GCPhase::ConcurrentPhaseType);
TimePartitionPhasesIterator iter(&time_partitions);
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_gc_phase(iter.next(), 0, "ConcurrentPhase", 2, 8));
// ConcurrentPhaseType should not affect to both 'sum_of_pauses()' and 'longest_pause()'.
EXPECT_NO_FATAL_FAILURE(TimePartitionPhasesIteratorTest::validate_pauses(time_partitions, Tickspan()));
EXPECT_FALSE(iter.has_next()) << "Too many elements";
}

View File

@ -0,0 +1,86 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "precompiled.hpp"
#include "memory/metaspace.hpp"
#include "memory/metaspace/virtualSpaceList.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/os.hpp"
#include "unittest.hpp"
using namespace metaspace;
TEST_VM(MetaspaceUtils, reserved) {
size_t reserved = MetaspaceUtils::reserved_bytes();
EXPECT_GT(reserved, 0UL);
size_t reserved_metadata = MetaspaceUtils::reserved_bytes(Metaspace::NonClassType);
EXPECT_GT(reserved_metadata, 0UL);
EXPECT_LE(reserved_metadata, reserved);
}
TEST_VM(MetaspaceUtils, reserved_compressed_class_pointers) {
if (!UseCompressedClassPointers) {
return;
}
size_t reserved = MetaspaceUtils::reserved_bytes();
EXPECT_GT(reserved, 0UL);
size_t reserved_class = MetaspaceUtils::reserved_bytes(Metaspace::ClassType);
EXPECT_GT(reserved_class, 0UL);
EXPECT_LE(reserved_class, reserved);
}
TEST_VM(MetaspaceUtils, committed) {
size_t committed = MetaspaceUtils::committed_bytes();
EXPECT_GT(committed, 0UL);
size_t reserved = MetaspaceUtils::reserved_bytes();
EXPECT_LE(committed, reserved);
size_t committed_metadata = MetaspaceUtils::committed_bytes(Metaspace::NonClassType);
EXPECT_GT(committed_metadata, 0UL);
EXPECT_LE(committed_metadata, committed);
}
TEST_VM(MetaspaceUtils, committed_compressed_class_pointers) {
if (!UseCompressedClassPointers) {
return;
}
size_t committed = MetaspaceUtils::committed_bytes();
EXPECT_GT(committed, 0UL);
size_t committed_class = MetaspaceUtils::committed_bytes(Metaspace::ClassType);
EXPECT_GT(committed_class, 0UL);
EXPECT_LE(committed_class, committed);
}
TEST_VM(MetaspaceUtils, virtual_space_list_large_chunk) {
VirtualSpaceList* vs_list = new VirtualSpaceList(os::vm_allocation_granularity());
MutexLockerEx cl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag);
// A size larger than VirtualSpaceSize (256k) and add one page to make it _not_ be
// vm_allocation_granularity aligned on Windows.
size_t large_size = (size_t)(2*256*K + (os::vm_page_size() / BytesPerWord));
large_size += (os::vm_page_size() / BytesPerWord);
vs_list->get_new_chunk(large_size, 0);
}

View File

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

View File

@ -0,0 +1,208 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "precompiled.hpp"
#include "metaprogramming/isRegisteredEnum.hpp"
#include "runtime/atomic.hpp"
#include "runtime/orderAccess.hpp"
#include "runtime/os.hpp"
#include "utilities/globalCounter.hpp"
#include "utilities/globalCounter.inline.hpp"
#include "utilities/spinYield.hpp"
#include "threadHelper.inline.hpp"
enum NestedTestState {
START,
START_WAIT,
OUTER_ENTERED,
INNER_ENTERED,
INNER_EXITED,
OUTER_EXITED,
SYNCHRONIZING,
SYNCHRONIZED
};
template<> struct IsRegisteredEnum<NestedTestState> : public TrueType {};
class RCUNestedThread : public JavaTestThread {
volatile NestedTestState _state;
volatile bool _proceed;
protected:
RCUNestedThread(Semaphore* post) :
JavaTestThread(post),
_state(START),
_proceed(false)
{}
~RCUNestedThread() {}
void set_state(NestedTestState new_state) {
OrderAccess::release_store(&_state, new_state);
}
void wait_with_state(NestedTestState new_state) {
SpinYield spinner;
OrderAccess::release_store(&_state, new_state);
while (!OrderAccess::load_acquire(&_proceed)) {
spinner.wait();
}
OrderAccess::release_store(&_proceed, false);
}
public:
NestedTestState state() const {
return OrderAccess::load_acquire(&_state);
}
void wait_for_state(NestedTestState goal) {
SpinYield spinner;
while (state() != goal) {
spinner.wait();
}
}
void proceed() {
OrderAccess::release_store(&_proceed, true);
}
};
class RCUNestedReaderThread : public RCUNestedThread {
public:
RCUNestedReaderThread(Semaphore* post) :
RCUNestedThread(post)
{}
virtual void main_run();
};
void RCUNestedReaderThread::main_run() {
wait_with_state(START_WAIT);
{
GlobalCounter::CriticalSection outer(Thread::current());
wait_with_state(OUTER_ENTERED);
{
GlobalCounter::CriticalSection inner(Thread::current());
wait_with_state(INNER_ENTERED);
}
wait_with_state(INNER_EXITED);
}
wait_with_state(OUTER_EXITED);
}
class RCUNestedWriterThread : public RCUNestedThread {
public:
RCUNestedWriterThread(Semaphore* post) :
RCUNestedThread(post)
{}
virtual void main_run();
};
void RCUNestedWriterThread::main_run() {
wait_with_state(START_WAIT);
set_state(SYNCHRONIZING);
GlobalCounter::write_synchronize();
wait_with_state(SYNCHRONIZED);
}
TEST_VM(GlobalCounter, nested_critical_section) {
Semaphore post;
RCUNestedReaderThread* reader = new RCUNestedReaderThread(&post);
RCUNestedWriterThread* outer = new RCUNestedWriterThread(&post);
RCUNestedWriterThread* inner = new RCUNestedWriterThread(&post);
reader->doit();
outer->doit();
inner->doit();
reader->wait_for_state(START_WAIT);
outer->wait_for_state(START_WAIT);
inner->wait_for_state(START_WAIT);
EXPECT_EQ(START_WAIT, reader->state());
EXPECT_EQ(START_WAIT, outer->state());
EXPECT_EQ(START_WAIT, inner->state());
reader->proceed();
reader->wait_for_state(OUTER_ENTERED);
EXPECT_EQ(OUTER_ENTERED, reader->state());
EXPECT_EQ(START_WAIT, outer->state());
EXPECT_EQ(START_WAIT, inner->state());
outer->proceed();
outer->wait_for_state(SYNCHRONIZING);
EXPECT_EQ(OUTER_ENTERED, reader->state());
EXPECT_EQ(SYNCHRONIZING, outer->state());
EXPECT_EQ(START_WAIT, inner->state());
os::naked_short_sleep(100); // Give outer time in synchronization.
EXPECT_EQ(OUTER_ENTERED, reader->state());
EXPECT_EQ(SYNCHRONIZING, outer->state());
EXPECT_EQ(START_WAIT, inner->state());
reader->proceed();
reader->wait_for_state(INNER_ENTERED);
EXPECT_EQ(INNER_ENTERED, reader->state());
EXPECT_EQ(SYNCHRONIZING, outer->state());
EXPECT_EQ(START_WAIT, inner->state());
inner->proceed();
inner->wait_for_state(SYNCHRONIZING);
EXPECT_EQ(INNER_ENTERED, reader->state());
EXPECT_EQ(SYNCHRONIZING, outer->state());
EXPECT_EQ(SYNCHRONIZING, inner->state());
os::naked_short_sleep(100); // Give writers time in synchronization.
EXPECT_EQ(INNER_ENTERED, reader->state());
EXPECT_EQ(SYNCHRONIZING, outer->state());
EXPECT_EQ(SYNCHRONIZING, inner->state());
reader->proceed();
reader->wait_for_state(INNER_EXITED);
// inner does *not* complete synchronization here.
EXPECT_EQ(INNER_EXITED, reader->state());
EXPECT_EQ(SYNCHRONIZING, outer->state());
EXPECT_EQ(SYNCHRONIZING, inner->state());
os::naked_short_sleep(100); // Give writers more time in synchronization.
EXPECT_EQ(INNER_EXITED, reader->state());
EXPECT_EQ(SYNCHRONIZING, outer->state());
EXPECT_EQ(SYNCHRONIZING, inner->state());
reader->proceed();
reader->wait_for_state(OUTER_EXITED);
// Both inner and outer can synchronize now.
outer->wait_for_state(SYNCHRONIZED);
inner->wait_for_state(SYNCHRONIZED);
EXPECT_EQ(OUTER_EXITED, reader->state());
EXPECT_EQ(SYNCHRONIZED, outer->state());
EXPECT_EQ(SYNCHRONIZED, inner->state());
// Wait for reader, outer, and inner to complete.
reader->proceed();
outer->proceed();
inner->proceed();
for (uint i = 0; i < 3; ++i) {
post.wait();
}
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -56,29 +56,29 @@ import java.util.stream.Collectors;
* @run driver ClassFileInstaller sun.hotspot.WhiteBox * @run driver ClassFileInstaller sun.hotspot.WhiteBox
* sun.hotspot.WhiteBox$WhiteBoxPermission * 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:+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: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 * -XX:G1MixedGCCountTarget=1 -XX:G1OldCSetRegionThresholdPercent=100 -XX:SurvivorRatio=1 -XX:InitiatingHeapOccupancyPercent=0
* gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC MIXED_GC * 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 * -XX:G1HeapRegionSize=1M -Xlog:gc*=debug:file=TestObjectGraphAfterGC_YOUNG_GC.gc.log
* gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC YOUNG_GC * 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 * -XX:G1HeapRegionSize=1M -Xlog:gc=info:file=TestObjectGraphAfterGC_FULL_GC.gc.log
* gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC FULL_GC * 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 * -XX:G1HeapRegionSize=1M -Xlog:gc=info:file=TestObjectGraphAfterGC_FULL_GC_MEMORY_PRESSURE.gc.log
* gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC FULL_GC_MEMORY_PRESSURE * 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 * -XX:G1HeapRegionSize=1M -Xlog:gc=info:file=TestObjectGraphAfterGC_CMC.gc.log -XX:MaxTenuringThreshold=16
* gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC CMC * 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 * -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 * gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC CMC_NO_SURV_ROOTS
* *

View File

@ -0,0 +1,222 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
/*
* @test
* @summary Tests how CDS works when critical library classes are replaced with JVMTI ClassFileLoadHook
* @library /test/lib
* @requires vm.cds
* @build sun.hotspot.WhiteBox
* @run driver ClassFileInstaller -jar whitebox.jar sun.hotspot.WhiteBox
* @run main/othervm/native ReplaceCriticalClasses
*/
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import jdk.test.lib.cds.CDSTestUtils;
import jdk.test.lib.cds.CDSOptions;
import jdk.test.lib.process.OutputAnalyzer;
import sun.hotspot.WhiteBox;
public class ReplaceCriticalClasses {
public static void main(String args[]) throws Throwable {
if (args.length == 0) {
launchChildProcesses();
} else if (args.length == 3 && args[0].equals("child")) {
Class klass = Class.forName(args[2].replace("/", "."));
if (args[1].equals("-shared")) {
testInChild(true, klass);
} else if (args[1].equals("-notshared")) {
testInChild(false, klass);
} else {
throw new RuntimeException("Unknown child exec option " + args[1]);
}
return;
} else {
throw new RuntimeException("Usage: @run main/othervm/native ReplaceCriticalClasses");
}
}
static void launchChildProcesses() throws Throwable {
String tests[] = {
// CDS should be disabled -- these critical classes will be replaced
// because JvmtiExport::early_class_hook_env() is true.
"-early -notshared java/lang/Object",
"-early -notshared java/lang/String",
"-early -notshared java/lang/Cloneable",
"-early -notshared java/io/Serializable",
// CDS should not be disabled -- these critical classes cannot be replaced because
// JvmtiExport::early_class_hook_env() is false.
"java/lang/Object",
"java/lang/String",
"java/lang/Cloneable",
"java/io/Serializable",
// Try to replace classes that are used by the archived subgraph graphs.
"-subgraph java/util/ArrayList",
"-subgraph java/lang/module/ResolvedModule",
// Replace classes that are loaded after JVMTI_PHASE_PRIMORDIAL. It's OK to replace such
// classes even when CDS is enabled. Nothing bad should happen.
"-notshared jdk/internal/vm/PostVMInitHook",
"-notshared java/util/Locale",
"-notshared sun/util/locale/BaseLocale",
"-notshared java/lang/Readable",
};
int n = 0;
for (String s : tests) {
System.out.println("Test case[" + (n++) + "] = \"" + s + "\"");
String args[] = s.split("\\s+"); // split by space character
launchChild(args);
}
}
static void launchChild(String args[]) throws Throwable {
if (args.length < 1) {
throw new RuntimeException("Invalid test case. Should be <-early> <-subgraph> <-notshared> klassName");
}
String klassName = null;
String early = "";
boolean subgraph = false;
String shared = "-shared";
for (int i=0; i<args.length-1; i++) {
String opt = args[i];
if (opt.equals("-early")) {
early = "-early,";
} else if (opt.equals("-subgraph")) {
subgraph = true;
} else if (opt.equals("-notshared")) {
shared = opt;
} else {
throw new RuntimeException("Unknown option: " + opt);
}
}
klassName = args[args.length-1];
Class.forName(klassName.replace("/", ".")); // make sure it's a valid class
// We will pass an option like "-agentlib:SimpleClassFileLoadHook=java/util/Locale,XXX,XXX".
// The SimpleClassFileLoadHook agent would attempt to hook the java/util/Locale class
// but leave the class file bytes unchanged (it replaces all bytes "XXX" with "XXX", i.e.,
// a no-op). JVMTI doesn't check the class file bytes returned by the agent, so as long
// as the agent returns a buffer, it will not load the class from CDS, and will instead
// load the class by parsing the buffer.
//
// Note that for safety we don't change the contents of the class file bytes. If in the
// future JVMTI starts checking the contents of the class file bytes, this test would need
// to be updated. (You'd see the test case with java/util/Locale staring to fail).
String agent = "-agentlib:SimpleClassFileLoadHook=" + early + klassName + ",XXX,XXX";
CDSOptions opts = (new CDSOptions())
.setXShareMode("auto")
.setUseSystemArchive(true)
.setUseVersion(false)
.addSuffix("-showversion",
"-Xlog:cds",
"-XX:+UnlockDiagnosticVMOptions",
agent,
"-XX:+WhiteBoxAPI",
"-Xbootclasspath/a:" + ClassFileInstaller.getJarPath("whitebox.jar"));
if (subgraph) {
opts.addSuffix("-Xlog:cds+heap",
"-Xlog:class+load");
}
opts.addSuffix("ReplaceCriticalClasses",
"child",
shared,
klassName);
final boolean expectDisable = !early.equals("");
final boolean checkSubgraph = subgraph;
CDSTestUtils.run(opts).assertNormalExit(out -> {
if (expectDisable) {
out.shouldContain("UseSharedSpaces: CDS is disabled because early JVMTI ClassFileLoadHook is in use.");
System.out.println("CDS disabled as expected");
}
if (checkSubgraph) {
// As of 2018/10/21 the classes in the archived subgraphs won't be
// replaced because all archived subgraphs were loaded in JVMTI_PHASE_PRIMORDIAL.
//
// This is the first class to be loaded after JVMTI has exited JVMTI_PHASE_PRIMORDIAL.
// Make sure no subgraphs are loaded afterwards.
//
// Can't use out.shouldNotMatch() because that doesn't match across multiple lines.
String firstNonPrimordialClass = "jdk.jfr.internal.EventWriter";
String regexp = firstNonPrimordialClass + ".*initialize_from_archived_subgraph";
Pattern regex = Pattern.compile(regexp, Pattern.DOTALL);
Matcher matcher = regex.matcher(out.getStdout());
if (matcher.find()) {
out.reportDiagnosticSummary();
throw new RuntimeException("'" + regexp
+ "' found in stdout: '" + matcher.group() + "' \n");
}
}
});
}
static void testInChild(boolean shouldBeShared, Class klass) {
WhiteBox wb = WhiteBox.getWhiteBox();
if (shouldBeShared && !wb.isSharedClass(klass)) {
throw new RuntimeException(klass + " should be shared but but actually is not.");
}
if (!shouldBeShared && wb.isSharedClass(klass)) {
throw new RuntimeException(klass + " should not be shared but actually is.");
}
System.out.println("wb.isSharedClass(klass): " + wb.isSharedClass(klass) + " == " + shouldBeShared);
String strings[] = {
// interned strings from j.l.Object
"@",
"nanosecond timeout value out of range",
"timeoutMillis value is negative",
// interned strings from j.l.Integer
"0",
"0X",
"0x",
"int"
};
// Make sure the interned string table is same
for (String s : strings) {
String i = s.intern();
if (s != i) {
throw new RuntimeException("Interned string mismatch: \"" + s + "\" @ " + System.identityHashCode(s) +
" vs \"" + i + "\" @ " + System.identityHashCode(i));
}
}
// We have tried to use ClassFileLoadHook to replace critical library classes (which may
// may not have succeeded, depending on whether the agent has requested
// can_generate_all_class_hook_events/can_generate_early_class_hook_events capabilities).
//
// In any case, the JVM should have started properly (perhaps with CDS disabled) and
// the above operations should succeed.
System.out.println("If I can come to here without crashing, things should be OK");
}
}

View File

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

View File

@ -0,0 +1,93 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
/*
* @test HandshakeWalkSuspendExitTest
* @summary This test tries to stress the handshakes with new and exiting threads while suspending them.
* @library /testlibrary /test/lib
* @build HandshakeWalkSuspendExitTest
* @run driver ClassFileInstaller sun.hotspot.WhiteBox
* sun.hotspot.WhiteBox$WhiteBoxPermission
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI HandshakeWalkSuspendExitTest
*/
import jdk.test.lib.Asserts;
import sun.hotspot.WhiteBox;
public class HandshakeWalkSuspendExitTest implements Runnable {
static final int _test_threads = 8;
static final int _test_exit_threads = 128;
static Thread[] _threads = new Thread[_test_threads];
static volatile boolean exit_now = false;
static java.util.concurrent.Semaphore _sem = new java.util.concurrent.Semaphore(0);
@Override
public void run() {
WhiteBox wb = WhiteBox.getWhiteBox();
while (!exit_now) {
_sem.release();
// We only suspend threads on even index and not ourself.
// Otherwise we can accidentially suspend all threads.
for (int i = 0; i < _threads.length; i += 2) {
wb.handshakeWalkStack(null /* ignored */, true /* stackwalk all threads */);
if (Thread.currentThread() != _threads[i]) {
_threads[i].suspend();
_threads[i].resume();
}
}
for (int i = 0; i < _threads.length; i += 2) {
wb.handshakeWalkStack(_threads[i] /* thread to stackwalk */, false /* stackwalk one thread */);
if (Thread.currentThread() != _threads[i]) {
_threads[i].suspend();
_threads[i].resume();
}
}
}
}
public static void main(String... args) throws Exception {
HandshakeWalkSuspendExitTest test = new HandshakeWalkSuspendExitTest();
for (int i = 0; i < _threads.length; i++) {
_threads[i] = new Thread(test);
_threads[i].start();
}
for (int i = 0; i < _test_threads; i++) {
_sem.acquire();
}
Thread[] exit_threads = new Thread[_test_exit_threads];
for (int i = 0; i < _test_exit_threads; i++) {
exit_threads[i] = new Thread(new Runnable() { public void run() {} });
exit_threads[i].start();
}
exit_now = true;
for (int i = 0; i < _threads.length; i++) {
_threads[i].join();
}
for (int i = 0; i < exit_threads.length; i++) {
exit_threads[i].join();
}
}
}

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