diff --git a/doc/testing.html b/doc/testing.html index 457061a7eb9..f66ef9b11e9 100644 --- a/doc/testing.html +++ b/doc/testing.html @@ -18,12 +18,13 @@ -

Using the run-test framework

+

Using "make test" (the run-test framework)

This new way of running tests is developer-centric. It assumes that you have built a JDK locally and want to test it. Running common test targets is simple, and more complex ad-hoc combination of tests is possible. The user interface is forgiving, and clearly report errors it cannot resolve.

-

The main target "run-test" uses the jdk-image as the tested product. There is also an alternate target "exploded-run-test" that uses the exploded image instead. Not all tests will run successfully on the exploded image, but using this target can greatly improve rebuild times for certain workflows.

+

The main target test uses the jdk-image as the tested product. There is also an alternate target exploded-test that uses the exploded image instead. Not all tests will run successfully on the exploded image, but using this target can greatly improve rebuild times for certain workflows.

+

Previously, make test was used invoke an old system for running test, and make run-test was used for the new test framework. For backward compatibility with scripts and muscle memory, run-test (and variants like exploded-run-test or run-test-tier1) are kept as aliases. The old system can still be accessed for some time using cd test && make.

Some example command-lines:

-
$ make run-test-tier1
-$ make run-test-jdk_lang JTREG="JOBS=8"
-$ make run-test TEST=jdk_lang
-$ make run-test-only TEST="gtest:LogTagSet gtest:LogTagSetDescriptions" GTEST="REPEAT=-1"
-$ make run-test TEST="hotspot:hotspot_gc" JTREG="JOBS=1;TIMEOUT=8;VM_OPTIONS=-XshowSettings -Xlog:gc+ref=debug"
-$ make run-test TEST="jtreg:test/hotspot:hotspot_gc test/hotspot/jtreg/native_sanity/JniVersion.java"
-$ make exploded-run-test TEST=tier2
+
$ make test-tier1
+$ make test-jdk_lang JTREG="JOBS=8"
+$ make test TEST=jdk_lang
+$ make test-only TEST="gtest:LogTagSet gtest:LogTagSetDescriptions" GTEST="REPEAT=-1"
+$ make test TEST="hotspot:hotspot_gc" JTREG="JOBS=1;TIMEOUT=8;VM_OPTIONS=-XshowSettings -Xlog:gc+ref=debug"
+$ make test TEST="jtreg:test/hotspot:hotspot_gc test/hotspot/jtreg/native_sanity/JniVersion.java"
+$ make exploded-test TEST=tier2

Configuration

To be able to run JTReg tests, configure needs to know where to find the JTReg test framework. If it is not picked up automatically by configure, use the --with-jtreg=<path to jtreg home> option to point to the JTReg framework. Note that this option should point to the JTReg home, i.e. the top directory, containing lib/jtreg.jar etc. (An alternative is to set the JT_HOME environment variable to point to the JTReg home before running configure.)

Test selection

-

All functionality is available using the run-test make target. In this use case, the test or tests to be executed is controlled using the TEST variable. To speed up subsequent test runs with no source code changes, run-test-only can be used instead, which do not depend on the source and test image build.

-

For some common top-level tests, direct make targets have been generated. This includes all JTReg test groups, the hotspot gtest, and custom tests (if present). This means that make run-test-tier1 is equivalent to make run-test 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.

+

All functionality is available using the test make target. In this use case, the test or tests to be executed is controlled using the TEST variable. To speed up subsequent test runs with no source code changes, test-only can be used instead, which do not depend on the source and test image build.

+

For some common top-level tests, direct make targets have been generated. This includes all JTReg test groups, the hotspot gtest, and custom tests (if present). This means that make test-tier1 is equivalent to make test TEST="tier1", but the latter is more tab-completion friendly. For more complex test runs, the test TEST="x" solution needs to be used.

The test specifications given in TEST is parsed into fully qualified test descriptors, which clearly and unambigously show which tests will be run. As an example, :tier1 will expand to 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. You can always submit a list of fully qualified test descriptors in the TEST variable if you want to shortcut the parser.

JTReg

JTReg tests can be selected either by picking a JTReg test group, or a selection of files or directories containing JTReg tests.

@@ -59,6 +61,14 @@ $ make exploded-run-test TEST=tier2

Since the Hotspot Gtest suite is so quick, the default is to run all tests. This is specified by just gtest, or as a fully qualified test descriptor gtest:all.

If you want, you can single out an individual test or a group of tests, for instance gtest:LogDecorations or gtest:LogDecorations.level_test_vm. This can be particularly useful if you want to run a shaky test repeatedly.

For Gtest, there is a separate test suite for each JVM variant. The JVM variant is defined by adding /<variant> to the test descriptor, e.g. gtest:Log/client. 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 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.

+

Test results and summary

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:

==============================
@@ -72,7 +82,7 @@ Test summary
 TEST FAILURE

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 >> ... << marker for easy identification.

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.

-

In case of test failures, make run-test will exit with a non-zero exit value.

+

In case of test failures, make test will exit with a non-zero exit value.

All tests have their result stored in build/$BUILD/test-results/$TEST_ID, where TEST_ID is a path-safe conversion from the fully qualified test descriptor, e.g. for jtreg:jdk/test:tier1 the TEST_ID is jtreg_jdk_test_tier1. This path is also printed in the log at the end of the test run.

Additional work data is stored in build/$BUILD/test-support/$TEST_ID. For some frameworks, this directory might contain information that is useful in determining the cause of a failed test.

Test suite control

diff --git a/doc/testing.md b/doc/testing.md index 7aa40a6c42b..fc7752274a5 100644 --- a/doc/testing.md +++ b/doc/testing.md @@ -1,26 +1,32 @@ % Testing the JDK -## Using the run-test framework +## Using "make test" (the run-test framework) This new way of running tests is developer-centric. It assumes that you have built a JDK locally and want to test it. Running common test targets is simple, and more complex ad-hoc combination of tests is possible. The user interface is forgiving, and clearly report errors it cannot resolve. -The main target "run-test" uses the jdk-image as the tested product. There is -also an alternate target "exploded-run-test" that uses the exploded image +The main target `test` uses the jdk-image as the tested product. There is +also an alternate target `exploded-test` that uses the exploded image instead. Not all tests will run successfully on the exploded image, but using this target can greatly improve rebuild times for certain workflows. +Previously, `make test` was used invoke an old system for running test, and +`make run-test` was used for the new test framework. For backward compatibility +with scripts and muscle memory, `run-test` (and variants like +`exploded-run-test` or `run-test-tier1`) are kept as aliases. The old system +can still be accessed for some time using `cd test && make`. + Some example command-lines: - $ make run-test-tier1 - $ make run-test-jdk_lang JTREG="JOBS=8" - $ make run-test TEST=jdk_lang - $ make run-test-only TEST="gtest:LogTagSet gtest:LogTagSetDescriptions" GTEST="REPEAT=-1" - $ make run-test TEST="hotspot:hotspot_gc" JTREG="JOBS=1;TIMEOUT=8;VM_OPTIONS=-XshowSettings -Xlog:gc+ref=debug" - $ make run-test TEST="jtreg:test/hotspot:hotspot_gc test/hotspot/jtreg/native_sanity/JniVersion.java" - $ make exploded-run-test TEST=tier2 + $ make test-tier1 + $ make test-jdk_lang JTREG="JOBS=8" + $ make test TEST=jdk_lang + $ make test-only TEST="gtest:LogTagSet gtest:LogTagSetDescriptions" GTEST="REPEAT=-1" + $ make test TEST="hotspot:hotspot_gc" JTREG="JOBS=1;TIMEOUT=8;VM_OPTIONS=-XshowSettings -Xlog:gc+ref=debug" + $ make test TEST="jtreg:test/hotspot:hotspot_gc test/hotspot/jtreg/native_sanity/JniVersion.java" + $ make exploded-test TEST=tier2 ### Configuration @@ -33,16 +39,16 @@ environment variable to point to the JTReg home before running `configure`.) ## Test selection -All functionality is available using the run-test make target. In this use -case, the test or tests to be executed is controlled using the `TEST` variable. -To speed up subsequent test runs with no source code changes, run-test-only can -be used instead, which do not depend on the source and test image build. +All functionality is available using the `test` make target. In this use case, +the test or tests to be executed is controlled using the `TEST` variable. To +speed up subsequent test runs with no source code changes, `test-only` can be +used instead, which do not depend on the source and test image build. For some common top-level tests, direct make targets have been generated. This includes all JTReg test groups, the hotspot gtest, and custom tests (if -present). This means that `make run-test-tier1` is equivalent to `make run-test +present). This means that `make test-tier1` is equivalent to `make test TEST="tier1"`, but the latter is more tab-completion friendly. For more complex -test runs, the `run-test TEST="x"` solution needs to be used. +test runs, the `test TEST="x"` solution needs to be used. The test specifications given in `TEST` is parsed into fully qualified test descriptors, which clearly and unambigously show which tests will be run. As an @@ -98,6 +104,27 @@ is defined by adding `/` to the test descriptor, e.g. variant present (e.g. server, client). So if you only have the server JVM present, then `gtest:all` will be equivalent to `gtest:all/server`. +### Special tests + +A handful of odd tests that are not covered by any other testing framework are +accessible using the `special:` test descriptor. Currently, this includes +`hotspot-internal`, `failure-handler` and `make`. + + * Hotspot legacy internal testing (run using `-XX:+ExecuteInternalVMTests`) + is run using `special:hotspot-internal` or just `hotspot-internal` as test + descriptor, and will only work on a debug JVM. + + * Failure handler testing is run using `special:failure-handler` or just + `failure-handler` as test descriptor. + + * Tests for the build system, including both makefiles and related + functionality, is run using `special:make` or just `make` as test + descriptor. This is equivalent to `special:make:all`. + + A specific make test can be run by supplying it as argument, e.g. + `special:make:idea`. As a special syntax, this can also be expressed as + `make-idea`, which allows for command lines as `make test-make-idea`. + ## Test results and summary At the end of the test run, a summary of all tests run will be presented. This @@ -123,7 +150,7 @@ the summary, ERROR is used as a catch-all for tests that neither passed nor are classified as failed by the framework. This might indicate test framework error, timeout or other problems. -In case of test failures, `make run-test` will exit with a non-zero exit value. +In case of test failures, `make test` will exit with a non-zero exit value. All tests have their result stored in `build/$BUILD/test-results/$TEST_ID`, where TEST_ID is a path-safe conversion from the fully qualified test diff --git a/make/Docs.gmk b/make/Docs.gmk index fdaed245e76..a3adae9117a 100644 --- a/make/Docs.gmk +++ b/make/Docs.gmk @@ -61,7 +61,7 @@ MODULES_SOURCE_PATH := $(call PathList, $(call GetModuleSrcPath) \ $(SUPPORT_OUTPUTDIR)/rmic/* $(TOPDIR)/src/*/share/doc/stub) # URLs -JAVADOC_BASE_URL := https://www.oracle.com/pls/topic/lookup?ctx=javase$(VERSION_NUMBER)&id=homepage +JAVADOC_BASE_URL := https://docs.oracle.com/pls/topic/lookup?ctx=javase$(VERSION_NUMBER)&id=homepage BUG_SUBMIT_URL := https://bugreport.java.com/bugreport/ COPYRIGHT_URL := {@docroot}/../legal/copyright.html LICENSE_URL := https://www.oracle.com/technetwork/java/javase/terms/license/java$(VERSION_NUMBER)speclicense.html diff --git a/make/Help.gmk b/make/Help.gmk index e58f54862af..7fe15453249 100644 --- a/make/Help.gmk +++ b/make/Help.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -55,10 +55,10 @@ help: $(info $(_) make install # Install the generated images locally) $(info $(_) make reconfigure # Rerun configure with the same arguments as last time) $(info $(_) make help # Give some help on using make) - $(info $(_) make test # Run tests, default is all tests (see TEST below)) - $(info $(_) make run-test- # Run test, e.g. run-test-tier1) - $(info $(_) make run-test TEST= # Run test(s) given by TEST specification) - $(info $(_) make exploded-run-test TEST= # Run test(s) on the exploded image instead of) + $(info $(_) make check # Run basic testing (currently tier1)) + $(info $(_) make test- # Run test, e.g. test-tier1) + $(info $(_) make test TEST= # Run test(s) given by TEST specification) + $(info $(_) make exploded-test TEST= # Run test(s) on the exploded image instead of) $(info $(_) # the full jdk image) $(info ) $(info Targets for cleaning) @@ -99,10 +99,12 @@ help: $(info $(_) TEST_JOBS= # Run parallel test jobs) $(info $(_) CONF_CHECK= # What to do if spec file is out of date) $(info $(_) # method is 'auto', 'ignore' or 'fail' (default)) - $(info $(_) make test TEST= # Only run the given test or tests, e.g.) - $(info $(_) # make test TEST="jdk_lang jdk_net") - $(info $(_) JTREG="OPT1=x;OPT2=y" # Control the JTREG test harness for run-test) - $(info $(_) GTEST="OPT1=x;OPT2=y" # Control the GTEST test harness for run-test) + $(info $(_) TEST="test1 ..." # Use the given test descriptor(s) for testing, e.g.) + $(info $(_) # make test TEST="jdk_lang gtest:all") + $(info $(_) JTREG="OPT1=x;OPT2=y" # Control the JTREG test harness) + $(info $(_) GTEST="OPT1=x;OPT2=y" # Control the GTEST test harness) + $(info $(_) TEST_OPTS="OPT1=x;..." # Generic control of all test harnesses) + $(info $(_) TEST_VM_OPTS="ARG ..." # Same as setting TEST_OPTS to VM_OPTIONS="ARG ...") $(info ) $(if $(all_confs), $(info Available configurations in $(build_dir):) $(foreach var,$(all_confs),$(info * $(var))),\ $(info No configurations were found in $(build_dir).) $(info Run 'bash configure' to create a configuration.)) diff --git a/make/Main.gmk b/make/Main.gmk index d16463910bc..ef60594ba0a 100644 --- a/make/Main.gmk +++ b/make/Main.gmk @@ -473,10 +473,10 @@ ALL_TARGETS += $(INTERIM_JMOD_TARGETS) interim-image generate-link-opt-data # define DeclareRunTestRecipe - run-test-$1: + test-$1: +($(CD) $(TOPDIR)/make && $(MAKE) $(MAKE_ARGS) -f RunTests.gmk run-test TEST="$1") - exploded-run-test-$1: + exploded-test-$1: +($(CD) $(TOPDIR)/make && $(MAKE) $(MAKE_ARGS) -f RunTests.gmk run-test \ TEST="$1" JDK_IMAGE_DIR=$(JDK_OUTPUTDIR)) @@ -484,8 +484,8 @@ endef # ALL_NAMED_TESTS is defined in FindTests.gmk $(foreach t, $(ALL_NAMED_TESTS), $(eval $(call DeclareRunTestRecipe,$t))) -ALL_TEST_TARGETS := $(addprefix run-test-, $(ALL_NAMED_TESTS)) -ALL_EXPLODED_TEST_TARGETS := $(addprefix exploded-run-test-, $(ALL_NAMED_TESTS)) +ALL_TEST_TARGETS := $(addprefix test-, $(ALL_NAMED_TESTS)) +ALL_EXPLODED_TEST_TARGETS := $(addprefix exploded-test-, $(ALL_NAMED_TESTS)) ALL_TARGETS += $(ALL_TEST_TARGETS) $(ALL_EXPLODED_TEST_TARGETS) @@ -520,13 +520,6 @@ test-image-hotspot-jtreg-graal: +($(CD) $(TOPDIR)/make/test && $(MAKE) $(MAKE_ARGS) -f JtregGraalUnit.gmk \ test-image-hotspot-jtreg-graal) -run-test: - +($(CD) $(TOPDIR)/make && $(MAKE) $(MAKE_ARGS) -f RunTests.gmk run-test TEST="$(TEST)") - -exploded-run-test: - +($(CD) $(TOPDIR)/make && $(MAKE) $(MAKE_ARGS) -f RunTests.gmk run-test \ - TEST="$(TEST)" JDK_IMAGE_DIR=$(JDK_OUTPUTDIR)) - ifeq ($(BUILD_GTEST), true) test-image-hotspot-gtest: +($(CD) $(TOPDIR)/make/hotspot/test && $(MAKE) $(MAKE_ARGS) -f GtestImage.gmk) @@ -541,11 +534,6 @@ ifeq ($(BUILD_FAILURE_HANDLER), true) +($(CD) $(TOPDIR)/make/test && $(MAKE) $(MAKE_ARGS) \ -f BuildFailureHandler.gmk build) - # Runs the tests for the failure handler jtreg extension - test-failure-handler: - +($(CD) $(TOPDIR)/make/test && $(MAKE) $(MAKE_ARGS) \ - -f BuildFailureHandler.gmk test) - # Copies the failure handler jtreg extension into the test image test-image-failure-handler: +($(CD) $(TOPDIR)/make/test && $(MAKE) $(MAKE_ARGS) \ @@ -556,40 +544,19 @@ ALL_TARGETS += prepare-test-image build-test-hotspot-jtreg-native \ test-image-hotspot-jtreg-native build-test-jdk-jtreg-native \ test-image-jdk-jtreg-native build-test-lib build-test-failure-handler \ test-failure-handler test-image-failure-handler test-image-hotspot-gtest \ - test-image-hotspot-jtreg-graal build-test-hotspot-jtreg-graal \ - run-test exploded-run-test + test-image-hotspot-jtreg-graal build-test-hotspot-jtreg-graal ################################################################################ # Run tests -# Run tests specified by $(TEST), or the default test set. test: - $(call RunTests, $(TEST), $(JDK_IMAGE_DIR)) + +($(CD) $(TOPDIR)/make && $(MAKE) $(MAKE_ARGS) -f RunTests.gmk run-test TEST="$(TEST)") -test-hotspot-jtreg: - $(call RunTests, "hotspot_all", $(JDK_IMAGE_DIR)) +exploded-test: + +($(CD) $(TOPDIR)/make && $(MAKE) $(MAKE_ARGS) -f RunTests.gmk run-test \ + TEST="$(TEST)" JDK_IMAGE_DIR=$(JDK_OUTPUTDIR)) -test-hotspot-jtreg-native: - $(call RunTests, "hotspot_native_sanity", $(JDK_IMAGE_DIR)) - -test-hotspot-internal: - $(call RunTests, "hotspot_internal", $(JDK_OUTPUTDIR)) - -test-hotspot-gtest: - $(call RunTests, "hotspot_gtest", $(JDK_OUTPUTDIR)) - -test-jdk-jtreg-native: - $(call RunTests, "jdk_native_sanity", $(JDK_IMAGE_DIR)) - -test-make: - ($(CD) $(TOPDIR)/test/make && $(MAKE) $(MAKE_ARGS) -f TestMake.gmk $(TEST_TARGET)) - -test-compile-commands: - ($(CD) $(TOPDIR)/test/make && $(MAKE) $(MAKE_ARGS) -f TestMake.gmk test-compile-commands) - -ALL_TARGETS += test test-hotspot-jtreg test-hotspot-jtreg-native \ - test-hotspot-internal test-hotspot-gtest test-jdk-jtreg-native test-make \ - test-compile-commands +ALL_TARGETS += test exploded-test ################################################################################ # Bundles @@ -874,14 +841,18 @@ else docs-zip: docs-jdk + # Tests test: jdk-image test-image - run-test: jdk-image test-image - exploded-run-test: exploded-image test-image + exploded-test: exploded-image test-image + + test-make: clean-test-make compile-commands + + test-make-compile-commands: compile-commands # Declare dependency for all generated test targets - $(foreach t, $(ALL_TEST_TARGETS), $(eval $t: jdk-image test-image)) - $(foreach t, $(ALL_EXPLODED_TEST_TARGETS), $(eval $t: exploded-image test-image)) + $(foreach t, $(filter-out test-make%, $(ALL_TEST_TARGETS)), $(eval $t: jdk-image test-image)) + $(foreach t, $(filter-out exploded-test-make%, $(ALL_EXPLODED_TEST_TARGETS)), $(eval $t: exploded-image test-image)) create-buildjdk-copy: jdk.jlink-java java.base-gendata \ $(addsuffix -java, $(INTERIM_IMAGE_MODULES)) @@ -890,16 +861,10 @@ else interim-image: $(INTERIM_JMOD_TARGETS) - test-make: clean-test-make - - test-compile-commands: compile-commands - build-test-lib: exploded-image-optimize build-test-failure-handler: interim-langtools - test-failure-handler: build-test-failure-handler - test-image-failure-handler: build-test-failure-handler build-test-hotspot-jtreg-native: buildtools-jdk \ @@ -917,12 +882,6 @@ else test-image-hotspot-gtest: hotspot - test-hotspot-internal: exploded-image - - test-hotspot-jtreg: jdk-image test-image - - test-hotspot-gtest: exploded-image test-image-hotspot-gtest - install: product-images product-bundles: product-images @@ -1091,6 +1050,30 @@ all: all-images ALL_TARGETS += default jdk images docs bundles all +# Aliases used for running tests. + +# Let "run-test" be an alias for "test" +$(foreach t, $(ALL_NAMED_TESTS), $(eval run-test-$t: test-$t)) +$(foreach t, $(ALL_NAMED_TESTS), $(eval exploded-run-test-$t: exploded-test-$t)) +RUN_TEST_TARGETS := $(addprefix run-test-, $(ALL_NAMED_TESTS)) \ + $(addprefix exploded-run-test-, $(ALL_NAMED_TESTS)) + +run-test: test +exploded-run-test: exploded-test + +# "make check" is a common idiom for running basic testing +check: test-tier1 + +# Keep some old names as aliases +test-hotspot-jtreg: test-hotspot_all +test-hotspot-jtreg-native: test-hotspot_native_sanity +test-hotspot-gtest: exploded-test-gtest +test-jdk-jtreg-native: test-jdk_native_sanity + +ALL_TARGETS += $(RUN_TEST_TARGETS) run-test exploded-run-test check \ + test-hotspot-jtreg test-hotspot-jtreg-native test-hotspot-gtest \ + test-jdk-jtreg-native + ################################################################################ ################################################################################ # diff --git a/make/MainSupport.gmk b/make/MainSupport.gmk index c7710ecb2c2..d7796c509d5 100644 --- a/make/MainSupport.gmk +++ b/make/MainSupport.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -30,17 +30,6 @@ ifndef _MAINSUPPORT_GMK _MAINSUPPORT_GMK := 1 -# Run the tests specified by $1, with PRODUCT_HOME specified by $2 -# JT_JAVA is picked up by the jtreg launcher and used to run Jtreg itself. -define RunTests - ($(CD) $(TOPDIR)/test && $(MAKE) $(MAKE_ARGS) -j1 -k MAKEFLAGS= \ - JT_HOME=$(JT_HOME) PRODUCT_HOME=$(strip $2) \ - TEST_IMAGE_DIR=$(TEST_IMAGE_DIR) \ - ALT_OUTPUTDIR=$(OUTPUTDIR) TEST_JOBS=$(TEST_JOBS) \ - JT_JAVA=$(BOOT_JDK) JIB_JAR=$(JIB_JAR) \ - JOBS=$(JOBS) $1) || true -endef - define CleanDocs @$(PRINTF) "Cleaning docs ..." @$(PRINTF) "\n" $(LOG_DEBUG) diff --git a/make/RunTests.gmk b/make/RunTests.gmk index cffa93aae37..7b661be775f 100644 --- a/make/RunTests.gmk +++ b/make/RunTests.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -300,16 +300,32 @@ endef # Helper function to determine if a test specification is a special test # -# It is a special test if it is "special:" followed by a test name. +# It is a special test if it is "special:" followed by a test name, +# if it is "make:" or "make-" followed by a make test, or any of the special test names +# as a single word. define ParseSpecialTestSelection $(if $(filter special:%, $1), \ $1 \ + ) \ + $(if $(filter make%, $1), \ + $(if $(filter make:%, $1), \ + special:$(strip $1) \ + ) \ + $(if $(filter make-%, $1), \ + special:$(patsubst make-%,make:%, $1) \ + ) \ + $(if $(filter make, $1), \ + special:make:all \ + ) + ) \ + $(if $(filter hotspot-internal failure-handler, $1), \ + special:$(strip $1) \ ) endef ifeq ($(TEST), ) $(info No test selection given in TEST!) - $(info Please use e.g. 'run-test TEST=tier1' or 'run-test-tier1') + $(info Please use e.g. 'make test TEST=tier1' or 'make test-tier1') $(info See doc/testing.[md|html] for help) $(error Cannot continue) endif @@ -663,9 +679,13 @@ define SetupRunSpecialTestBody $$(JDK_IMAGE_DIR)/bin/java -XX:+ExecuteInternalVMTests \ -XX:+ShowMessageBoxOnError -version else ifeq ($$($1_TEST_NAME), failure-handler) - $1_TEST_COMMAND_LINE := \ - ($(CD) $(TOPDIR)/make/test && $(MAKE) $(MAKE_ARGS) -f \ - BuildFailureHandler.gmk test) + ifeq ($(BUILD_FAILURE_HANDLER), true) + $1_TEST_COMMAND_LINE := \ + ($(CD) $(TOPDIR)/make/test && $(MAKE) $(MAKE_ARGS) -f \ + BuildFailureHandler.gmk test) + else + $$(error Cannot test failure handler if it is not built) + endif else ifeq ($$($1_TEST_NAME), make) $1_TEST_COMMAND_LINE := \ ($(CD) $(TOPDIR)/test/make && $(MAKE) $(MAKE_ARGS) -f \ diff --git a/make/common/FindTests.gmk b/make/common/FindTests.gmk index 35a9e20f305..e84152579c9 100644 --- a/make/common/FindTests.gmk +++ b/make/common/FindTests.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -72,6 +72,15 @@ ALL_NAMED_TESTS += $(JTREG_TEST_GROUPS) # Add Gtest ALL_NAMED_TESTS += gtest +# Find make test targets +MAKE_TEST_TARGETS := $(shell $(MAKE) -s --no-print-directory $(MAKE_ARGS) \ + SPEC=$(SPEC) -f $(TOPDIR)/test/make/TestMake.gmk print-targets) + +ALL_NAMED_TESTS += $(addprefix make-, $(MAKE_TEST_TARGETS)) + +# Add special tests +ALL_NAMED_TESTS += hotspot-internal failure-handler make + ################################################################################ endif # _FIND_TESTS_GMK diff --git a/make/jdk/src/classes/build/tools/taglet/ExtLink.java b/make/jdk/src/classes/build/tools/taglet/ExtLink.java index f3f9c7c5fc8..be261bc4a46 100644 --- a/make/jdk/src/classes/build/tools/taglet/ExtLink.java +++ b/make/jdk/src/classes/build/tools/taglet/ExtLink.java @@ -48,7 +48,7 @@ import static com.sun.source.doctree.DocTree.Kind.*; * will produce the following html *

* {@code - * Please see a spectacular sight. + * Please see a spectacular sight. * } */ public class ExtLink implements Taglet { @@ -63,7 +63,7 @@ public class ExtLink implements Taglet { static final String TAG_NAME = "extLink"; - static final String URL = "https://www.oracle.com/pls/topic/lookup?ctx=javase" + + static final String URL = "https://docs.oracle.com/pls/topic/lookup?ctx=javase" + SPEC_VERSION + "&id="; static final Pattern TAG_PATTERN = Pattern.compile("(?s)(\\s*)(?\\w+)(\\s+)(?.*)$"); diff --git a/src/hotspot/share/aot/aotLoader.cpp b/src/hotspot/share/aot/aotLoader.cpp index d08b8e1cf37..3e1c83039ee 100644 --- a/src/hotspot/share/aot/aotLoader.cpp +++ b/src/hotspot/share/aot/aotLoader.cpp @@ -47,13 +47,8 @@ void AOTLoader::load_for_klass(InstanceKlass* ik, Thread* thread) { return; } if (UseAOT) { - if (JvmtiExport::can_hotswap_or_post_breakpoint()) { - if (PrintAOT) { - warning("JVMTI capability to hotswap and post breakpoint is not compatible with AOT (switching AOT off)"); - } - FLAG_SET_DEFAULT(UseAOT, false); - return; - } + // We allow hotswap to be enabled after the onload phase, but not breakpoints + assert(!JvmtiExport::can_post_breakpoint(), "AOT should have been disabled."); FOR_ALL_AOT_HEAPS(heap) { (*heap)->load_klass_data(ik, thread); } @@ -120,9 +115,9 @@ void AOTLoader::initialize() { return; } - if (JvmtiExport::can_hotswap_or_post_breakpoint()) { + if (JvmtiExport::can_post_breakpoint()) { if (PrintAOT) { - warning("JVMTI capability to hotswap and post breakpoint is not compatible with AOT (switching AOT off)"); + warning("JVMTI capability to post breakpoint is not compatible with AOT (switching AOT off)"); } FLAG_SET_DEFAULT(UseAOT, false); return; diff --git a/src/hotspot/share/c1/c1_Compiler.cpp b/src/hotspot/share/c1/c1_Compiler.cpp index c3b1547900f..76836e75858 100644 --- a/src/hotspot/share/c1/c1_Compiler.cpp +++ b/src/hotspot/share/c1/c1_Compiler.cpp @@ -79,7 +79,6 @@ void Compiler::initialize() { } int Compiler::code_buffer_size() { - assert(SegmentedCodeCache, "Should be only used with a segmented code cache"); return Compilation::desired_max_code_buffer_size() + Compilation::desired_max_constant_size(); } @@ -90,10 +89,7 @@ BufferBlob* Compiler::init_buffer_blob() { // setup CodeBuffer. Preallocate a BufferBlob of size // NMethodSizeLimit plus some extra space for constants. - int code_buffer_size = Compilation::desired_max_code_buffer_size() + - Compilation::desired_max_constant_size(); - - BufferBlob* buffer_blob = BufferBlob::create("C1 temporary CodeBuffer", code_buffer_size); + BufferBlob* buffer_blob = BufferBlob::create("C1 temporary CodeBuffer", code_buffer_size()); if (buffer_blob != NULL) { CompilerThread::current()->set_buffer_blob(buffer_blob); } diff --git a/src/hotspot/share/classfile/classLoaderDataGraph.cpp b/src/hotspot/share/classfile/classLoaderDataGraph.cpp index 350570e4f10..f1a15f6ddd8 100644 --- a/src/hotspot/share/classfile/classLoaderDataGraph.cpp +++ b/src/hotspot/share/classfile/classLoaderDataGraph.cpp @@ -287,54 +287,50 @@ class ClassLoaderDataGraphIterator : public StackObj { Handle _holder; Thread* _thread; - void hold_next() { - if (_next != NULL) { - _holder = Handle(_thread, _next->holder_phantom()); - } - } public: ClassLoaderDataGraphIterator() : _next(ClassLoaderDataGraph::_head) { _thread = Thread::current(); assert_locked_or_safepoint(ClassLoaderDataGraph_lock); - hold_next(); - } - - bool repeat() const { - return _next != NULL; } ClassLoaderData* get_next() { - ClassLoaderData* next = _next; - if (_next != NULL) { - _next = _next->next(); - hold_next(); + ClassLoaderData* cld = _next; + // Skip already unloaded CLD for concurrent unloading. + while (cld != NULL && !cld->is_alive()) { + cld = cld->next(); } - return next; + if (cld != NULL) { + // Keep cld that is being returned alive. + _holder = Handle(_thread, cld->holder_phantom()); + _next = cld->next(); + } else { + _next = NULL; + } + return cld; } + + }; // These functions assume that the caller has locked the ClassLoaderDataGraph_lock // if they are not calling the function from a safepoint. void ClassLoaderDataGraph::classes_do(KlassClosure* klass_closure) { ClassLoaderDataGraphIterator iter; - while (iter.repeat()) { - ClassLoaderData* cld = iter.get_next(); + while (ClassLoaderData* cld = iter.get_next()) { cld->classes_do(klass_closure); } } void ClassLoaderDataGraph::classes_do(void f(Klass* const)) { ClassLoaderDataGraphIterator iter; - while (iter.repeat()) { - ClassLoaderData* cld = iter.get_next(); + while (ClassLoaderData* cld = iter.get_next()) { cld->classes_do(f); } } void ClassLoaderDataGraph::methods_do(void f(Method*)) { ClassLoaderDataGraphIterator iter; - while (iter.repeat()) { - ClassLoaderData* cld = iter.get_next(); + while (ClassLoaderData* cld = iter.get_next()) { cld->methods_do(f); } } @@ -342,8 +338,7 @@ void ClassLoaderDataGraph::methods_do(void f(Method*)) { void ClassLoaderDataGraph::modules_do(void f(ModuleEntry*)) { assert_locked_or_safepoint(Module_lock); ClassLoaderDataGraphIterator iter; - while (iter.repeat()) { - ClassLoaderData* cld = iter.get_next(); + while (ClassLoaderData* cld = iter.get_next()) { cld->modules_do(f); } } @@ -361,8 +356,7 @@ void ClassLoaderDataGraph::modules_unloading_do(void f(ModuleEntry*)) { void ClassLoaderDataGraph::packages_do(void f(PackageEntry*)) { assert_locked_or_safepoint(Module_lock); ClassLoaderDataGraphIterator iter; - while (iter.repeat()) { - ClassLoaderData* cld = iter.get_next(); + while (ClassLoaderData* cld = iter.get_next()) { cld->packages_do(f); } } @@ -379,8 +373,7 @@ void ClassLoaderDataGraph::packages_unloading_do(void f(PackageEntry*)) { void ClassLoaderDataGraph::loaded_classes_do(KlassClosure* klass_closure) { ClassLoaderDataGraphIterator iter; - while (iter.repeat()) { - ClassLoaderData* cld = iter.get_next(); + while (ClassLoaderData* cld = iter.get_next()) { cld->loaded_classes_do(klass_closure); } } @@ -404,8 +397,7 @@ void ClassLoaderDataGraph::classes_unloading_do(void f(Klass* const)) { } #define FOR_ALL_DICTIONARY(X) ClassLoaderDataGraphIterator iter; \ - ClassLoaderData* X; \ - while ((X = iter.get_next()) != NULL) \ + while (ClassLoaderData* X = iter.get_next()) \ if (X->dictionary() != NULL) // Walk classes in the loaded class dictionaries in various forms. @@ -696,16 +688,14 @@ extern "C" int print_loader_data_graph() { void ClassLoaderDataGraph::verify() { ClassLoaderDataGraphIterator iter; - while (iter.repeat()) { - ClassLoaderData* cld = iter.get_next(); + while (ClassLoaderData* cld = iter.get_next()) { cld->verify(); } } void ClassLoaderDataGraph::print_on(outputStream * const out) { ClassLoaderDataGraphIterator iter; - while (iter.repeat()) { - ClassLoaderData* cld = iter.get_next(); + while (ClassLoaderData* cld = iter.get_next()) { cld->print_on(out); } } diff --git a/src/hotspot/share/classfile/dictionary.cpp b/src/hotspot/share/classfile/dictionary.cpp index 272a5d0ae0b..fc767ca6f93 100644 --- a/src/hotspot/share/classfile/dictionary.cpp +++ b/src/hotspot/share/classfile/dictionary.cpp @@ -468,7 +468,7 @@ bool Dictionary::is_valid_protection_domain(unsigned int hash, #if INCLUDE_CDS static bool is_jfr_event_class(Klass *k) { while (k) { - if (k->name()->equals("jdk/jfr/Event")) { + if (k->name()->equals("jdk/internal/event/Event")) { return true; } k = k->super(); diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index f94f5c655d6..dfa094b3f76 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -50,6 +50,7 @@ #include "oops/oop.inline.hpp" #include "oops/symbol.hpp" #include "oops/typeArrayOop.inline.hpp" +#include "prims/jvmtiExport.hpp" #include "prims/resolvedMethodTable.hpp" #include "runtime/fieldDescriptor.inline.hpp" #include "runtime/frame.inline.hpp" @@ -125,7 +126,7 @@ static void compute_offset(int &dest_offset, if (ik == NULL) { ResourceMark rm; log_error(class)("Mismatch JDK version for field: %s type: %s", name_symbol->as_C_string(), signature_symbol->as_C_string()); - vm_exit_during_initialization("Invalid layout of preloaded class"); + vm_exit_during_initialization("Invalid layout of well-known class"); } if (!ik->find_local_field(name_symbol, signature_symbol, &fd) || fd.is_static() != is_static) { @@ -138,7 +139,7 @@ static void compute_offset(int &dest_offset, LogStream ls(lt.error()); ik->print_on(&ls); #endif //PRODUCT - vm_exit_during_initialization("Invalid layout of preloaded class: use -Xlog:class+load=info to see the origin of the problem class"); + vm_exit_during_initialization("Invalid layout of well-known class: use -Xlog:class+load=info to see the origin of the problem class"); } dest_offset = fd.offset(); } @@ -151,7 +152,7 @@ static void compute_offset(int& dest_offset, InstanceKlass* ik, if (name == NULL) { ResourceMark rm; log_error(class)("Name %s should be in the SymbolTable since its class is loaded", name_string); - vm_exit_during_initialization("Invalid layout of preloaded class", ik->external_name()); + vm_exit_during_initialization("Invalid layout of well-known class", ik->external_name()); } compute_offset(dest_offset, ik, name, signature_symbol, is_static); } @@ -1196,7 +1197,7 @@ bool java_lang_Class::restore_archived_mirror(Klass *k, Handle class_loader, Handle module, Handle protection_domain, TRAPS) { // Postpone restoring archived mirror until java.lang.Class is loaded. Please - // see more details in SystemDictionary::resolve_preloaded_classes(). + // see more details in SystemDictionary::resolve_well_known_classes(). if (!SystemDictionary::Class_klass_loaded()) { assert(fixup_mirror_list() != NULL, "fixup_mirror_list not initialized"); fixup_mirror_list()->push(k); @@ -4250,12 +4251,19 @@ void JavaClasses::compute_hard_coded_offsets() { // Compute non-hard-coded field offsets of all the classes in this file void JavaClasses::compute_offsets() { if (UseSharedSpaces) { - return; // field offsets are loaded from archive + assert(JvmtiExport::is_early_phase() && !(JvmtiExport::should_post_class_file_load_hook() && + JvmtiExport::has_early_class_hook_env()), + "JavaClasses::compute_offsets() must be called in early JVMTI phase."); + // None of the classes used by the rest of this function can be replaced by + // JMVTI ClassFileLoadHook. + // We are safe to use the archived offsets, which have already been restored + // by JavaClasses::serialize_offsets, without computing the offsets again. + return; } // We have already called the compute_offsets() of the // BASIC_JAVA_CLASSES_DO_PART1 classes (java_lang_String and java_lang_Class) - // earlier inside SystemDictionary::resolve_preloaded_classes() + // earlier inside SystemDictionary::resolve_well_known_classes() BASIC_JAVA_CLASSES_DO_PART2(DO_COMPUTE_OFFSETS); // generated interpreter code wants to know about the offsets we just computed: @@ -4356,7 +4364,7 @@ int InjectedField::compute_offset() { tty->print_cr(" name: %s, sig: %s, flags: %08x", fs.name()->as_C_string(), fs.signature()->as_C_string(), fs.access_flags().as_int()); } #endif //PRODUCT - vm_exit_during_initialization("Invalid layout of preloaded class: use -Xlog:class+load=info to see the origin of the problem class"); + vm_exit_during_initialization("Invalid layout of well-known class: use -Xlog:class+load=info to see the origin of the problem class"); return -1; } diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp index 05262b1f572..c01f42847eb 100644 --- a/src/hotspot/share/classfile/systemDictionary.cpp +++ b/src/hotspot/share/classfile/systemDictionary.cpp @@ -67,7 +67,7 @@ #include "oops/oop.inline.hpp" #include "oops/symbol.hpp" #include "oops/typeArrayKlass.hpp" -#include "prims/jvmtiEnvBase.hpp" +#include "prims/jvmtiExport.hpp" #include "prims/resolvedMethodTable.hpp" #include "prims/methodHandles.hpp" #include "runtime/arguments.hpp" @@ -1189,20 +1189,15 @@ InstanceKlass* SystemDictionary::find_shared_class(Symbol* class_name) { } -// Load a class from the shared spaces (found through the shared system -// dictionary). Force the superclass and all interfaces to be loaded. -// Update the class definition to include sibling classes and no -// subclasses (yet). [Classes in the shared space are not part of the -// object hierarchy until loaded.] - -InstanceKlass* SystemDictionary::load_shared_class( - Symbol* class_name, Handle class_loader, TRAPS) { +// Load a class for boot loader from the shared spaces (found through +// the shared system dictionary). Force the super class and all interfaces +// to be loaded. +InstanceKlass* SystemDictionary::load_shared_boot_class(Symbol* class_name, + TRAPS) { InstanceKlass* ik = find_shared_class(class_name); - // Make sure we only return the boot class for the NULL classloader. - if (ik != NULL && - ik->is_shared_boot_class() && class_loader.is_null()) { - Handle protection_domain; - return load_shared_class(ik, class_loader, protection_domain, THREAD); + // Make sure we only return the boot class. + if (ik != NULL && ik->is_shared_boot_class()) { + return load_shared_class(ik, Handle(), Handle(), THREAD); } return NULL; } @@ -1489,13 +1484,12 @@ InstanceKlass* SystemDictionary::load_instance_class(Symbol* class_name, Handle !search_only_bootloader_append, "Attempt to load a class outside of boot loader's module path"); - // Search the shared system dictionary for classes preloaded into the - // shared spaces. + // Search for classes in the CDS archive. InstanceKlass* k = NULL; { #if INCLUDE_CDS PerfTraceTime vmtimer(ClassLoader::perf_shared_classload_time()); - k = load_shared_class(class_name, class_loader, THREAD); + k = load_shared_boot_class(class_name, THREAD); #endif } @@ -1958,7 +1952,7 @@ void SystemDictionary::initialize(TRAPS) { // Allocate private object used as system class loader lock _system_loader_lock_obj = oopFactory::new_intArray(0, CHECK); // Initialize basic classes - resolve_preloaded_classes(CHECK); + resolve_well_known_classes(CHECK); } // Compact table of directions on the initialization of klasses: @@ -1971,6 +1965,19 @@ static const short wk_init_info[] = { 0 }; +#ifdef ASSERT +bool SystemDictionary::is_well_known_klass(Symbol* class_name) { + int sid; + for (int i = 0; (sid = wk_init_info[i]) != 0; i++) { + Symbol* symbol = vmSymbols::symbol_at((vmSymbols::SID)sid); + if (class_name == symbol) { + return true; + } + } + return false; +} +#endif + bool SystemDictionary::resolve_wk_klass(WKID id, TRAPS) { assert(id >= (int)FIRST_WKID && id < (int)WKID_LIMIT, "oob"); int sid = wk_init_info[id - FIRST_WKID]; @@ -2002,8 +2009,8 @@ void SystemDictionary::resolve_wk_klasses_until(WKID limit_id, WKID &start_id, T start_id = limit_id; } -void SystemDictionary::resolve_preloaded_classes(TRAPS) { - assert(WK_KLASS(Object_klass) == NULL, "preloaded classes should only be initialized once"); +void SystemDictionary::resolve_well_known_classes(TRAPS) { + assert(WK_KLASS(Object_klass) == NULL, "well-known classes should only be initialized once"); // Create the ModuleEntry for java.base. This call needs to be done here, // after vmSymbols::initialize() is called but before any classes are pre-loaded. @@ -2071,7 +2078,8 @@ void SystemDictionary::resolve_preloaded_classes(TRAPS) { WKID jsr292_group_end = WK_KLASS_ENUM_NAME(VolatileCallSite_klass); resolve_wk_klasses_until(jsr292_group_start, scan, CHECK); resolve_wk_klasses_through(jsr292_group_end, scan, CHECK); - resolve_wk_klasses_until(NOT_JVMCI(WKID_LIMIT) JVMCI_ONLY(FIRST_JVMCI_WKID), scan, CHECK); + WKID last = NOT_JVMCI(WKID_LIMIT) JVMCI_ONLY(FIRST_JVMCI_WKID); + resolve_wk_klasses_until(last, scan, CHECK); _box_klasses[T_BOOLEAN] = WK_KLASS(Boolean_klass); _box_klasses[T_CHAR] = WK_KLASS(Character_klass); @@ -2088,6 +2096,17 @@ void SystemDictionary::resolve_preloaded_classes(TRAPS) { Method* method = InstanceKlass::cast(ClassLoader_klass())->find_method(vmSymbols::checkPackageAccess_name(), vmSymbols::class_protectiondomain_signature()); _has_checkPackageAccess = (method != NULL); } + +#ifdef ASSERT + if (UseSharedSpaces) { + assert(JvmtiExport::is_early_phase(), + "All well known classes must be resolved in JVMTI early phase"); + for (int i = FIRST_WKID; i < last; i++) { + InstanceKlass* k = _well_known_klasses[i]; + assert(k->is_shared(), "must not be replaced by JVMTI class file load hook"); + } + } +#endif } // Tells if a given klass is a box (wrapper class, such as java.lang.Integer). diff --git a/src/hotspot/share/classfile/systemDictionary.hpp b/src/hotspot/share/classfile/systemDictionary.hpp index d34240701b1..79e28a0c53b 100644 --- a/src/hotspot/share/classfile/systemDictionary.hpp +++ b/src/hotspot/share/classfile/systemDictionary.hpp @@ -85,19 +85,20 @@ class ProtectionDomainCacheEntry; class GCTimer; class OopStorage; -// Certain classes are preloaded, such as java.lang.Object and java.lang.String. -// They are all "well-known", in the sense that no class loader is allowed -// to provide a different definition. -// -// These klasses must all have names defined in vmSymbols. - #define WK_KLASS_ENUM_NAME(kname) kname##_knum +// Certain classes, such as java.lang.Object and java.lang.String, +// are "well-known", in the sense that no class loader is allowed +// to provide a different definition. +// // Each well-known class has a short klass name (like object_klass), // and a vmSymbol name (like java_lang_Object). -// The order of these definitions is significant; it is the order in which -// preloading is actually performed by resolve_preloaded_classes. - +// +// The order of these definitions is significant: the classes are +// resolved during early VM start-up by resolve_well_known_classes +// in this order. Changing the order may require careful restructuring +// of the VM start-up sequence. +// #define WK_KLASSES_DO(do_klass) \ /* well-known classes */ \ do_klass(Object_klass, java_lang_Object ) \ @@ -127,7 +128,7 @@ class OopStorage; do_klass(IllegalMonitorStateException_klass, java_lang_IllegalMonitorStateException ) \ do_klass(Reference_klass, java_lang_ref_Reference ) \ \ - /* Preload ref klasses and set reference types */ \ + /* ref klasses and set reference types */ \ do_klass(SoftReference_klass, java_lang_ref_SoftReference ) \ do_klass(WeakReference_klass, java_lang_ref_WeakReference ) \ do_klass(FinalReference_klass, java_lang_ref_FinalReference ) \ @@ -200,7 +201,7 @@ class OopStorage; /* support for stack dump lock analysis */ \ do_klass(java_util_concurrent_locks_AbstractOwnableSynchronizer_klass, java_util_concurrent_locks_AbstractOwnableSynchronizer) \ \ - /* Preload boxing klasses */ \ + /* boxing klasses */ \ do_klass(Boolean_klass, java_lang_Boolean ) \ do_klass(Character_klass, java_lang_Character ) \ do_klass(Float_klass, java_lang_Float ) \ @@ -391,7 +392,8 @@ public: // Initialization static void initialize(TRAPS); - // Checked fast access to commonly used classes - mostly preloaded + // Checked fast access to the well-known classes -- so that you don't try to use them + // before they are resolved. static InstanceKlass* check_klass(InstanceKlass* k) { assert(k != NULL, "klass not loaded"); return k; @@ -435,6 +437,12 @@ public: return check_klass(_box_klasses[t]); } static BasicType box_klass_type(Klass* k); // inverse of box_klass +#ifdef ASSERT + static bool is_well_known_klass(Klass* k) { + return is_well_known_klass(k->name()); + } + static bool is_well_known_klass(Symbol* class_name); +#endif protected: // Returns the class loader data to be used when looking up/updating the @@ -643,6 +651,8 @@ protected: Handle class_loader, Handle protection_domain, TRAPS); + static InstanceKlass* load_shared_boot_class(Symbol* class_name, + TRAPS); static InstanceKlass* load_instance_class(Symbol* class_name, Handle class_loader, TRAPS); static Handle compute_loader_lock_object(Handle class_loader, TRAPS); static void check_loader_lock_contention(Handle loader_lock, TRAPS); @@ -650,9 +660,6 @@ protected: static bool is_parallelDefine(Handle class_loader); public: - static InstanceKlass* load_shared_class(Symbol* class_name, - Handle class_loader, - TRAPS); static bool is_system_class_loader(oop class_loader); static bool is_platform_class_loader(oop class_loader); static void clear_invoke_method_table(); @@ -695,8 +702,8 @@ protected: ClassLoaderData* loader_data, TRAPS); - // Resolve preloaded classes so they can be used like SystemDictionary::String_klass() - static void resolve_preloaded_classes(TRAPS); + // Resolve well-known classes so they can be used like SystemDictionary::String_klass() + static void resolve_well_known_classes(TRAPS); // Class loader constraints static void check_constraints(unsigned int hash, @@ -707,7 +714,6 @@ protected: InstanceKlass* k, Handle loader, TRAPS); - // Variables holding commonly used klasses (preloaded) static InstanceKlass* _well_known_klasses[]; // table of box klasses (int_klass, etc.) diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp index d4c2b1e1ac9..c853d2e5b47 100644 --- a/src/hotspot/share/code/codeCache.cpp +++ b/src/hotspot/share/code/codeCache.cpp @@ -274,10 +274,10 @@ void CodeCache::initialize_heaps() { } // Make sure we have enough space for VM internal code uint min_code_cache_size = CodeCacheMinimumUseSpace DEBUG_ONLY(* 3); - if (non_nmethod_size < (min_code_cache_size + code_buffers_size)) { + if (non_nmethod_size < min_code_cache_size) { vm_exit_during_initialization(err_msg( "Not enough space in non-nmethod code heap to run VM: " SIZE_FORMAT "K < " SIZE_FORMAT "K", - non_nmethod_size/K, (min_code_cache_size + code_buffers_size)/K)); + non_nmethod_size/K, min_code_cache_size/K)); } // Verify sizes and update flag values diff --git a/src/hotspot/share/compiler/compileBroker.cpp b/src/hotspot/share/compiler/compileBroker.cpp index 861972e5bc9..1a4829411bc 100644 --- a/src/hotspot/share/compiler/compileBroker.cpp +++ b/src/hotspot/share/compiler/compileBroker.cpp @@ -894,14 +894,19 @@ void CompileBroker::possibly_add_compiler_threads() { EXCEPTION_MARK; julong available_memory = os::available_memory(); + // If SegmentedCodeCache is off, both values refer to the single heap (with type CodeBlobType::All). + size_t available_cc_np = CodeCache::unallocated_capacity(CodeBlobType::MethodNonProfiled), + available_cc_p = CodeCache::unallocated_capacity(CodeBlobType::MethodProfiled); + // Only do attempt to start additional threads if the lock is free. if (!CompileThread_lock->try_lock()) return; if (_c2_compile_queue != NULL) { int old_c2_count = _compilers[1]->num_compiler_threads(); - int new_c2_count = MIN3(_c2_count, + int new_c2_count = MIN4(_c2_count, _c2_compile_queue->size() / 2, - (int)(available_memory / 200*M)); + (int)(available_memory / (200*M)), + (int)(available_cc_np / (128*K))); for (int i = old_c2_count; i < new_c2_count; i++) { JavaThread *ct = make_thread(compiler2_object(i), _c2_compile_queue, _compilers[1], true, CHECK); @@ -910,17 +915,18 @@ void CompileBroker::possibly_add_compiler_threads() { if (TraceCompilerThreads) { ResourceMark rm; MutexLocker mu(Threads_lock); - tty->print_cr("Added compiler thread %s (available memory: %dMB)", - ct->get_thread_name(), (int)(available_memory/M)); + tty->print_cr("Added compiler thread %s (available memory: %dMB, available non-profiled code cache: %dMB)", + ct->get_thread_name(), (int)(available_memory/M), (int)(available_cc_np/M)); } } } if (_c1_compile_queue != NULL) { int old_c1_count = _compilers[0]->num_compiler_threads(); - int new_c1_count = MIN3(_c1_count, + int new_c1_count = MIN4(_c1_count, _c1_compile_queue->size() / 4, - (int)(available_memory / 100*M)); + (int)(available_memory / (100*M)), + (int)(available_cc_p / (128*K))); for (int i = old_c1_count; i < new_c1_count; i++) { JavaThread *ct = make_thread(compiler1_object(i), _c1_compile_queue, _compilers[0], true, CHECK); @@ -929,8 +935,8 @@ void CompileBroker::possibly_add_compiler_threads() { if (TraceCompilerThreads) { ResourceMark rm; MutexLocker mu(Threads_lock); - tty->print_cr("Added compiler thread %s (available memory: %dMB)", - ct->get_thread_name(), (int)(available_memory/M)); + tty->print_cr("Added compiler thread %s (available memory: %dMB, available profiled code cache: %dMB)", + ct->get_thread_name(), (int)(available_memory/M), (int)(available_cc_p/M)); } } } @@ -1501,8 +1507,9 @@ bool CompileBroker::wait_for_jvmci_completion(JVMCICompiler* jvmci, CompileTask* bool progress; if (jvmci_compiler_thread != NULL) { - // If the JVMCI compiler thread is not blocked, we deem it to be making progress. - progress = jvmci_compiler_thread->thread_state() != _thread_blocked; + // If the JVMCI compiler thread is not blocked or suspended, we deem it to be making progress. + progress = jvmci_compiler_thread->thread_state() != _thread_blocked && + !jvmci_compiler_thread->is_external_suspend(); } else { // Still waiting on JVMCI compiler queue. This thread may be holding a lock // that all JVMCI compiler threads are blocked on. We use the counter for diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index c23980757bb..4b10e3e2fd6 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -1043,7 +1043,7 @@ void G1CollectedHeap::prepare_heap_for_mutators() { assert(num_free_regions() == 0, "we should not have added any free regions"); rebuild_region_sets(false /* free_list_only */); abort_refinement(); - resize_if_necessary_after_full_collection(); + resize_heap_if_necessary(); // Rebuild the strong code root lists for each region rebuild_strong_code_roots(); @@ -1149,7 +1149,7 @@ void G1CollectedHeap::do_full_collection(bool clear_all_soft_refs) { clear_all_soft_refs); } -void G1CollectedHeap::resize_if_necessary_after_full_collection() { +void G1CollectedHeap::resize_heap_if_necessary() { // Capacity, free and used after the GC counted as full regions to // include the waste in the following calculations. const size_t capacity_after_gc = capacity(); @@ -1206,7 +1206,7 @@ void G1CollectedHeap::resize_if_necessary_after_full_collection() { // Don't expand unless it's significant size_t expand_bytes = minimum_desired_capacity - capacity_after_gc; - log_debug(gc, ergo, heap)("Attempt heap expansion (capacity lower than min desired capacity after Full GC). " + log_debug(gc, ergo, heap)("Attempt heap expansion (capacity lower than min desired capacity). " "Capacity: " SIZE_FORMAT "B occupancy: " SIZE_FORMAT "B live: " SIZE_FORMAT "B " "min_desired_capacity: " SIZE_FORMAT "B (" UINTX_FORMAT " %%)", capacity_after_gc, used_after_gc, used(), minimum_desired_capacity, MinHeapFreeRatio); @@ -1218,7 +1218,7 @@ void G1CollectedHeap::resize_if_necessary_after_full_collection() { // Capacity too large, compute shrinking size size_t shrink_bytes = capacity_after_gc - maximum_desired_capacity; - log_debug(gc, ergo, heap)("Attempt heap shrinking (capacity higher than max desired capacity after Full GC). " + log_debug(gc, ergo, heap)("Attempt heap shrinking (capacity higher than max desired capacity). " "Capacity: " SIZE_FORMAT "B occupancy: " SIZE_FORMAT "B live: " SIZE_FORMAT "B " "maximum_desired_capacity: " SIZE_FORMAT "B (" UINTX_FORMAT " %%)", capacity_after_gc, used_after_gc, used(), maximum_desired_capacity, MaxHeapFreeRatio); @@ -1394,8 +1394,8 @@ void G1CollectedHeap::shrink_helper(size_t shrink_bytes) { void G1CollectedHeap::shrink(size_t shrink_bytes) { _verifier->verify_region_sets_optional(); - // We should only reach here at the end of a Full GC which means we - // should not not be holding to any GC alloc regions. The method + // We should only reach here at the end of a Full GC or during Remark which + // means we should not not be holding to any GC alloc regions. The method // below will make sure of that and do any remaining clean up. _allocator->abandon_gc_alloc_regions(); @@ -4399,13 +4399,13 @@ public: } bool do_heap_region(HeapRegion* r) { - // After full GC, no region should have a remembered set. - r->rem_set()->clear(true); if (r->is_empty()) { + assert(r->rem_set()->is_empty(), "Empty regions should have empty remembered sets."); // Add free regions to the free list r->set_free(); _hrm->insert_into_free_list(r); } else if (!_free_list_only) { + assert(r->rem_set()->is_empty(), "At this point remembered sets must have been cleared."); if (r->is_archive() || r->is_humongous()) { // We ignore archive and humongous regions. We left these sets unchanged. @@ -4443,10 +4443,9 @@ void G1CollectedHeap::rebuild_region_sets(bool free_list_only) { _archive_allocator->clear_used(); } } - assert(used_unlocked() == recalculate_used(), - "inconsistent used_unlocked(), " - "value: " SIZE_FORMAT " recalculated: " SIZE_FORMAT, - used_unlocked(), recalculate_used()); + assert(used() == recalculate_used(), + "inconsistent used(), value: " SIZE_FORMAT " recalculated: " SIZE_FORMAT, + used(), recalculate_used()); } bool G1CollectedHeap::is_in_closed_subset(const void* p) const { diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index d64713fb3d7..9d9f72c293a 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -468,9 +468,6 @@ private: // Callback from VM_G1CollectFull operation, or collect_as_vm_thread. virtual void do_full_collection(bool clear_all_soft_refs); - // Resize the heap if necessary after a full collection. - void resize_if_necessary_after_full_collection(); - // Callback from VM_G1CollectForAllocation operation. // This function does everything necessary/possible to satisfy a // failed allocation request (including collection, expansion, etc.) @@ -528,6 +525,8 @@ public: return _g1mm; } + void resize_heap_if_necessary(); + // Expand the garbage-first heap by at least the given size (in bytes!). // Returns true if the heap was expanded by the requested amount; // false otherwise. @@ -1120,6 +1119,7 @@ public: // Return the region with the given index. It assumes the index is valid. inline HeapRegion* region_at(uint index) const; + inline HeapRegion* region_at_or_null(uint index) const; // Return the next region (by index) that is part of the same // humongous object that hr is part of. @@ -1157,6 +1157,11 @@ public: template 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 + inline HeapRegion* heap_region_containing_or_null(const T addr) const; + // A CollectedHeap is divided into a dense sequence of "blocks"; that is, // each address in the (reserved) heap is a member of exactly // one block. The defining characteristic of a block is that it is diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp index f8e5887ef6b..2bd0c2141f8 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp @@ -60,6 +60,9 @@ size_t G1CollectedHeap::desired_plab_sz(InCSetState dest) { // Return the region with the given index. It assumes the index is valid. inline HeapRegion* G1CollectedHeap::region_at(uint index) const { return _hrm.at(index); } +// Return the region with the given index, or NULL if unmapped. It assumes the index is valid. +inline HeapRegion* G1CollectedHeap::region_at_or_null(uint index) const { return _hrm.at_or_null(index); } + inline HeapRegion* G1CollectedHeap::next_region_in_humongous(HeapRegion* hr) const { return _hrm.next_region_in_humongous(hr); } @@ -84,6 +87,16 @@ inline HeapRegion* G1CollectedHeap::heap_region_containing(const T addr) const { return _hrm.addr_to_region((HeapWord*) addr); } +template +inline HeapRegion* G1CollectedHeap::heap_region_containing_or_null(const T addr) const { + assert(addr != NULL, "invariant"); + assert(is_in_g1_reserved((const void*) addr), + "Address " PTR_FORMAT " is outside of the heap ranging from [" PTR_FORMAT " to " PTR_FORMAT ")", + p2i((void*)addr), p2i(g1_reserved().start()), p2i(g1_reserved().end())); + uint const region_idx = addr_to_region(addr); + return region_at_or_null(region_idx); +} + inline void G1CollectedHeap::old_set_add(HeapRegion* hr) { _old_set.add(hr); } diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index 71f503521e5..f400fb7152e 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -1178,6 +1178,8 @@ void G1ConcurrentMark::remark() { ClassLoaderDataGraph::purge(); } + _g1h->resize_heap_if_necessary(); + compute_new_sizes(); verify_during_pause(G1HeapVerifier::G1VerifyRemark, VerifyOption_G1UsePrevMarking, "Remark after"); diff --git a/src/hotspot/share/gc/g1/g1OopClosures.cpp b/src/hotspot/share/gc/g1/g1OopClosures.cpp index 3724153f49d..3533da369d6 100644 --- a/src/hotspot/share/gc/g1/g1OopClosures.cpp +++ b/src/hotspot/share/gc/g1/g1OopClosures.cpp @@ -38,7 +38,7 @@ G1ParCopyHelper::G1ParCopyHelper(G1CollectedHeap* g1h, G1ParScanThreadState* pa { } G1ScanClosureBase::G1ScanClosureBase(G1CollectedHeap* g1h, G1ParScanThreadState* par_scan_state) : - _g1h(g1h), _par_scan_state(par_scan_state), _from(NULL) + _g1h(g1h), _par_scan_state(par_scan_state) { } void G1CLDScanClosure::do_cld(ClassLoaderData* cld) { diff --git a/src/hotspot/share/gc/g1/g1OopClosures.hpp b/src/hotspot/share/gc/g1/g1OopClosures.hpp index a15f870e6ec..90fc41bafb7 100644 --- a/src/hotspot/share/gc/g1/g1OopClosures.hpp +++ b/src/hotspot/share/gc/g1/g1OopClosures.hpp @@ -36,6 +36,7 @@ class G1ConcurrentMark; class DirtyCardToOopClosure; class G1CMBitMap; class G1ParScanThreadState; +class G1ScanEvacuatedObjClosure; class G1CMTask; class ReferenceProcessor; @@ -43,7 +44,6 @@ class G1ScanClosureBase : public BasicOopIterateClosure { protected: G1CollectedHeap* _g1h; G1ParScanThreadState* _par_scan_state; - HeapRegion* _from; G1ScanClosureBase(G1CollectedHeap* g1h, G1ParScanThreadState* par_scan_state); ~G1ScanClosureBase() { } @@ -56,24 +56,19 @@ protected: public: virtual ReferenceIterationMode reference_iteration_mode() { return DO_FIELDS; } - void set_region(HeapRegion* from) { _from = from; } - inline void trim_queue_partially(); }; // Used during the Update RS phase to refine remaining cards in the DCQ during garbage collection. -class G1ScanObjsDuringUpdateRSClosure: public G1ScanClosureBase { - uint _worker_i; - +class G1ScanObjsDuringUpdateRSClosure : public G1ScanClosureBase { public: G1ScanObjsDuringUpdateRSClosure(G1CollectedHeap* g1h, - G1ParScanThreadState* pss, - uint worker_i) : - G1ScanClosureBase(g1h, pss), _worker_i(worker_i) { } + G1ParScanThreadState* pss) : + G1ScanClosureBase(g1h, pss) { } template void do_oop_work(T* p); virtual void do_oop(narrowOop* p) { do_oop_work(p); } - virtual void do_oop(oop* p) { do_oop_work(p); } + virtual void do_oop(oop* p) { do_oop_work(p); } }; // Used during the Scan RS phase to scan cards from the remembered set during garbage collection. @@ -88,11 +83,22 @@ public: virtual void do_oop(narrowOop* p) { do_oop_work(p); } }; + // This closure is applied to the fields of the objects that have just been copied during evacuation. class G1ScanEvacuatedObjClosure : public G1ScanClosureBase { + friend class G1ScanInYoungSetter; + + enum ScanningInYoungValues { + False = 0, + True, + Uninitialized + }; + + ScanningInYoungValues _scanning_in_young; + public: G1ScanEvacuatedObjClosure(G1CollectedHeap* g1h, G1ParScanThreadState* par_scan_state) : - G1ScanClosureBase(g1h, par_scan_state) { } + G1ScanClosureBase(g1h, par_scan_state), _scanning_in_young(Uninitialized) { } template void do_oop_work(T* p); virtual void do_oop(oop* p) { do_oop_work(p); } @@ -106,6 +112,21 @@ public: } }; +// RAII object to properly set the _scanning_in_young field in G1ScanEvacuatedObjClosure. +class G1ScanInYoungSetter : public StackObj { + G1ScanEvacuatedObjClosure* _closure; + +public: + G1ScanInYoungSetter(G1ScanEvacuatedObjClosure* closure, bool new_value) : _closure(closure) { + assert(_closure->_scanning_in_young == G1ScanEvacuatedObjClosure::Uninitialized, "Must not be set"); + _closure->_scanning_in_young = new_value ? G1ScanEvacuatedObjClosure::True : G1ScanEvacuatedObjClosure::False; + } + + ~G1ScanInYoungSetter() { + DEBUG_ONLY(_closure->_scanning_in_young = G1ScanEvacuatedObjClosure::Uninitialized;) + } +}; + // Add back base class for metadata class G1ParCopyHelper : public OopClosure { protected: diff --git a/src/hotspot/share/gc/g1/g1OopClosures.inline.hpp b/src/hotspot/share/gc/g1/g1OopClosures.inline.hpp index c2549132350..074a6bf7486 100644 --- a/src/hotspot/share/gc/g1/g1OopClosures.inline.hpp +++ b/src/hotspot/share/gc/g1/g1OopClosures.inline.hpp @@ -82,12 +82,13 @@ inline void G1ScanEvacuatedObjClosure::do_oop_work(T* p) { const InCSetState state = _g1h->in_cset_state(obj); if (state.is_in_cset()) { prefetch_and_push(p, obj); - } else { - if (HeapRegion::is_in_same_region(p, obj)) { + } else if (!HeapRegion::is_in_same_region(p, obj)) { + handle_non_cset_obj_common(state, p, obj); + assert(_scanning_in_young != Uninitialized, "Scan location has not been initialized."); + if (_scanning_in_young == True) { return; } - handle_non_cset_obj_common(state, p, obj); - _par_scan_state->update_rs(_from, p, obj); + _par_scan_state->enqueue_card_if_tracked(p, obj); } } @@ -172,13 +173,9 @@ inline void G1ScanObjsDuringUpdateRSClosure::do_oop_work(T* p) { // Since the source is always from outside the collection set, here we implicitly know // that this is a cross-region reference too. prefetch_and_push(p, obj); - } else { - HeapRegion* to = _g1h->heap_region_containing(obj); - if (_from == to) { - return; - } + } else if (!HeapRegion::is_in_same_region(p, obj)) { handle_non_cset_obj_common(state, p, obj); - to->rem_set()->add_reference(p, _worker_i); + _par_scan_state->enqueue_card_if_tracked(p, obj); } } @@ -193,10 +190,7 @@ inline void G1ScanObjsDuringScanRSClosure::do_oop_work(T* p) { const InCSetState state = _g1h->in_cset_state(obj); if (state.is_in_cset()) { prefetch_and_push(p, obj); - } else { - if (HeapRegion::is_in_same_region(p, obj)) { - return; - } + } else if (!HeapRegion::is_in_same_region(p, obj)) { handle_non_cset_obj_common(state, p, obj); } } diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp index fb1d8715fc8..e3fe0aae1ef 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp @@ -311,8 +311,7 @@ oop G1ParScanThreadState::copy_to_survivor_space(InCSetState const state, oop* old_p = set_partial_array_mask(old); do_oop_partial_array(old_p); } else { - HeapRegion* const to_region = _g1h->heap_region_containing(obj_ptr); - _scanner.set_region(to_region); + G1ScanInYoungSetter x(&_scanner, dest_state.is_young()); obj->oop_iterate_backwards(&_scanner); } return obj; @@ -367,7 +366,7 @@ oop G1ParScanThreadState::handle_evacuation_failure_par(oop old, markOop m) { _g1h->preserve_mark_during_evac_failure(_worker_id, old, m); - _scanner.set_region(r); + G1ScanInYoungSetter x(&_scanner, r->is_young()); old->oop_iterate_backwards(&_scanner); return old; diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp index ac1769a073c..367cdbdfb6a 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp @@ -104,17 +104,16 @@ public: template void do_oop_ext(T* ref); template void push_on_queue(T* ref); - template void update_rs(HeapRegion* from, T* p, oop o) { - assert(!HeapRegion::is_in_same_region(p, o), "Caller should have filtered out cross-region references already."); - // If the field originates from the to-space, we don't need to include it - // in the remembered set updates. Also, if we are not tracking the remembered - // set in the destination region, do not bother either. - if (!from->is_young() && _g1h->heap_region_containing((HeapWord*)o)->rem_set()->is_tracked()) { - size_t card_index = ct()->index_for(p); - // If the card hasn't been added to the buffer, do it. - if (ct()->mark_card_deferred(card_index)) { - dirty_card_queue().enqueue((jbyte*)ct()->byte_for_index(card_index)); - } + template void enqueue_card_if_tracked(T* p, oop o) { + assert(!HeapRegion::is_in_same_region(p, o), "Should have filtered out cross-region references already."); + assert(!_g1h->heap_region_containing(p)->is_young(), "Should have filtered out from-young references already."); + if (!_g1h->heap_region_containing((HeapWord*)o)->rem_set()->is_tracked()) { + return; + } + size_t card_index = ct()->index_for(p); + // If the card hasn't been added to the buffer, do it. + if (ct()->mark_card_deferred(card_index)) { + dirty_card_queue().enqueue((jbyte*)ct()->byte_for_index(card_index)); } } diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp index 0d4d7fb5a07..02d3dd70b9c 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp @@ -61,9 +61,12 @@ template void G1ParScanThreadState::do_oop_evac(T* p) { RawAccess::oop_store(p, obj); assert(obj != NULL, "Must be"); - if (!HeapRegion::is_in_same_region(p, obj)) { - HeapRegion* from = _g1h->heap_region_containing(p); - update_rs(from, p, obj); + if (HeapRegion::is_in_same_region(p, obj)) { + return; + } + HeapRegion* from = _g1h->heap_region_containing(p); + if (!from->is_young()) { + enqueue_card_if_tracked(p, obj); } } @@ -109,7 +112,9 @@ inline void G1ParScanThreadState::do_oop_partial_array(oop* p) { // so that the heap remains parsable in case of evacuation failure. to_obj_array->set_length(end); } - _scanner.set_region(_g1h->heap_region_containing(to_obj)); + + HeapRegion* hr = _g1h->heap_region_containing(to_obj); + G1ScanInYoungSetter x(&_scanner, hr->is_young()); // Process indexes [start,end). It will also process the header // along with the first chunk (i.e., the chunk with start == 0). // Note that at this point the length field of to_obj_array is not diff --git a/src/hotspot/share/gc/g1/g1RemSet.cpp b/src/hotspot/share/gc/g1/g1RemSet.cpp index 72e07c30d75..ca01fe22744 100644 --- a/src/hotspot/share/gc/g1/g1RemSet.cpp +++ b/src/hotspot/share/gc/g1/g1RemSet.cpp @@ -133,10 +133,10 @@ private: virtual bool do_heap_region(HeapRegion* r) { uint hrm_index = r->hrm_index(); - if (!r->in_collection_set() && r->is_old_or_humongous_or_archive()) { + if (!r->in_collection_set() && r->is_old_or_humongous_or_archive() && !r->is_empty()) { _scan_top[hrm_index] = r->top(); } else { - _scan_top[hrm_index] = r->bottom(); + _scan_top[hrm_index] = NULL; } return false; } @@ -191,6 +191,7 @@ public: void reset() { for (uint i = 0; i < _max_regions; i++) { _iter_states[i] = Unclaimed; + _scan_top[i] = NULL; } G1ResetScanTopClosure cl(_scan_top); @@ -333,7 +334,7 @@ void G1ScanRSForRegionClosure::claim_card(size_t card_index, const uint region_i void G1ScanRSForRegionClosure::scan_card(MemRegion mr, uint region_idx_for_card) { HeapRegion* const card_region = _g1h->region_at(region_idx_for_card); - _scan_objs_on_card_cl->set_region(card_region); + assert(!card_region->is_young(), "Should not scan card in young region %u", region_idx_for_card); card_region->oops_on_card_seq_iterate_careful(mr, _scan_objs_on_card_cl); _scan_objs_on_card_cl->trim_queue_partially(); _cards_scanned++; @@ -350,6 +351,10 @@ void G1ScanRSForRegionClosure::scan_rem_set_roots(HeapRegion* r) { _scan_state->add_dirty_region(region_idx); } + if (r->rem_set()->cardset_is_empty()) { + return; + } + // We claim cards in blocks so as to reduce the contention. size_t const block_size = G1RSetScanBlockSize; @@ -367,18 +372,21 @@ void G1ScanRSForRegionClosure::scan_rem_set_roots(HeapRegion* r) { } _cards_claimed++; - // If the card is dirty, then G1 will scan it during Update RS. - if (_ct->is_card_claimed(card_index) || _ct->is_card_dirty(card_index)) { + HeapWord* const card_start = _g1h->bot()->address_for_index_raw(card_index); + uint const region_idx_for_card = _g1h->addr_to_region(card_start); + +#ifdef ASSERT + HeapRegion* hr = _g1h->region_at_or_null(region_idx_for_card); + assert(hr == NULL || hr->is_in_reserved(card_start), + "Card start " PTR_FORMAT " to scan outside of region %u", p2i(card_start), _g1h->region_at(region_idx_for_card)->hrm_index()); +#endif + HeapWord* const top = _scan_state->scan_top(region_idx_for_card); + if (card_start >= top) { continue; } - HeapWord* const card_start = _g1h->bot()->address_for_index(card_index); - uint const region_idx_for_card = _g1h->addr_to_region(card_start); - - assert(_g1h->region_at(region_idx_for_card)->is_in_reserved(card_start), - "Card start " PTR_FORMAT " to scan outside of region %u", p2i(card_start), _g1h->region_at(region_idx_for_card)->hrm_index()); - HeapWord* const top = _scan_state->scan_top(region_idx_for_card); - if (card_start >= top) { + // If the card is dirty, then G1 will scan it during Update RS. + if (_ct->is_card_claimed(card_index) || _ct->is_card_dirty(card_index)) { continue; } @@ -486,7 +494,7 @@ void G1RemSet::update_rem_set(G1ParScanThreadState* pss, uint worker_i) { if (G1HotCardCache::default_use_cache()) { G1EvacPhaseTimesTracker x(p, pss, G1GCPhaseTimes::ScanHCC, worker_i); - G1ScanObjsDuringUpdateRSClosure scan_hcc_cl(_g1h, pss, worker_i); + G1ScanObjsDuringUpdateRSClosure scan_hcc_cl(_g1h, pss); G1RefineCardClosure refine_card_cl(_g1h, &scan_hcc_cl); _g1h->iterate_hcc_closure(&refine_card_cl, worker_i); } @@ -495,7 +503,7 @@ void G1RemSet::update_rem_set(G1ParScanThreadState* pss, uint worker_i) { { G1EvacPhaseTimesTracker x(p, pss, G1GCPhaseTimes::UpdateRS, worker_i); - G1ScanObjsDuringUpdateRSClosure update_rs_cl(_g1h, pss, worker_i); + G1ScanObjsDuringUpdateRSClosure update_rs_cl(_g1h, pss); G1RefineCardClosure refine_card_cl(_g1h, &update_rs_cl); _g1h->iterate_dirty_card_closure(&refine_card_cl, worker_i); @@ -545,6 +553,16 @@ void G1RemSet::refine_card_concurrently(jbyte* card_ptr, uint worker_i) { assert(!_g1h->is_gc_active(), "Only call concurrently"); + // Construct the region representing the card. + HeapWord* start = _ct->addr_for(card_ptr); + // And find the region containing it. + HeapRegion* r = _g1h->heap_region_containing_or_null(start); + + // If this is a (stale) card into an uncommitted region, exit. + if (r == NULL) { + return; + } + check_card_ptr(card_ptr, _ct); // If the card is no longer dirty, nothing to do. @@ -552,11 +570,6 @@ void G1RemSet::refine_card_concurrently(jbyte* card_ptr, return; } - // Construct the region representing the card. - HeapWord* start = _ct->addr_for(card_ptr); - // And find the region containing it. - HeapRegion* r = _g1h->heap_region_containing(start); - // This check is needed for some uncommon cases where we should // ignore the card. // @@ -679,6 +692,18 @@ bool G1RemSet::refine_card_during_gc(jbyte* card_ptr, G1ScanObjsDuringUpdateRSClosure* update_rs_cl) { assert(_g1h->is_gc_active(), "Only call during GC"); + // Construct the region representing the card. + HeapWord* card_start = _ct->addr_for(card_ptr); + // And find the region containing it. + uint const card_region_idx = _g1h->addr_to_region(card_start); + + HeapWord* scan_limit = _scan_state->scan_top(card_region_idx); + if (scan_limit == NULL) { + // This is a card into an uncommitted region. We need to bail out early as we + // should not access the corresponding card table entry. + return false; + } + check_card_ptr(card_ptr, _ct); // If the card is no longer dirty, nothing to do. This covers cards that were already @@ -691,13 +716,7 @@ bool G1RemSet::refine_card_during_gc(jbyte* card_ptr, // number of potential duplicate scans (multiple threads may enqueue the same card twice). *card_ptr = G1CardTable::clean_card_val() | G1CardTable::claimed_card_val(); - // Construct the region representing the card. - HeapWord* card_start = _ct->addr_for(card_ptr); - // And find the region containing it. - uint const card_region_idx = _g1h->addr_to_region(card_start); - _scan_state->add_dirty_region(card_region_idx); - HeapWord* scan_limit = _scan_state->scan_top(card_region_idx); if (scan_limit <= card_start) { // If the card starts above the area in the region containing objects to scan, skip it. return false; @@ -710,7 +729,7 @@ bool G1RemSet::refine_card_during_gc(jbyte* card_ptr, assert(!dirty_region.is_empty(), "sanity"); HeapRegion* const card_region = _g1h->region_at(card_region_idx); - update_rs_cl->set_region(card_region); + assert(!card_region->is_young(), "Should not scan card in young region %u", card_region_idx); bool card_processed = card_region->oops_on_card_seq_iterate_careful(dirty_region, update_rs_cl); assert(card_processed, "must be"); return true; diff --git a/src/hotspot/share/gc/g1/heapRegionManager.hpp b/src/hotspot/share/gc/g1/heapRegionManager.hpp index ecbfa0cd9c1..90578ac1703 100644 --- a/src/hotspot/share/gc/g1/heapRegionManager.hpp +++ b/src/hotspot/share/gc/g1/heapRegionManager.hpp @@ -123,10 +123,7 @@ class HeapRegionManager: public CHeapObj { public: bool is_free(HeapRegion* hr) const; #endif - // Returns whether the given region is available for allocation. - bool is_available(uint region) const; - - public: +public: // Empty constructor, we'll initialize it with the initialize() method. HeapRegionManager(); @@ -147,6 +144,13 @@ public: // is valid. inline HeapRegion* at(uint index) const; + // Return the HeapRegion at the given index, NULL if the index + // is for an unavailable region. + inline HeapRegion* at_or_null(uint index) const; + + // Returns whether the given region is available for allocation. + bool is_available(uint region) const; + // Return the next region (by index) that is part of the same // humongous object that hr is part of. inline HeapRegion* next_region_in_humongous(HeapRegion* hr) const; diff --git a/src/hotspot/share/gc/g1/heapRegionManager.inline.hpp b/src/hotspot/share/gc/g1/heapRegionManager.inline.hpp index 67b31149b12..de6e85a4225 100644 --- a/src/hotspot/share/gc/g1/heapRegionManager.inline.hpp +++ b/src/hotspot/share/gc/g1/heapRegionManager.inline.hpp @@ -47,6 +47,16 @@ inline HeapRegion* HeapRegionManager::at(uint index) const { return hr; } +inline HeapRegion* HeapRegionManager::at_or_null(uint index) const { + if (!is_available(index)) { + return NULL; + } + HeapRegion* hr = _regions.get_by_index(index); + assert(hr != NULL, "All available regions must have a HeapRegion but index %u has not.", index); + assert(hr->hrm_index() == index, "sanity"); + return hr; +} + inline HeapRegion* HeapRegionManager::next_region_in_humongous(HeapRegion* hr) const { uint index = hr->hrm_index(); assert(is_available(index), "pre-condition"); diff --git a/src/hotspot/share/gc/g1/heapRegionRemSet.cpp b/src/hotspot/share/gc/g1/heapRegionRemSet.cpp index 88490bf4af6..5ce1d31f0c6 100644 --- a/src/hotspot/share/gc/g1/heapRegionRemSet.cpp +++ b/src/hotspot/share/gc/g1/heapRegionRemSet.cpp @@ -239,10 +239,9 @@ size_t OtherRegionsTable::_mod_max_fine_entries_mask = 0; size_t OtherRegionsTable::_fine_eviction_stride = 0; size_t OtherRegionsTable::_fine_eviction_sample_size = 0; -OtherRegionsTable::OtherRegionsTable(HeapRegion* hr, Mutex* m) : +OtherRegionsTable::OtherRegionsTable(Mutex* m) : _g1h(G1CollectedHeap::heap()), _m(m), - _hr(hr), _coarse_map(G1CollectedHeap::heap()->max_regions(), mtGC), _n_coarse_entries(0), _fine_grain_regions(NULL), @@ -250,7 +249,7 @@ OtherRegionsTable::OtherRegionsTable(HeapRegion* hr, Mutex* m) : _first_all_fine_prts(NULL), _last_all_fine_prts(NULL), _fine_eviction_start(0), - _sparse_table(hr) + _sparse_table() { typedef PerRegionTable* PerRegionTablePtr; @@ -348,15 +347,6 @@ CardIdx_t OtherRegionsTable::card_within_region(OopOrNarrowOopStar within_region } void OtherRegionsTable::add_reference(OopOrNarrowOopStar from, uint tid) { - uint cur_hrm_ind = _hr->hrm_index(); - - uintptr_t from_card = uintptr_t(from) >> CardTable::card_shift; - - if (G1FromCardCache::contains_or_replace(tid, cur_hrm_ind, from_card)) { - assert(contains_reference(from), "We just found " PTR_FORMAT " in the FromCardCache", p2i(from)); - return; - } - // Note that this may be a continued H region. HeapRegion* from_hr = _g1h->heap_region_containing(from); RegionIdx_t from_hrm_ind = (RegionIdx_t) from_hr->hrm_index(); @@ -569,10 +559,6 @@ size_t OtherRegionsTable::fl_mem_size() { return PerRegionTable::fl_mem_size(); } -void OtherRegionsTable::clear_fcc() { - G1FromCardCache::clear(_hr->hrm_index()); -} - void OtherRegionsTable::clear() { // if there are no entries, skip this step if (_first_all_fine_prts != NULL) { @@ -590,8 +576,6 @@ void OtherRegionsTable::clear() { } _n_fine_entries = 0; _n_coarse_entries = 0; - - clear_fcc(); } bool OtherRegionsTable::contains_reference(OopOrNarrowOopStar from) const { @@ -627,11 +611,16 @@ HeapRegionRemSet::HeapRegionRemSet(G1BlockOffsetTable* bot, : _bot(bot), _code_roots(), _m(Mutex::leaf, FormatBuffer<128>("HeapRegionRemSet lock #%u", hr->hrm_index()), true, Monitor::_safepoint_check_never), - _other_regions(hr, &_m), + _other_regions(&_m), + _hr(hr), _state(Untracked) { } +void HeapRegionRemSet::clear_fcc() { + G1FromCardCache::clear(_hr->hrm_index()); +} + void HeapRegionRemSet::setup_remset_size() { // Setup sparse and fine-grain tables sizes. // table_size = base * (log(region_size / 1M) + 1) @@ -659,6 +648,7 @@ void HeapRegionRemSet::clear_locked(bool only_cardset) { if (!only_cardset) { _code_roots.clear(); } + clear_fcc(); _other_regions.clear(); set_state_empty(); assert(occupied_locked() == 0, "Should be clear."); @@ -751,7 +741,7 @@ bool HeapRegionRemSetIterator::coarse_has_next(size_t& card_index) { _coarse_cur_region_cur_card = 0; HeapWord* r_bot = _g1h->region_at((uint) _coarse_cur_region_index)->bottom(); - _cur_region_card_offset = _bot->index_for(r_bot); + _cur_region_card_offset = _bot->index_for_raw(r_bot); } else { return false; } @@ -792,7 +782,7 @@ void HeapRegionRemSetIterator::switch_to_prt(PerRegionTable* prt) { _fine_cur_prt = prt; HeapWord* r_bot = _fine_cur_prt->hr()->bottom(); - _cur_region_card_offset = _bot->index_for(r_bot); + _cur_region_card_offset = _bot->index_for_raw(r_bot); // The bitmap scan for the PRT always scans from _cur_region_cur_card + 1. // To avoid special-casing this start case, and not miss the first bitmap diff --git a/src/hotspot/share/gc/g1/heapRegionRemSet.hpp b/src/hotspot/share/gc/g1/heapRegionRemSet.hpp index 4659fc7aa4a..74532d3fab4 100644 --- a/src/hotspot/share/gc/g1/heapRegionRemSet.hpp +++ b/src/hotspot/share/gc/g1/heapRegionRemSet.hpp @@ -76,7 +76,6 @@ class OtherRegionsTable { G1CollectedHeap* _g1h; Mutex* _m; - HeapRegion* _hr; // These are protected by "_m". CHeapBitMap _coarse_map; @@ -124,11 +123,8 @@ class OtherRegionsTable { bool contains_reference_locked(OopOrNarrowOopStar from) const; public: - // Clear the from_card_cache entries for this region. - void clear_fcc(); - // Create a new remembered set for the given heap region. The given mutex should - // be used to ensure consistency. - OtherRegionsTable(HeapRegion* hr, Mutex* m); + // Create a new remembered set. The given mutex is used to ensure consistency. + OtherRegionsTable(Mutex* m); // Returns the card index of the given within_region pointer relative to the bottom // of the given heap region. @@ -182,13 +178,21 @@ private: OtherRegionsTable _other_regions; + HeapRegion* _hr; + + void clear_fcc(); + public: HeapRegionRemSet(G1BlockOffsetTable* bot, HeapRegion* hr); static void setup_remset_size(); + bool cardset_is_empty() const { + return _other_regions.is_empty(); + } + bool is_empty() const { - return (strong_code_roots_list_length() == 0) && _other_regions.is_empty(); + return (strong_code_roots_list_length() == 0) && cardset_is_empty(); } bool occupancy_less_or_equal_than(size_t occ) const { @@ -239,18 +243,18 @@ public: if (_state == Untracked) { return; } - _other_regions.clear_fcc(); + clear_fcc(); _state = Untracked; } void set_state_updating() { guarantee(SafepointSynchronize::is_at_safepoint() && !is_tracked(), "Should only set to Updating from Untracked during safepoint but is %s", get_state_str()); - _other_regions.clear_fcc(); + clear_fcc(); _state = Updating; } void set_state_complete() { - _other_regions.clear_fcc(); + clear_fcc(); _state = Complete; } @@ -265,6 +269,15 @@ public: if (state == Untracked) { return; } + + uint cur_idx = _hr->hrm_index(); + uintptr_t from_card = uintptr_t(from) >> CardTable::card_shift; + + if (G1FromCardCache::contains_or_replace(tid, cur_idx, from_card)) { + assert(contains_reference(from), "We just found " PTR_FORMAT " in the FromCardCache", p2i(from)); + return; + } + _other_regions.add_reference(from, tid); } @@ -353,7 +366,7 @@ public: }; class HeapRegionRemSetIterator : public StackObj { - private: +private: // The region RSet over which we are iterating. HeapRegionRemSet* _hrrs; @@ -401,7 +414,7 @@ class HeapRegionRemSetIterator : public StackObj { // The Sparse remembered set iterator. SparsePRTIter _sparse_iter; - public: +public: HeapRegionRemSetIterator(HeapRegionRemSet* hrrs); // If there remains one or more cards to be yielded, returns true and diff --git a/src/hotspot/share/gc/g1/sparsePRT.cpp b/src/hotspot/share/gc/g1/sparsePRT.cpp index f4d1ce09afe..9bb3070f21d 100644 --- a/src/hotspot/share/gc/g1/sparsePRT.cpp +++ b/src/hotspot/share/gc/g1/sparsePRT.cpp @@ -361,8 +361,8 @@ void SparsePRT::cleanup_all() { } -SparsePRT::SparsePRT(HeapRegion* hr) : - _hr(hr), _expanded(false), _next_expanded(NULL) +SparsePRT::SparsePRT() : + _expanded(false), _next_expanded(NULL) { _cur = new RSHashTable(InitialCapacity); _next = _cur; diff --git a/src/hotspot/share/gc/g1/sparsePRT.hpp b/src/hotspot/share/gc/g1/sparsePRT.hpp index 4e94f9b9b94..6797e708cb1 100644 --- a/src/hotspot/share/gc/g1/sparsePRT.hpp +++ b/src/hotspot/share/gc/g1/sparsePRT.hpp @@ -231,8 +231,6 @@ class SparsePRT { RSHashTable* _cur; RSHashTable* _next; - HeapRegion* _hr; - enum SomeAdditionalPrivateConstants { InitialCapacity = 16 }; @@ -254,7 +252,7 @@ class SparsePRT { static SparsePRT* volatile _head_expanded_list; public: - SparsePRT(HeapRegion* hr); + SparsePRT(); ~SparsePRT(); diff --git a/src/hotspot/share/gc/shared/gcTimer.cpp b/src/hotspot/share/gc/shared/gcTimer.cpp index 229812a0a63..871a3e48bbc 100644 --- a/src/hotspot/share/gc/shared/gcTimer.cpp +++ b/src/hotspot/share/gc/shared/gcTimer.cpp @@ -182,214 +182,3 @@ GCPhase* TimePartitionPhasesIterator::next() { assert(has_next(), "Must have phases left"); return _time_partitions->phase_at(_next++); } - - -/////////////// Unit tests /////////////// - -#ifndef PRODUCT - -class TimePartitionPhasesIteratorTest { - public: - static void all() { - one_pause(); - two_pauses(); - one_sub_pause_phase(); - many_sub_pause_phases(); - many_sub_pause_phases2(); - max_nested_pause_phases(); - one_concurrent(); - } - - static void validate_gc_phase(GCPhase* phase, int level, const char* name, const Ticks& start, const Ticks& end) { - assert(phase->level() == level, "Incorrect level"); - assert(strcmp(phase->name(), name) == 0, "Incorrect name"); - assert(phase->start() == start, "Incorrect start"); - assert(phase->end() == end, "Incorrect end"); - } - - static void one_pause() { - TimePartitions time_partitions; - time_partitions.report_gc_phase_start("PausePhase", 2); - time_partitions.report_gc_phase_end(8); - - TimePartitionPhasesIterator iter(&time_partitions); - - validate_gc_phase(iter.next(), 0, "PausePhase", 2, 8); - assert(time_partitions.sum_of_pauses() == Ticks(8) - Ticks(2), "Incorrect"); - assert(time_partitions.longest_pause() == Ticks(8) - Ticks(2), "Incorrect"); - - assert(!iter.has_next(), "Too many elements"); - } - - static void two_pauses() { - TimePartitions time_partitions; - time_partitions.report_gc_phase_start("PausePhase1", 2); - time_partitions.report_gc_phase_end(3); - time_partitions.report_gc_phase_start("PausePhase2", 4); - time_partitions.report_gc_phase_end(6); - - TimePartitionPhasesIterator iter(&time_partitions); - - validate_gc_phase(iter.next(), 0, "PausePhase1", 2, 3); - validate_gc_phase(iter.next(), 0, "PausePhase2", 4, 6); - - assert(time_partitions.sum_of_pauses() == Ticks(3) - Ticks(0), "Incorrect"); - assert(time_partitions.longest_pause() == Ticks(2) - Ticks(0), "Incorrect"); - - assert(!iter.has_next(), "Too many elements"); - } - - static void one_sub_pause_phase() { - TimePartitions time_partitions; - time_partitions.report_gc_phase_start("PausePhase", 2); - time_partitions.report_gc_phase_start("SubPhase", 3); - time_partitions.report_gc_phase_end(4); - time_partitions.report_gc_phase_end(5); - - TimePartitionPhasesIterator iter(&time_partitions); - - validate_gc_phase(iter.next(), 0, "PausePhase", 2, 5); - validate_gc_phase(iter.next(), 1, "SubPhase", 3, 4); - - assert(time_partitions.sum_of_pauses() == Ticks(3) - Ticks(0), "Incorrect"); - assert(time_partitions.longest_pause() == Ticks(3) - Ticks(0), "Incorrect"); - - assert(!iter.has_next(), "Too many elements"); - } - - static void max_nested_pause_phases() { - TimePartitions time_partitions; - time_partitions.report_gc_phase_start("PausePhase", 2); - time_partitions.report_gc_phase_start("SubPhase1", 3); - time_partitions.report_gc_phase_start("SubPhase2", 4); - time_partitions.report_gc_phase_start("SubPhase3", 5); - time_partitions.report_gc_phase_end(6); - time_partitions.report_gc_phase_end(7); - time_partitions.report_gc_phase_end(8); - time_partitions.report_gc_phase_end(9); - - TimePartitionPhasesIterator iter(&time_partitions); - - validate_gc_phase(iter.next(), 0, "PausePhase", 2, 9); - validate_gc_phase(iter.next(), 1, "SubPhase1", 3, 8); - validate_gc_phase(iter.next(), 2, "SubPhase2", 4, 7); - validate_gc_phase(iter.next(), 3, "SubPhase3", 5, 6); - - assert(time_partitions.sum_of_pauses() == Ticks(7) - Ticks(0), "Incorrect"); - assert(time_partitions.longest_pause() == Ticks(7) - Ticks(0), "Incorrect"); - - assert(!iter.has_next(), "Too many elements"); - } - - static void many_sub_pause_phases() { - TimePartitions time_partitions; - time_partitions.report_gc_phase_start("PausePhase", 2); - - time_partitions.report_gc_phase_start("SubPhase1", 3); - time_partitions.report_gc_phase_end(4); - time_partitions.report_gc_phase_start("SubPhase2", 5); - time_partitions.report_gc_phase_end(6); - time_partitions.report_gc_phase_start("SubPhase3", 7); - time_partitions.report_gc_phase_end(8); - time_partitions.report_gc_phase_start("SubPhase4", 9); - time_partitions.report_gc_phase_end(10); - - time_partitions.report_gc_phase_end(11); - - TimePartitionPhasesIterator iter(&time_partitions); - - validate_gc_phase(iter.next(), 0, "PausePhase", 2, 11); - validate_gc_phase(iter.next(), 1, "SubPhase1", 3, 4); - validate_gc_phase(iter.next(), 1, "SubPhase2", 5, 6); - validate_gc_phase(iter.next(), 1, "SubPhase3", 7, 8); - validate_gc_phase(iter.next(), 1, "SubPhase4", 9, 10); - - assert(time_partitions.sum_of_pauses() == Ticks(9) - Ticks(0), "Incorrect"); - assert(time_partitions.longest_pause() == Ticks(9) - Ticks(0), "Incorrect"); - - assert(!iter.has_next(), "Too many elements"); - } - - static void many_sub_pause_phases2() { - TimePartitions time_partitions; - time_partitions.report_gc_phase_start("PausePhase", 2); - - time_partitions.report_gc_phase_start("SubPhase1", 3); - time_partitions.report_gc_phase_start("SubPhase11", 4); - time_partitions.report_gc_phase_end(5); - time_partitions.report_gc_phase_start("SubPhase12", 6); - time_partitions.report_gc_phase_end(7); - time_partitions.report_gc_phase_end(8); - time_partitions.report_gc_phase_start("SubPhase2", 9); - time_partitions.report_gc_phase_start("SubPhase21", 10); - time_partitions.report_gc_phase_end(11); - time_partitions.report_gc_phase_start("SubPhase22", 12); - time_partitions.report_gc_phase_end(13); - time_partitions.report_gc_phase_end(14); - time_partitions.report_gc_phase_start("SubPhase3", 15); - time_partitions.report_gc_phase_end(16); - - time_partitions.report_gc_phase_end(17); - - TimePartitionPhasesIterator iter(&time_partitions); - - validate_gc_phase(iter.next(), 0, "PausePhase", 2, 17); - validate_gc_phase(iter.next(), 1, "SubPhase1", 3, 8); - validate_gc_phase(iter.next(), 2, "SubPhase11", 4, 5); - validate_gc_phase(iter.next(), 2, "SubPhase12", 6, 7); - validate_gc_phase(iter.next(), 1, "SubPhase2", 9, 14); - validate_gc_phase(iter.next(), 2, "SubPhase21", 10, 11); - validate_gc_phase(iter.next(), 2, "SubPhase22", 12, 13); - validate_gc_phase(iter.next(), 1, "SubPhase3", 15, 16); - - assert(time_partitions.sum_of_pauses() == Ticks(15) - Ticks(0), "Incorrect"); - assert(time_partitions.longest_pause() == Ticks(15) - Ticks(0), "Incorrect"); - - assert(!iter.has_next(), "Too many elements"); - } - - static void one_concurrent() { - TimePartitions time_partitions; - time_partitions.report_gc_phase_start("ConcurrentPhase", 2, GCPhase::ConcurrentPhaseType); - time_partitions.report_gc_phase_end(8, GCPhase::ConcurrentPhaseType); - - TimePartitionPhasesIterator iter(&time_partitions); - - validate_gc_phase(iter.next(), 0, "ConcurrentPhase", 2, 8); - // ConcurrentPhaseType should not affect to both 'sum_of_pauses()' and 'longest_pause()'. - assert(time_partitions.sum_of_pauses() == Tickspan(), "Incorrect"); - assert(time_partitions.longest_pause() == Tickspan(), "Incorrect"); - - assert(!iter.has_next(), "Too many elements"); - } -}; - -class GCTimerTest { -public: - static void all() { - gc_start(); - gc_end(); - } - - static void gc_start() { - GCTimer gc_timer; - gc_timer.register_gc_start(1); - - assert(gc_timer.gc_start() == Ticks(1), "Incorrect"); - } - - static void gc_end() { - GCTimer gc_timer; - gc_timer.register_gc_start(1); - gc_timer.register_gc_end(2); - - assert(gc_timer.gc_end() == Ticks(2), "Incorrect"); - } -}; - -void GCTimer_test() { - GCTimerTest::all(); - TimePartitionPhasesIteratorTest::all(); -} - -#endif diff --git a/src/hotspot/share/gc/shared/gcTimer.hpp b/src/hotspot/share/gc/shared/gcTimer.hpp index d8834cfee63..30507edfe34 100644 --- a/src/hotspot/share/gc/shared/gcTimer.hpp +++ b/src/hotspot/share/gc/shared/gcTimer.hpp @@ -129,7 +129,6 @@ class PhasesIterator { }; class GCTimer : public ResourceObj { - NOT_PRODUCT(friend class GCTimerTest;) protected: Ticks _gc_start; Ticks _gc_end; diff --git a/src/hotspot/share/jfr/instrumentation/jfrEventClassTransformer.cpp b/src/hotspot/share/jfr/instrumentation/jfrEventClassTransformer.cpp index c22ee3d7760..0f3a3e067af 100644 --- a/src/hotspot/share/jfr/instrumentation/jfrEventClassTransformer.cpp +++ b/src/hotspot/share/jfr/instrumentation/jfrEventClassTransformer.cpp @@ -27,6 +27,8 @@ #include "classfile/classFileParser.hpp" #include "classfile/classFileStream.hpp" #include "classfile/javaClasses.inline.hpp" +#include "classfile/moduleEntry.hpp" +#include "classfile/modules.hpp" #include "classfile/stackMapTable.hpp" #include "classfile/verificationType.hpp" #include "interpreter/bytecodes.hpp" @@ -61,25 +63,26 @@ static const char* utf8_constants[] = { "J", // 1 "commit", // 2 "eventHandler", // 3 - "Ljdk/jfr/internal/handlers/EventHandler;", // 4 - "duration", // 5 - "begin", // 6 - "()V", // 7 - "isEnabled", // 8 - "()Z", // 9 - "end", // 10 - "shouldCommit", // 11 - "startTime", // 12 - "", // 13 - "jdk/jfr/FlightRecorder", // 14 - "register", // 15 - "(Ljava/lang/Class;)V", // 16 // LAST_REQUIRED_UTF8 - "StackMapTable", // 17 - "Exceptions", // 18 + "duration", // 4 + "begin", // 5 + "()V", // 6 + "isEnabled", // 7 + "()Z", // 8 + "end", // 9 + "shouldCommit", // 10 + "startTime", // 11 // LAST_REQUIRED_UTF8 + "Ljdk/jfr/internal/handlers/EventHandler;", // 12 + "Ljava/lang/Object;", // 13 + "", // 14 + "jdk/jfr/FlightRecorder", // 15 + "register", // 16 + "(Ljava/lang/Class;)V", // 17 + "StackMapTable", // 18 + "Exceptions", // 19 "LineNumberTable", // 20 "LocalVariableTable", // 21 "LocalVariableTypeTable", // 22 - "RuntimeVisibleAnnotation" // 23 + "RuntimeVisibleAnnotation", // 23 }; enum utf8_req_symbols { @@ -87,7 +90,6 @@ enum utf8_req_symbols { UTF8_REQ_J_FIELD_DESC, UTF8_REQ_commit, UTF8_REQ_eventHandler, - UTF8_REQ_eventHandler_FIELD_DESC, UTF8_REQ_duration, UTF8_REQ_begin, UTF8_REQ_EMPTY_VOID_METHOD_DESC, @@ -96,15 +98,17 @@ enum utf8_req_symbols { UTF8_REQ_end, UTF8_REQ_shouldCommit, UTF8_REQ_startTime, - UTF8_REQ_clinit, - UTF8_REQ_FlightRecorder, - UTF8_REQ_register, - UTF8_REQ_CLASS_VOID_METHOD_DESC, NOF_UTF8_REQ_SYMBOLS }; enum utf8_opt_symbols { - UTF8_OPT_StackMapTable = NOF_UTF8_REQ_SYMBOLS, + UTF8_OPT_eventHandler_FIELD_DESC = NOF_UTF8_REQ_SYMBOLS, + UTF8_OPT_LjavaLangObject, + UTF8_OPT_clinit, + UTF8_OPT_FlightRecorder, + UTF8_OPT_register, + UTF8_OPT_CLASS_VOID_METHOD_DESC, + UTF8_OPT_StackMapTable, UTF8_OPT_Exceptions, UTF8_OPT_LineNumberTable, UTF8_OPT_LocalVariableTable, @@ -353,7 +357,7 @@ class AnnotationIterator : public StackObj { static unsigned int unused_hash = 0; static const char value_name[] = "value"; -static bool has_registered_annotation(const InstanceKlass* ik, const Symbol* annotation_type, bool& value) { +static bool has_annotation(const InstanceKlass* ik, const Symbol* annotation_type, bool& value) { assert(annotation_type != NULL, "invariant"); AnnotationArray* class_annotations = ik->class_annotations(); if (class_annotations == NULL) { @@ -383,16 +387,51 @@ static bool has_registered_annotation(const InstanceKlass* ik, const Symbol* ann return false; } -static bool registered_annotation_value(const InstanceKlass* ik, const Symbol* const registered_symbol) { - assert(registered_symbol != NULL, "invariant"); +// Evaluate to the value of the first found Symbol* annotation type. +// Searching moves upwards in the klass hierarchy in order to support +// inherited annotations in addition to the ability to override. +static bool annotation_value(const InstanceKlass* ik, const Symbol* annotation_type, bool& value) { assert(ik != NULL, "invariant"); + assert(annotation_type != NULL, "invariant"); assert(JdkJfrEvent::is_a(ik), "invariant"); - bool registered_value = false; - if (has_registered_annotation(ik, registered_symbol, registered_value)) { - return registered_value; + if (has_annotation(ik, annotation_type, value)) { + return true; } - InstanceKlass* super = InstanceKlass::cast(ik->super()); - return registered_annotation_value(super, registered_symbol); + InstanceKlass* const super = InstanceKlass::cast(ik->super()); + return super != NULL && JdkJfrEvent::is_a(super) ? annotation_value(super, annotation_type, value) : false; +} + +static const char jdk_jfr_module_name[] = "jdk.jfr"; + +static bool java_base_can_read_jdk_jfr() { + static bool can_read = false; + if (can_read) { + return true; + } + static Symbol* jdk_jfr_module_symbol = NULL; + if (jdk_jfr_module_symbol == NULL) { + jdk_jfr_module_symbol = SymbolTable::lookup_only(jdk_jfr_module_name, sizeof jdk_jfr_module_name - 1, unused_hash); + if (jdk_jfr_module_symbol == NULL) { + return false; + } + } + assert(jdk_jfr_module_symbol != NULL, "invariant"); + ModuleEntryTable* const table = Modules::get_module_entry_table(Handle()); + assert(table != NULL, "invariant"); + const ModuleEntry* const java_base_module = table->javabase_moduleEntry(); + if (java_base_module == NULL) { + return false; + } + assert(java_base_module != NULL, "invariant"); + ModuleEntry* const jdk_jfr_module = table->lookup_only(jdk_jfr_module_symbol); + if (jdk_jfr_module == NULL) { + return false; + } + assert(jdk_jfr_module != NULL, "invariant"); + if (java_base_module->can_read(jdk_jfr_module)) { + can_read = true; + } + return can_read; } static const char registered_constant[] = "Ljdk/jfr/Registered;"; @@ -400,13 +439,23 @@ static const char registered_constant[] = "Ljdk/jfr/Registered;"; // Evaluate to the value of the first found "Ljdk/jfr/Registered;" annotation. // Searching moves upwards in the klass hierarchy in order to support // inherited annotations in addition to the ability to override. -static bool should_register_klass(const InstanceKlass* ik) { - static const Symbol* const registered_symbol = SymbolTable::lookup_only(registered_constant, - sizeof registered_constant - 1, - unused_hash); +static bool should_register_klass(const InstanceKlass* ik, bool& untypedEventHandler) { + assert(ik != NULL, "invariant"); + assert(JdkJfrEvent::is_a(ik), "invariant"); + assert(!untypedEventHandler, "invariant"); + static const Symbol* registered_symbol = NULL; + if (registered_symbol == NULL) { + registered_symbol = SymbolTable::lookup_only(registered_constant, sizeof registered_constant - 1, unused_hash); + if (registered_symbol == NULL) { + return false; + } + } assert(registered_symbol != NULL, "invariant"); - return registered_annotation_value(ik, registered_symbol); + bool value = false; // to be set by annotation_value + untypedEventHandler = !(annotation_value(ik, registered_symbol, value) || java_base_can_read_jdk_jfr()); + return value; } + /* * Map an utf8 constant back to its CONSTANT_UTF8_INFO */ @@ -450,6 +499,9 @@ static u2 add_method_ref_info(JfrBigEndianWriter& writer, u2 orig_cp_len, u2& number_of_new_constants, TRAPS) { + assert(cls_name_index != invalid_cp_index, "invariant"); + assert(method_index != invalid_cp_index, "invariant"); + assert(desc_index != invalid_cp_index, "invariant"); assert(is_index_within_range(cls_name_index, orig_cp_len, number_of_new_constants), "invariant"); assert(is_index_within_range(method_index, orig_cp_len, number_of_new_constants), "invariant"); assert(is_index_within_range(desc_index, orig_cp_len, number_of_new_constants), "invariant"); @@ -477,9 +529,9 @@ static u2 add_flr_register_method_constants(JfrBigEndianWriter& writer, TRAPS) { assert(utf8_indexes != NULL, "invariant"); return add_method_ref_info(writer, - utf8_indexes[UTF8_REQ_FlightRecorder], - utf8_indexes[UTF8_REQ_register], - utf8_indexes[UTF8_REQ_CLASS_VOID_METHOD_DESC], + utf8_indexes[UTF8_OPT_FlightRecorder], + utf8_indexes[UTF8_OPT_register], + utf8_indexes[UTF8_OPT_CLASS_VOID_METHOD_DESC], orig_cp_len, number_of_new_constants, THREAD); @@ -495,8 +547,8 @@ static u2 add_flr_register_method_constants(JfrBigEndianWriter& writer, * } */ static jlong add_field_info(JfrBigEndianWriter& writer, u2 name_index, u2 desc_index, bool is_static = false) { - assert(name_index > 0, "invariant"); - assert(desc_index > 0, "invariant"); + assert(name_index != invalid_cp_index, "invariant"); + assert(desc_index != invalid_cp_index, "invariant"); DEBUG_ONLY(const jlong start_offset = writer.current_offset();) writer.write(JVM_ACC_SYNTHETIC | JVM_ACC_PRIVATE | (is_static ? JVM_ACC_STATIC : JVM_ACC_TRANSIENT)); // flags writer.write(name_index); @@ -507,11 +559,11 @@ static jlong add_field_info(JfrBigEndianWriter& writer, u2 name_index, u2 desc_i return writer.current_offset(); } -static u2 add_field_infos(JfrBigEndianWriter& writer, const u2* utf8_indexes) { +static u2 add_field_infos(JfrBigEndianWriter& writer, const u2* utf8_indexes, bool untypedEventHandler) { assert(utf8_indexes != NULL, "invariant"); add_field_info(writer, utf8_indexes[UTF8_REQ_eventHandler], - utf8_indexes[UTF8_REQ_eventHandler_FIELD_DESC], + untypedEventHandler ? utf8_indexes[UTF8_OPT_LjavaLangObject] : utf8_indexes[UTF8_OPT_eventHandler_FIELD_DESC], true); // static add_field_info(writer, @@ -989,7 +1041,8 @@ static jlong insert_clinit_method(const InstanceKlass* ik, // This is to ensure that padding can be done // where needed and to simplify size calculations. static const u2 injected_code_length = 8; - const u2 name_index = utf8_indexes[UTF8_REQ_clinit]; + const u2 name_index = utf8_indexes[UTF8_OPT_clinit]; + assert(name_index != invalid_cp_index, "invariant"); const u2 desc_index = utf8_indexes[UTF8_REQ_EMPTY_VOID_METHOD_DESC]; const u2 max_stack = MAX2(clinit_method != NULL ? clinit_method->verifier_max_stack() : 1, 1); const u2 max_locals = MAX2(clinit_method != NULL ? clinit_method->max_locals() : 0, 0); @@ -1138,59 +1191,58 @@ static u2 resolve_utf8_indexes(JfrBigEndianWriter& writer, u2* const utf8_indexes, u2 orig_cp_len, const Method* clinit_method, + bool register_klass, + bool untypedEventHandler, TRAPS) { assert(utf8_indexes != NULL, "invariant"); u2 added_cp_entries = 0; // resolve all required symbols for (u2 index = 0; index < NOF_UTF8_REQ_SYMBOLS; ++index) { - utf8_indexes[index] = find_or_add_utf8_info(writer, - ik, - utf8_constants[index], - orig_cp_len, - added_cp_entries, - THREAD); + utf8_indexes[index] = find_or_add_utf8_info(writer, ik, utf8_constants[index], orig_cp_len, added_cp_entries, THREAD); } - // Now determine optional constants (mainly "Code" attributes) + + // resolve optional constants + utf8_indexes[UTF8_OPT_eventHandler_FIELD_DESC] = untypedEventHandler ? invalid_cp_index : + find_or_add_utf8_info(writer, ik, utf8_constants[UTF8_OPT_eventHandler_FIELD_DESC], orig_cp_len, added_cp_entries, THREAD); + + utf8_indexes[UTF8_OPT_LjavaLangObject] = untypedEventHandler ? + find_or_add_utf8_info(writer, ik, utf8_constants[UTF8_OPT_LjavaLangObject], orig_cp_len, added_cp_entries, THREAD) : invalid_cp_index; + + if (register_klass) { + utf8_indexes[UTF8_OPT_clinit] = + find_or_add_utf8_info(writer, ik, utf8_constants[UTF8_OPT_clinit], orig_cp_len, added_cp_entries, THREAD); + utf8_indexes[UTF8_OPT_FlightRecorder] = + find_or_add_utf8_info(writer, ik, utf8_constants[UTF8_OPT_FlightRecorder], orig_cp_len, added_cp_entries, THREAD); + utf8_indexes[UTF8_OPT_register] = + find_or_add_utf8_info(writer, ik, utf8_constants[UTF8_OPT_register], orig_cp_len, added_cp_entries, THREAD); + utf8_indexes[UTF8_OPT_CLASS_VOID_METHOD_DESC] = + find_or_add_utf8_info(writer, ik, utf8_constants[UTF8_OPT_CLASS_VOID_METHOD_DESC], orig_cp_len, added_cp_entries, THREAD); + } else { + utf8_indexes[UTF8_OPT_clinit] = invalid_cp_index; + utf8_indexes[UTF8_OPT_FlightRecorder] = invalid_cp_index; + utf8_indexes[UTF8_OPT_register] = invalid_cp_index; + utf8_indexes[UTF8_OPT_CLASS_VOID_METHOD_DESC] = invalid_cp_index; + } + if (clinit_method != NULL && clinit_method->has_stackmap_table()) { utf8_indexes[UTF8_OPT_StackMapTable] = - find_or_add_utf8_info(writer, - ik, - utf8_constants[UTF8_OPT_StackMapTable], - orig_cp_len, - added_cp_entries, - THREAD); + find_or_add_utf8_info(writer, ik, utf8_constants[UTF8_OPT_StackMapTable], orig_cp_len, added_cp_entries, THREAD); } else { utf8_indexes[UTF8_OPT_StackMapTable] = invalid_cp_index; } if (clinit_method != NULL && clinit_method->has_linenumber_table()) { utf8_indexes[UTF8_OPT_LineNumberTable] = - find_or_add_utf8_info(writer, - ik, - utf8_constants[UTF8_OPT_LineNumberTable], - orig_cp_len, - added_cp_entries, - THREAD); + find_or_add_utf8_info(writer, ik, utf8_constants[UTF8_OPT_LineNumberTable], orig_cp_len, added_cp_entries, THREAD); } else { utf8_indexes[UTF8_OPT_LineNumberTable] = invalid_cp_index; } if (clinit_method != NULL && clinit_method->has_localvariable_table()) { utf8_indexes[UTF8_OPT_LocalVariableTable] = - find_or_add_utf8_info(writer, - ik, - utf8_constants[UTF8_OPT_LocalVariableTable], - orig_cp_len, - added_cp_entries, - THREAD); - + find_or_add_utf8_info(writer, ik, utf8_constants[UTF8_OPT_LocalVariableTable], orig_cp_len, added_cp_entries, THREAD); utf8_indexes[UTF8_OPT_LocalVariableTypeTable] = - find_or_add_utf8_info(writer, - ik, - utf8_constants[UTF8_OPT_LocalVariableTypeTable], - orig_cp_len, - added_cp_entries, - THREAD); + find_or_add_utf8_info(writer, ik, utf8_constants[UTF8_OPT_LocalVariableTypeTable], orig_cp_len, added_cp_entries, THREAD); } else { utf8_indexes[UTF8_OPT_LocalVariableTable] = invalid_cp_index; utf8_indexes[UTF8_OPT_LocalVariableTypeTable] = invalid_cp_index; @@ -1207,7 +1259,8 @@ static u1* new_bytes_for_lazy_instrumentation(const InstanceKlass* ik, // If the class already has a clinit method // we need to take that into account const Method* clinit_method = ik->class_initializer(); - const bool register_klass = should_register_klass(ik); + bool untypedEventHandler = false; + const bool register_klass = should_register_klass(ik, untypedEventHandler); const ClassFileStream* const orig_stream = parser.clone_stream(); const int orig_stream_size = orig_stream->length(); assert(orig_stream->current_offset() == 0, "invariant"); @@ -1241,7 +1294,8 @@ static u1* new_bytes_for_lazy_instrumentation(const InstanceKlass* ik, // Resolve_utf8_indexes will be conservative in attempting to // locate an existing UTF8_INFO; it will only append constants // that is absolutely required - u2 number_of_new_constants = resolve_utf8_indexes(writer, ik, utf8_indexes, orig_cp_len, clinit_method, THREAD); + u2 number_of_new_constants = + resolve_utf8_indexes(writer, ik, utf8_indexes, orig_cp_len, clinit_method, register_klass, untypedEventHandler, THREAD); // UTF8_INFO entries now added to the constant pool // In order to invoke a method we would need additional // constants, JVM_CONSTANT_Class, JVM_CONSTANT_NameAndType @@ -1274,7 +1328,7 @@ static u1* new_bytes_for_lazy_instrumentation(const InstanceKlass* ik, assert(writer.is_valid(), "invariant"); // We are sitting just after the original number of field_infos // so this is a position where we can add (append) new field_infos - const u2 number_of_new_fields = add_field_infos(writer, utf8_indexes); + const u2 number_of_new_fields = add_field_infos(writer, utf8_indexes, untypedEventHandler); assert(writer.is_valid(), "invariant"); const jlong new_method_len_offset = writer.current_offset(); // Additional field_infos added, update classfile fields_count diff --git a/src/hotspot/share/jfr/jni/jfrGetAllEventClasses.cpp b/src/hotspot/share/jfr/jni/jfrGetAllEventClasses.cpp index d4978c46be7..87290ad08b8 100644 --- a/src/hotspot/share/jfr/jni/jfrGetAllEventClasses.cpp +++ b/src/hotspot/share/jfr/jni/jfrGetAllEventClasses.cpp @@ -132,7 +132,7 @@ jobject JfrEventClasses::get_all_event_classes(TRAPS) { DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD)); initialize(THREAD); assert(empty_java_util_arraylist != NULL, "should have been setup already!"); - static const char jdk_jfr_event_name[] = "jdk/jfr/Event"; + static const char jdk_jfr_event_name[] = "jdk/internal/event/Event"; unsigned int unused_hash = 0; Symbol* const event_klass_name = SymbolTable::lookup_only(jdk_jfr_event_name, sizeof jdk_jfr_event_name - 1, unused_hash); diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.cpp index 7e578e08c9d..dafedd83cb1 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.cpp @@ -76,22 +76,43 @@ static traceid next_class_loader_data_id() { return atomic_inc(&cld_id_counter) << TRACE_ID_SHIFT; } +static bool found_jdk_internal_event_klass = false; static bool found_jdk_jfr_event_klass = false; static void check_klass(const Klass* klass) { assert(klass != NULL, "invariant"); - if (found_jdk_jfr_event_klass) { + if (found_jdk_internal_event_klass && found_jdk_jfr_event_klass) { return; } + static const Symbol* jdk_internal_event_sym = NULL; + if (jdk_internal_event_sym == NULL) { + // setup when loading the first TypeArrayKlass (Universe::genesis) hence single threaded invariant + jdk_internal_event_sym = SymbolTable::new_permanent_symbol("jdk/internal/event/Event", Thread::current()); + } + assert(jdk_internal_event_sym != NULL, "invariant"); + static const Symbol* jdk_jfr_event_sym = NULL; if (jdk_jfr_event_sym == NULL) { // setup when loading the first TypeArrayKlass (Universe::genesis) hence single threaded invariant jdk_jfr_event_sym = SymbolTable::new_permanent_symbol("jdk/jfr/Event", Thread::current()); } assert(jdk_jfr_event_sym != NULL, "invariant"); - if (jdk_jfr_event_sym == klass->name() && klass->class_loader() == NULL) { - found_jdk_jfr_event_klass = true; - JfrTraceId::tag_as_jdk_jfr_event(klass); + const Symbol* const klass_name = klass->name(); + + if (!found_jdk_internal_event_klass) { + if (jdk_internal_event_sym == klass_name && klass->class_loader() == NULL) { + found_jdk_internal_event_klass = true; + JfrTraceId::tag_as_jdk_jfr_event(klass); + return; + } + } + + if (!found_jdk_jfr_event_klass) { + if (jdk_jfr_event_sym == klass_name && klass->class_loader() == NULL) { + found_jdk_jfr_event_klass = true; + JfrTraceId::tag_as_jdk_jfr_event(klass); + return; + } } } diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp index 3d130b8fd78..5ad1f8671ab 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp @@ -112,10 +112,8 @@ inline bool JfrTraceId::is_jdk_jfr_event(const Klass* k) { inline void JfrTraceId::tag_as_jdk_jfr_event(const Klass* klass) { assert(klass != NULL, "invariant"); - assert(IS_NOT_AN_EVENT_KLASS(klass), "invariant"); SET_TAG(klass, JDK_JFR_EVENT_KLASS); assert(IS_JDK_JFR_EVENT_KLASS(klass), "invariant"); - assert(IS_NOT_AN_EVENT_SUB_KLASS(klass), "invariant"); } inline bool JfrTraceId::is_jdk_jfr_event_sub(const Klass* k) { @@ -125,7 +123,7 @@ inline bool JfrTraceId::is_jdk_jfr_event_sub(const Klass* k) { inline void JfrTraceId::tag_as_jdk_jfr_event_sub(const Klass* k) { assert(k != NULL, "invariant"); - if (IS_NOT_AN_EVENT_KLASS(k)) { + if (IS_NOT_AN_EVENT_SUB_KLASS(k)) { SET_TAG(k, JDK_JFR_EVENT_SUBKLASS); } assert(IS_JDK_JFR_EVENT_SUBKLASS(k), "invariant"); diff --git a/src/hotspot/share/memory/filemap.cpp b/src/hotspot/share/memory/filemap.cpp index 63778deb8f1..5f287f301cd 100644 --- a/src/hotspot/share/memory/filemap.cpp +++ b/src/hotspot/share/memory/filemap.cpp @@ -914,6 +914,19 @@ void FileMapInfo::map_heap_regions_impl() { return; } + if (JvmtiExport::should_post_class_file_load_hook() && JvmtiExport::has_early_class_hook_env()) { + ShouldNotReachHere(); // CDS should have been disabled. + // The archived objects are mapped at JVM start-up, but we don't know if + // j.l.String or j.l.Class might be replaced by the ClassFileLoadHook, + // which would make the archived String or mirror objects invalid. Let's be safe and not + // use the archived objects. These 2 classes are loaded during the JVMTI "early" stage. + // + // If JvmtiExport::has_early_class_hook_env() is false, the classes of some objects + // in the archived subgraphs may be replaced by the ClassFileLoadHook. But that's OK + // because we won't install an archived object subgraph if the klass of any of the + // referenced objects are replaced. See HeapShared::initialize_from_archived_subgraph(). + } + MemRegion heap_reserved = Universe::heap()->reserved_region(); log_info(cds)("CDS archive was created with max heap size = " SIZE_FORMAT "M, and the following configuration:", @@ -1224,6 +1237,15 @@ bool FileMapInfo::_validating_shared_path_table = false; bool FileMapInfo::initialize() { assert(UseSharedSpaces, "UseSharedSpaces expected."); + if (JvmtiExport::should_post_class_file_load_hook() && JvmtiExport::has_early_class_hook_env()) { + // CDS assumes that no classes resolved in SystemDictionary::resolve_well_known_classes + // are replaced at runtime by JVMTI ClassFileLoadHook. All of those classes are resolved + // during the JVMTI "early" stage, so we can still use CDS if + // JvmtiExport::has_early_class_hook_env() is false. + FileMapInfo::fail_continue("CDS is disabled because early JVMTI ClassFileLoadHook is in use."); + return false; + } + if (!open_for_read()) { return false; } diff --git a/src/hotspot/share/memory/heapShared.cpp b/src/hotspot/share/memory/heapShared.cpp index 69e10b1767e..4c9d55ccf8a 100644 --- a/src/hotspot/share/memory/heapShared.cpp +++ b/src/hotspot/share/memory/heapShared.cpp @@ -417,6 +417,11 @@ void HeapShared::initialize_from_archived_subgraph(Klass* k) { Klass* resolved_k = SystemDictionary::resolve_or_null( (obj_k)->name(), THREAD); if (resolved_k != obj_k) { + assert(!SystemDictionary::is_well_known_klass(resolved_k), + "shared well-known classes must not be replaced by JVMTI ClassFileLoadHook"); + ResourceMark rm(THREAD); + log_info(cds, heap)("Failed to load subgraph because %s was not loaded from archive", + resolved_k->external_name()); return; } if ((obj_k)->is_instance_klass()) { diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp index ab6c54d832f..7f4243d993f 100644 --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -604,7 +604,9 @@ bool C2Compiler::is_intrinsic_supported(const methodHandle& method, bool is_virt return true; } -int C2Compiler::initial_code_buffer_size() { - assert(SegmentedCodeCache, "Should be only used with a segmented code cache"); - return Compile::MAX_inst_size + Compile::MAX_locs_size + initial_const_capacity; +int C2Compiler::initial_code_buffer_size(int const_size) { + // See Compile::init_scratch_buffer_blob + int locs_size = sizeof(relocInfo) * Compile::MAX_locs_size; + int slop = 2 * CodeSection::end_slop(); // space between sections + return Compile::MAX_inst_size + Compile::MAX_stubs_size + const_size + slop + locs_size; } diff --git a/src/hotspot/share/opto/c2compiler.hpp b/src/hotspot/share/opto/c2compiler.hpp index d7eb0225841..33193bde3e5 100644 --- a/src/hotspot/share/opto/c2compiler.hpp +++ b/src/hotspot/share/opto/c2compiler.hpp @@ -26,6 +26,7 @@ #define SHARE_VM_OPTO_C2COMPILER_HPP #include "compiler/abstractCompiler.hpp" +#include "opto/output.hpp" class C2Compiler : public AbstractCompiler { private: @@ -66,7 +67,7 @@ public: virtual bool is_intrinsic_supported(const methodHandle& method, bool is_virtual); // Initial size of the code buffer (may be increased at runtime) - static int initial_code_buffer_size(); + static int initial_code_buffer_size(int const_size = initial_const_capacity); }; #endif // SHARE_VM_OPTO_C2COMPILER_HPP diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 30c56b1f4a5..759f193a3be 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -544,9 +544,7 @@ void Compile::init_scratch_buffer_blob(int const_size) { ResourceMark rm; _scratch_const_size = const_size; - int locs_size = sizeof(relocInfo) * MAX_locs_size; - int slop = 2 * CodeSection::end_slop(); // space between sections - int size = (MAX_inst_size + MAX_stubs_size + _scratch_const_size + slop + locs_size); + int size = C2Compiler::initial_code_buffer_size(const_size); blob = BufferBlob::create("Compile::scratch_buffer", size); // Record the buffer blob for next time. set_scratch_buffer_blob(blob); diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index 715aa84ea9c..79f3695efc8 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -2116,8 +2116,17 @@ void GraphKit::uncommon_trap(int trap_request, // We use this to determine if an object is so "fresh" that // it does not require card marks. Node* GraphKit::just_allocated_object(Node* current_control) { - if (C->recent_alloc_ctl() == current_control) - return C->recent_alloc_obj(); + Node* ctrl = current_control; + // Object:: 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; } diff --git a/src/hotspot/share/prims/jvmtiExport.cpp b/src/hotspot/share/prims/jvmtiExport.cpp index 07f381e1c48..3379dcc1b7e 100644 --- a/src/hotspot/share/prims/jvmtiExport.cpp +++ b/src/hotspot/share/prims/jvmtiExport.cpp @@ -994,6 +994,20 @@ class JvmtiClassFileLoadHookPoster : public StackObj { } }; +bool JvmtiExport::is_early_phase() { + return JvmtiEnvBase::get_phase() <= JVMTI_PHASE_PRIMORDIAL; +} + +bool JvmtiExport::has_early_class_hook_env() { + JvmtiEnvIterator it; + for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { + if (env->early_class_hook_env()) { + return true; + } + } + return false; +} + bool JvmtiExport::_should_post_class_file_load_hook = false; // this entry is for class file load hook on class load, redefine and retransform diff --git a/src/hotspot/share/prims/jvmtiExport.hpp b/src/hotspot/share/prims/jvmtiExport.hpp index 42c9000693a..7e8fc7e584e 100644 --- a/src/hotspot/share/prims/jvmtiExport.hpp +++ b/src/hotspot/share/prims/jvmtiExport.hpp @@ -328,6 +328,8 @@ class JvmtiExport : public AllStatic { JVMTI_ONLY(return _should_post_class_file_load_hook); NOT_JVMTI(return false;) } + static bool is_early_phase(); + static bool has_early_class_hook_env(); // Return true if the class was modified by the hook. static bool post_class_file_load_hook(Symbol* h_name, Handle class_loader, Handle h_protection_domain, diff --git a/src/hotspot/share/runtime/compilationPolicy.cpp b/src/hotspot/share/runtime/compilationPolicy.cpp index f235d4f093d..3fc9689c524 100644 --- a/src/hotspot/share/runtime/compilationPolicy.cpp +++ b/src/hotspot/share/runtime/compilationPolicy.cpp @@ -46,6 +46,13 @@ #include "utilities/events.hpp" #include "utilities/globalDefinitions.hpp" +#ifdef COMPILER1 +#include "c1/c1_Compiler.hpp" +#endif +#ifdef COMPILER2 +#include "opto/c2compiler.hpp" +#endif + CompilationPolicy* CompilationPolicy::_policy; elapsedTimer CompilationPolicy::_accumulated_time; bool CompilationPolicy::_in_vm_startup; @@ -222,6 +229,19 @@ void NonTieredCompPolicy::initialize() { // max(log2(8)-1,1) = 2 compiler threads on an 8-way machine. // May help big-app startup time. _compiler_count = MAX2(log2_intptr(os::active_processor_count())-1,1); + // Make sure there is enough space in the code cache to hold all the compiler buffers + size_t buffer_size = 1; +#ifdef COMPILER1 + buffer_size = is_client_compilation_mode_vm() ? Compiler::code_buffer_size() : buffer_size; +#endif +#ifdef COMPILER2 + buffer_size = is_server_compilation_mode_vm() ? C2Compiler::initial_code_buffer_size() : buffer_size; +#endif + int max_count = (ReservedCodeCacheSize - (CodeCacheMinimumUseSpace DEBUG_ONLY(* 3))) / (int)buffer_size; + if (_compiler_count > max_count) { + // Lower the compiler count such that all buffers fit into the code cache + _compiler_count = MAX2(max_count, 1); + } FLAG_SET_ERGO(intx, CICompilerCount, _compiler_count); } else { _compiler_count = CICompilerCount; diff --git a/src/hotspot/share/runtime/handshake.cpp b/src/hotspot/share/runtime/handshake.cpp index 68703cb3663..84b8be3d99f 100644 --- a/src/hotspot/share/runtime/handshake.cpp +++ b/src/hotspot/share/runtime/handshake.cpp @@ -41,7 +41,6 @@ class HandshakeOperation: public StackObj { public: virtual void do_handshake(JavaThread* thread) = 0; - virtual void cancel_handshake(JavaThread* thread) = 0; }; class HandshakeThreadsOperation: public HandshakeOperation { @@ -51,8 +50,6 @@ class HandshakeThreadsOperation: public HandshakeOperation { public: HandshakeThreadsOperation(ThreadClosure* cl) : _thread_cl(cl) {} void do_handshake(JavaThread* thread); - void cancel_handshake(JavaThread* thread) { _done.signal(); }; - bool thread_has_completed() { return _done.trywait(); } #ifdef ASSERT @@ -121,15 +118,11 @@ class VM_HandshakeOneThread: public VM_Handshake { DEBUG_ONLY(_op->check_state();) TraceTime timer("Performing single-target operation (vmoperation doit)", TRACETIME_LOG(Info, handshake)); - { - ThreadsListHandle tlh; - if (tlh.includes(_target)) { - set_handshake(_target); - _thread_alive = true; - } - } - - if (!_thread_alive) { + ThreadsListHandle tlh; + if (tlh.includes(_target)) { + set_handshake(_target); + _thread_alive = true; + } else { return; } @@ -147,20 +140,9 @@ class VM_HandshakeOneThread: public VM_Handshake { // We need to re-think this with SMR ThreadsList. // There is an assumption in the code that the Threads_lock should be // locked during certain phases. - MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag); - ThreadsListHandle tlh; - if (tlh.includes(_target)) { - // Warning _target's address might be re-used. - // handshake_process_by_vmthread will check the semaphore for us again. - // Since we can't have more then one handshake in flight a reuse of - // _target's address should be okay since the new thread will not have - // an operation. + { + MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag); _target->handshake_process_by_vmthread(); - } else { - // We can't warn here since the thread does cancel_handshake after - // it has been removed from the ThreadsList. So we should just keep - // looping here until while below returns false. If we have a bug, - // then we hang here, which is good for debugging. } } while (!poll_for_completed_thread()); DEBUG_ONLY(_op->check_state();) @@ -179,8 +161,9 @@ class VM_HandshakeAllThreads: public VM_Handshake { DEBUG_ONLY(_op->check_state();) TraceTime timer("Performing operation (vmoperation doit)", TRACETIME_LOG(Info, handshake)); + JavaThreadIteratorWithHandle jtiwh; int number_of_threads_issued = 0; - for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thr = jtiwh.next(); ) { + for (JavaThread *thr = jtiwh.next(); thr != NULL; thr = jtiwh.next()) { set_handshake(thr); number_of_threads_issued++; } @@ -210,8 +193,9 @@ class VM_HandshakeAllThreads: public VM_Handshake { // We need to re-think this with SMR ThreadsList. // There is an assumption in the code that the Threads_lock should // be locked during certain phases. + jtiwh.rewind(); MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag); - for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thr = jtiwh.next(); ) { + for (JavaThread *thr = jtiwh.next(); thr != NULL; thr = jtiwh.next()) { // A new thread on the ThreadsList will not have an operation, // hence it is skipped in handshake_process_by_vmthread. thr->handshake_process_by_vmthread(); @@ -262,7 +246,11 @@ void HandshakeThreadsOperation::do_handshake(JavaThread* thread) { FormatBufferResource message("Operation for thread " PTR_FORMAT ", is_vm_thread: %s", p2i(thread), BOOL_TO_STR(Thread::current()->is_VM_thread())); TraceTime timer(message, TRACETIME_LOG(Debug, handshake, task)); - _thread_cl->do_thread(thread); + + // Only actually execute the operation for non terminated threads. + if (!thread->is_terminated()) { + _thread_cl->do_thread(thread); + } // Use the semaphore to inform the VM thread that we have completed the operation _done.signal(); @@ -306,12 +294,7 @@ void HandshakeState::clear_handshake(JavaThread* target) { void HandshakeState::process_self_inner(JavaThread* thread) { assert(Thread::current() == thread, "should call from thread"); - - if (thread->is_terminated()) { - // If thread is not on threads list but armed, cancel. - thread->cancel_handshake(); - return; - } + assert(!thread->is_terminated(), "should not be a terminated thread"); CautiouslyPreserveExceptionMark pem(thread); ThreadInVMForHandshake tivm(thread); @@ -327,16 +310,6 @@ void HandshakeState::process_self_inner(JavaThread* thread) { _semaphore.signal(); } -void HandshakeState::cancel_inner(JavaThread* thread) { - assert(Thread::current() == thread, "should call from thread"); - assert(thread->thread_state() == _thread_in_vm, "must be in vm state"); - HandshakeOperation* op = _operation; - clear_handshake(thread); - if (op != NULL) { - op->cancel_handshake(thread); - } -} - bool HandshakeState::vmthread_can_process_handshake(JavaThread* target) { // SafepointSynchronize::safepoint_safe() does not consider an externally // suspended thread to be safe. However, this function must be called with @@ -344,7 +317,7 @@ bool HandshakeState::vmthread_can_process_handshake(JavaThread* target) { // resumed thus it is safe. assert(Threads_lock->owned_by_self(), "Not holding Threads_lock."); return SafepointSynchronize::safepoint_safe(target, target->thread_state()) || - target->is_ext_suspended(); + target->is_ext_suspended() || target->is_terminated(); } static bool possibly_vmthread_can_process_handshake(JavaThread* target) { @@ -355,6 +328,9 @@ static bool possibly_vmthread_can_process_handshake(JavaThread* target) { if (target->is_ext_suspended()) { return true; } + if (target->is_terminated()) { + return true; + } switch (target->thread_state()) { case _thread_in_native: // native threads are safe if they have no java stack or have walkable stack @@ -381,6 +357,8 @@ bool HandshakeState::claim_handshake_for_vmthread() { void HandshakeState::process_by_vmthread(JavaThread* target) { assert(Thread::current()->is_VM_thread(), "should call from vm thread"); + // Threads_lock must be held here, but that is assert()ed in + // possibly_vmthread_can_process_handshake(). if (!has_operation()) { // JT has already cleared its handshake @@ -402,7 +380,6 @@ void HandshakeState::process_by_vmthread(JavaThread* target) { // getting caught by the semaphore. if (vmthread_can_process_handshake(target)) { guarantee(!_semaphore.trywait(), "we should already own the semaphore"); - _operation->do_handshake(target); // Disarm after VM thread have executed the operation. clear_handshake(target); diff --git a/src/hotspot/share/runtime/handshake.hpp b/src/hotspot/share/runtime/handshake.hpp index 88dcd7ed420..a735d1ec97a 100644 --- a/src/hotspot/share/runtime/handshake.hpp +++ b/src/hotspot/share/runtime/handshake.hpp @@ -60,7 +60,6 @@ class HandshakeState { bool vmthread_can_process_handshake(JavaThread* target); void clear_handshake(JavaThread* thread); - void cancel_inner(JavaThread* thread); void process_self_inner(JavaThread* thread); public: @@ -72,19 +71,13 @@ public: return _operation != NULL; } - void cancel(JavaThread* thread) { - if (!_thread_in_process_handshake) { - FlagSetting fs(_thread_in_process_handshake, true); - cancel_inner(thread); - } - } - void process_by_self(JavaThread* thread) { if (!_thread_in_process_handshake) { FlagSetting fs(_thread_in_process_handshake, true); process_self_inner(thread); } } + void process_by_vmthread(JavaThread* target); }; diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp index 496607c3d17..42960cd0c7f 100644 --- a/src/hotspot/share/runtime/thread.cpp +++ b/src/hotspot/share/runtime/thread.cpp @@ -4276,9 +4276,6 @@ bool Threads::destroy_vm() { before_exit(thread); thread->exit(true); - // thread will never call smr_delete, instead of implicit cancel - // in wait_for_vm_thread_exit we do it explicit. - thread->cancel_handshake(); // Stop VM thread. { diff --git a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp index ab7e01ccda8..69686e2d66f 100644 --- a/src/hotspot/share/runtime/thread.hpp +++ b/src/hotspot/share/runtime/thread.hpp @@ -1271,10 +1271,6 @@ class JavaThread: public Thread { return _handshake.has_operation(); } - void cancel_handshake() { - _handshake.cancel(this); - } - void handshake_process_by_self() { _handshake.process_by_self(this); } diff --git a/src/hotspot/share/runtime/threadSMR.cpp b/src/hotspot/share/runtime/threadSMR.cpp index 8b232fbde0b..ba18aa497d0 100644 --- a/src/hotspot/share/runtime/threadSMR.cpp +++ b/src/hotspot/share/runtime/threadSMR.cpp @@ -989,11 +989,6 @@ void ThreadsSMRSupport::smr_delete(JavaThread *thread) { // Retry the whole scenario. } - if (ThreadLocalHandshakes) { - // The thread is about to be deleted so cancel any handshake. - thread->cancel_handshake(); - } - delete thread; if (EnableThreadSMRStatistics) { timer.stop(); diff --git a/src/hotspot/share/runtime/tieredThresholdPolicy.cpp b/src/hotspot/share/runtime/tieredThresholdPolicy.cpp index 0e6a2acbe31..4d8b351730b 100644 --- a/src/hotspot/share/runtime/tieredThresholdPolicy.cpp +++ b/src/hotspot/share/runtime/tieredThresholdPolicy.cpp @@ -38,6 +38,9 @@ #ifdef TIERED +#include "c1/c1_Compiler.hpp" +#include "opto/c2compiler.hpp" + template bool TieredThresholdPolicy::call_predicate_helper(int i, int b, double scale, Method* method) { double threshold_scaling; @@ -215,6 +218,7 @@ void TieredThresholdPolicy::print_event(EventType type, const methodHandle& mh, void TieredThresholdPolicy::initialize() { int count = CICompilerCount; + bool c1_only = TieredStopAtLevel < CompLevel_full_optimization; #ifdef _LP64 // Turn on ergonomic compiler count selection if (FLAG_IS_DEFAULT(CICompilerCountPerCPU) && FLAG_IS_DEFAULT(CICompilerCount)) { @@ -225,6 +229,15 @@ void TieredThresholdPolicy::initialize() { int log_cpu = log2_intptr(os::active_processor_count()); int loglog_cpu = log2_intptr(MAX2(log_cpu, 1)); count = MAX2(log_cpu * loglog_cpu * 3 / 2, 2); + // Make sure there is enough space in the code cache to hold all the compiler buffers + size_t c1_size = Compiler::code_buffer_size(); + size_t c2_size = C2Compiler::initial_code_buffer_size(); + size_t buffer_size = c1_only ? c1_size : (c1_size/3 + 2*c2_size/3); + int max_count = (ReservedCodeCacheSize - (CodeCacheMinimumUseSpace DEBUG_ONLY(* 3))) / (int)buffer_size; + if (count > max_count) { + // Lower the compiler count such that all buffers fit into the code cache + count = MAX2(max_count, c1_only ? 1 : 2); + } FLAG_SET_ERGO(intx, CICompilerCount, count); } #else @@ -241,7 +254,7 @@ void TieredThresholdPolicy::initialize() { } #endif - if (TieredStopAtLevel < CompLevel_full_optimization) { + if (c1_only) { // No C2 compiler thread required set_c1_count(count); } else { diff --git a/src/hotspot/share/utilities/concurrentHashTable.hpp b/src/hotspot/share/utilities/concurrentHashTable.hpp index 708fe016998..f93494b885c 100644 --- a/src/hotspot/share/utilities/concurrentHashTable.hpp +++ b/src/hotspot/share/utilities/concurrentHashTable.hpp @@ -25,6 +25,10 @@ #ifndef SHARE_UTILITIES_CONCURRENT_HASH_TABLE_HPP #define SHARE_UTILITIES_CONCURRENT_HASH_TABLE_HPP +#include "memory/allocation.hpp" +#include "utilities/globalCounter.hpp" +#include "utilities/globalDefinitions.hpp" + // A mostly concurrent-hash-table where the read-side is wait-free, inserts are // CAS and deletes mutual exclude each other on per bucket-basis. VALUE is the // type kept inside each Node and CONFIG contains hash and allocation methods. @@ -247,6 +251,7 @@ class ConcurrentHashTable : public CHeapObj { protected: Thread* _thread; ConcurrentHashTable* _cht; + GlobalCounter::CSContext _cs_context; public: ScopedCS(Thread* thread, ConcurrentHashTable* cht); ~ScopedCS(); diff --git a/src/hotspot/share/utilities/concurrentHashTable.inline.hpp b/src/hotspot/share/utilities/concurrentHashTable.inline.hpp index f4aa3747fd4..3e08c3ee40f 100644 --- a/src/hotspot/share/utilities/concurrentHashTable.inline.hpp +++ b/src/hotspot/share/utilities/concurrentHashTable.inline.hpp @@ -208,9 +208,10 @@ inline ConcurrentHashTable:: template inline ConcurrentHashTable:: ScopedCS::ScopedCS(Thread* thread, ConcurrentHashTable* cht) - : _thread(thread), _cht(cht) + : _thread(thread), + _cht(cht), + _cs_context(GlobalCounter::critical_section_begin(_thread)) { - GlobalCounter::critical_section_begin(_thread); // This version is published now. if (OrderAccess::load_acquire(&_cht->_invisible_epoch) != NULL) { OrderAccess::release_store_fence(&_cht->_invisible_epoch, (Thread*)NULL); @@ -221,7 +222,7 @@ template inline ConcurrentHashTable:: ScopedCS::~ScopedCS() { - GlobalCounter::critical_section_end(_thread); + GlobalCounter::critical_section_end(_thread, _cs_context); } // BaseConfig @@ -502,7 +503,7 @@ inline void ConcurrentHashTable:: // concurrent single deletes. The _invisible_epoch can only be used by the // owner of _resize_lock, us here. There we should not changed it in our // own read-side. - GlobalCounter::critical_section_begin(thread); + GlobalCounter::CSContext cs_context = GlobalCounter::critical_section_begin(thread); for (size_t bucket_it = start_idx; bucket_it < stop_idx; bucket_it++) { Bucket* bucket = table->get_bucket(bucket_it); Bucket* prefetch_bucket = (bucket_it+1) < stop_idx ? @@ -514,7 +515,7 @@ inline void ConcurrentHashTable:: continue; } - GlobalCounter::critical_section_end(thread); + GlobalCounter::critical_section_end(thread, cs_context); // We left critical section but the bucket cannot be removed while we hold // the _resize_lock. bucket->lock(); @@ -530,9 +531,9 @@ inline void ConcurrentHashTable:: Node::destroy_node(ndel[node_it]); DEBUG_ONLY(ndel[node_it] = (Node*)POISON_PTR;) } - GlobalCounter::critical_section_begin(thread); + cs_context = GlobalCounter::critical_section_begin(thread); } - GlobalCounter::critical_section_end(thread); + GlobalCounter::critical_section_end(thread, cs_context); } template diff --git a/src/hotspot/share/utilities/globalCounter.cpp b/src/hotspot/share/utilities/globalCounter.cpp index 942cc0ab301..17b4334da8b 100644 --- a/src/hotspot/share/utilities/globalCounter.cpp +++ b/src/hotspot/share/utilities/globalCounter.cpp @@ -59,8 +59,8 @@ class GlobalCounter::CounterThreadCheck : public ThreadClosure { void GlobalCounter::write_synchronize() { assert((*Thread::current()->get_rcu_counter() & COUNTER_ACTIVE) == 0x0, "must be outside a critcal section"); // Atomic::add must provide fence since we have storeload dependency. - volatile uintx gbl_cnt = Atomic::add((uintx)COUNTER_INCREMENT, &_global_counter._counter, - memory_order_conservative); + uintx gbl_cnt = Atomic::add(COUNTER_INCREMENT, &_global_counter._counter); + // Handle bootstrap if (Threads::number_of_threads() == 0) { return; diff --git a/src/hotspot/share/utilities/globalCounter.hpp b/src/hotspot/share/utilities/globalCounter.hpp index 79ce172b150..ecd0a96a840 100644 --- a/src/hotspot/share/utilities/globalCounter.hpp +++ b/src/hotspot/share/utilities/globalCounter.hpp @@ -33,8 +33,9 @@ class Thread; // The GlobalCounter provides a synchronization mechanism between threads for // safe memory reclamation and other ABA problems. All readers must call // critical_section_begin before reading the volatile data and -// critical_section_end afterwards. The write side must call write_synchronize -// before reclaming the memory. The read-path only does an uncontented store +// critical_section_end afterwards. Such read-side critical sections may +// be properly nested. The write side must call write_synchronize +// before reclaming the memory. The read-path only does an uncontended store // to a thread-local-storage and fence to stop any loads from floating up, thus // light weight and wait-free. The write-side is more heavy since it must check // all readers and wait until they have left the generation. (a system memory @@ -62,20 +63,26 @@ class GlobalCounter : public AllStatic { class CounterThreadCheck; public: - // Must be called before accessing the data. Only threads accessible lock-free - // can used this. Those included now are all Threads on SMR ThreadsList and - // the VMThread. Nesting is not yet supported. - static void critical_section_begin(Thread *thread); + // The type of the critical section context passed from + // critical_section_begin() to critical_section_end(). + typedef uintx CSContext; - // Must be called after finished accessing the data. - // Do not provide fence, allows load/stores moving into the critical section. - static void critical_section_end(Thread *thread); + // Must be called before accessing the data. The result must be passed + // to the associated call to critical_section_end(). Acts as a full + // memory barrier before the code within the critical section. + static CSContext critical_section_begin(Thread *thread); + + // Must be called after finished accessing the data. The context + // must be the result of the associated initiating critical_section_begin(). + // Acts as a release memory barrier after the code within the critical + // section. + static void critical_section_end(Thread *thread, CSContext context); // Make the data inaccessible to readers before calling. When this call - // returns it's safe to reclaim the data. + // returns it's safe to reclaim the data. Acts as a full memory barrier. static void write_synchronize(); - // A scoped object for a reads-side critical-section. + // A scoped object for a read-side critical-section. class CriticalSection; }; diff --git a/src/hotspot/share/utilities/globalCounter.inline.hpp b/src/hotspot/share/utilities/globalCounter.inline.hpp index 2c4868b3513..4f664a37121 100644 --- a/src/hotspot/share/utilities/globalCounter.inline.hpp +++ b/src/hotspot/share/utilities/globalCounter.inline.hpp @@ -25,34 +25,46 @@ #ifndef SHARE_UTILITIES_GLOBAL_COUNTER_INLINE_HPP #define SHARE_UTILITIES_GLOBAL_COUNTER_INLINE_HPP +#include "runtime/atomic.hpp" #include "runtime/orderAccess.hpp" #include "runtime/thread.inline.hpp" #include "utilities/globalCounter.hpp" -inline void GlobalCounter::critical_section_begin(Thread *thread) { +inline GlobalCounter::CSContext +GlobalCounter::critical_section_begin(Thread *thread) { assert(thread == Thread::current(), "must be current thread"); - assert((*thread->get_rcu_counter() & COUNTER_ACTIVE) == 0x0, "nested critical sections, not supported yet"); - uintx gbl_cnt = OrderAccess::load_acquire(&_global_counter._counter); - OrderAccess::release_store_fence(thread->get_rcu_counter(), gbl_cnt | COUNTER_ACTIVE); + uintx old_cnt = Atomic::load(thread->get_rcu_counter()); + // Retain the old counter value if already active, e.g. nested. + // Otherwise, set the counter to the current version + active bit. + uintx new_cnt = old_cnt; + if ((new_cnt & COUNTER_ACTIVE) == 0) { + new_cnt = Atomic::load(&_global_counter._counter) | COUNTER_ACTIVE; + } + OrderAccess::release_store_fence(thread->get_rcu_counter(), new_cnt); + return static_cast(old_cnt); } -inline void GlobalCounter::critical_section_end(Thread *thread) { +inline void +GlobalCounter::critical_section_end(Thread *thread, CSContext context) { assert(thread == Thread::current(), "must be current thread"); assert((*thread->get_rcu_counter() & COUNTER_ACTIVE) == COUNTER_ACTIVE, "must be in critical section"); - // Mainly for debugging we set it to 'now'. - uintx gbl_cnt = OrderAccess::load_acquire(&_global_counter._counter); - OrderAccess::release_store(thread->get_rcu_counter(), gbl_cnt); + // Restore the counter value from before the associated begin. + OrderAccess::release_store(thread->get_rcu_counter(), + static_cast(context)); } class GlobalCounter::CriticalSection { private: Thread* _thread; + CSContext _context; public: - inline CriticalSection(Thread* thread) : _thread(thread) { - GlobalCounter::critical_section_begin(_thread); - } + inline CriticalSection(Thread* thread) : + _thread(thread), + _context(GlobalCounter::critical_section_begin(_thread)) + {} + inline ~CriticalSection() { - GlobalCounter::critical_section_end(_thread); + GlobalCounter::critical_section_end(_thread, _context); } }; diff --git a/src/hotspot/share/utilities/internalVMTests.cpp b/src/hotspot/share/utilities/internalVMTests.cpp index b324ed1aacd..854e94ada55 100644 --- a/src/hotspot/share/utilities/internalVMTests.cpp +++ b/src/hotspot/share/utilities/internalVMTests.cpp @@ -43,8 +43,6 @@ void InternalVMTests::run_test(const char* name, void (*test)()) { void InternalVMTests::run() { tty->print_cr("Running internal VM tests"); run_unit_test(TestReserveMemorySpecial_test); - run_unit_test(TestMetaspaceUtils_test); - run_unit_test(GCTimer_test); tty->print_cr("All internal VM tests passed"); } diff --git a/src/hotspot/share/utilities/singleWriterSynchronizer.hpp b/src/hotspot/share/utilities/singleWriterSynchronizer.hpp index 3877fd54c6b..faee3dce902 100644 --- a/src/hotspot/share/utilities/singleWriterSynchronizer.hpp +++ b/src/hotspot/share/utilities/singleWriterSynchronizer.hpp @@ -36,7 +36,7 @@ // Any number of threads may enter critical sections associated with a // synchronizer object. One (at a time) other thread may wait for the // completion of all critical sections for the synchronizer object -// that were extent when the wait was initiated. Usage is that there +// that were extant when the wait was initiated. Usage is that there // is some state that can be accessed either before or after some // change. An accessing thread performs the access within a critical // section. A writer thread performs the state change, and then waits @@ -46,9 +46,7 @@ // Generally, GlobalCounter should be used instead of this class, as // GlobalCounter has measurably better performance and doesn't have // the single writer at a time restriction. Use this only in -// situations where GlobalCounter won't work for some reason, such as -// nesting. But note that nesting often indicates other problems, and -// may risk deadlock. +// situations where GlobalCounter won't work for some reason. class SingleWriterSynchronizer { volatile uint _enter; volatile uint _exit[2]; diff --git a/src/hotspot/share/utilities/ticks.hpp b/src/hotspot/share/utilities/ticks.hpp index 81deb7094f5..6e03d6887a8 100644 --- a/src/hotspot/share/utilities/ticks.hpp +++ b/src/hotspot/share/utilities/ticks.hpp @@ -233,7 +233,7 @@ class TimeInstant : public Rep { TimeInstant(jlong ticks) : Rep(ticks) {} friend class GranularTimer; friend class ObjectSample; - // GC VM tests + // GC unit tests friend class TimePartitionPhasesIteratorTest; friend class GCTimerTest; }; diff --git a/src/java.base/share/classes/java/lang/StringConcatHelper.java b/src/java.base/share/classes/java/lang/StringConcatHelper.java index cae184701e1..242d54af4ce 100644 --- a/src/java.base/share/classes/java/lang/StringConcatHelper.java +++ b/src/java.base/share/classes/java/lang/StringConcatHelper.java @@ -37,119 +37,104 @@ final class StringConcatHelper { } /** - * Check for overflow, throw the exception on overflow. - * @param len String length - * @return length + * Check for overflow, throw exception on overflow. + * @param lengthCoder String length and coder + * @return lengthCoder */ - private static int checkOverflow(int len) { - if (len < 0) { - throw new OutOfMemoryError("Overflow: String length out of range"); + private static long checkOverflow(long lengthCoder) { + if ((int)lengthCoder >= 0) { + return lengthCoder; } - return len; + throw new OutOfMemoryError("Overflow: String length out of range"); } /** - * Mix value length into current length + * Mix value length and coder into current length and coder. * @param current current length * @param value value to mix in - * @return new length + * @return new length and coder */ - static int mixLen(int current, boolean value) { + static long mix(long current, boolean value) { return checkOverflow(current + (value ? 4 : 5)); } /** - * Mix value length into current length + * Mix value length and coder into current length and coder. * @param current current length * @param value value to mix in - * @return new length + * @return new length and coder */ - static int mixLen(int current, byte value) { - return mixLen(current, (int)value); + static long mix(long current, byte value) { + return mix(current, (int)value); } /** - * Mix value length into current length + * Mix value length and coder into current length and coder. * @param current current length * @param value value to mix in - * @return new length + * @return new length and coder */ - static int mixLen(int current, char value) { - return checkOverflow(current + 1); + static long mix(long current, char value) { + return checkOverflow(current + 1) | (StringLatin1.canEncode(value) ? 0 : UTF16); } /** - * Mix value length into current length + * Mix value length and coder into current length and coder. * @param current current length * @param value value to mix in - * @return new length + * @return new length and coder */ - static int mixLen(int current, short value) { - return mixLen(current, (int)value); + static long mix(long current, short value) { + return mix(current, (int)value); } /** - * Mix value length into current length + * Mix value length and coder into current length and coder. * @param current current length * @param value value to mix in - * @return new length + * @return new length and coder */ - static int mixLen(int current, int value) { + static long mix(long current, int value) { return checkOverflow(current + Integer.stringSize(value)); } /** - * Mix value length into current length + * Mix value length and coder into current length and coder. * @param current current length * @param value value to mix in - * @return new length + * @return new length and coder */ - static int mixLen(int current, long value) { + static long mix(long current, long value) { return checkOverflow(current + Long.stringSize(value)); } /** - * Mix value length into current length + * Mix value length and coder into current length and coder. * @param current current length * @param value value to mix in - * @return new length + * @return new length and coder */ - static int mixLen(int current, String value) { - return checkOverflow(current + value.length()); - } - - /** - * Mix coder into current coder - * @param current current coder - * @param value value to mix in - * @return new coder - */ - static byte mixCoder(byte current, char value) { - return (byte)(current | (StringLatin1.canEncode(value) ? 0 : 1)); - } - - /** - * Mix coder into current coder - * @param current current coder - * @param value value to mix in - * @return new coder - */ - static byte mixCoder(byte current, String value) { - return (byte)(current | value.coder()); + static long mix(long current, String value) { + current += value.length(); + if (value.coder() == String.UTF16) { + current |= UTF16; + } + return checkOverflow(current); } /** * Prepends the stringly representation of boolean value into buffer, * given the coder and final index. Index is measured in chars, not in bytes! * - * @param index final char index in the buffer - * @param buf buffer to append to - * @param coder coder to add with - * @param value boolean value to encode - * @return new index + * @param indexCoder final char index in the buffer, along with coder packed + * into higher bits. + * @param buf buffer to append to + * @param value boolean value to encode + * @return updated index (coder value retained) */ - static int prepend(int index, byte[] buf, byte coder, boolean value) { - if (coder == String.LATIN1) { + static long prepend(long indexCoder, byte[] buf, boolean value) { + int index = (int)indexCoder; + if (indexCoder < UTF16) { if (value) { buf[--index] = 'e'; buf[--index] = 'u'; @@ -162,6 +147,7 @@ final class StringConcatHelper { buf[--index] = 'a'; buf[--index] = 'f'; } + return index; } else { if (value) { StringUTF16.putChar(buf, --index, 'e'); @@ -175,72 +161,72 @@ final class StringConcatHelper { StringUTF16.putChar(buf, --index, 'a'); StringUTF16.putChar(buf, --index, 'f'); } + return index | UTF16; } - return index; } /** * Prepends the stringly representation of byte value into buffer, * given the coder and final index. Index is measured in chars, not in bytes! * - * @param index final char index in the buffer - * @param buf buffer to append to - * @param coder coder to add with - * @param value byte value to encode - * @return new index + * @param indexCoder final char index in the buffer, along with coder packed + * into higher bits. + * @param buf buffer to append to + * @param value byte value to encode + * @return updated index (coder value retained) */ - static int prepend(int index, byte[] buf, byte coder, byte value) { - return prepend(index, buf, coder, (int)value); + static long prepend(long indexCoder, byte[] buf, byte value) { + return prepend(indexCoder, buf, (int)value); } /** * Prepends the stringly representation of char value into buffer, * given the coder and final index. Index is measured in chars, not in bytes! * - * @param index final char index in the buffer - * @param buf buffer to append to - * @param coder coder to add with - * @param value char value to encode - * @return new index + * @param indexCoder final char index in the buffer, along with coder packed + * into higher bits. + * @param buf buffer to append to + * @param value char value to encode + * @return updated index (coder value retained) */ - static int prepend(int index, byte[] buf, byte coder, char value) { - if (coder == String.LATIN1) { - buf[--index] = (byte) (value & 0xFF); + static long prepend(long indexCoder, byte[] buf, char value) { + if (indexCoder < UTF16) { + buf[(int)(--indexCoder)] = (byte) (value & 0xFF); } else { - StringUTF16.putChar(buf, --index, value); + StringUTF16.putChar(buf, (int)(--indexCoder), value); } - return index; + return indexCoder; } /** * Prepends the stringly representation of short value into buffer, * given the coder and final index. Index is measured in chars, not in bytes! * - * @param index final char index in the buffer - * @param buf buffer to append to - * @param coder coder to add with - * @param value short value to encode - * @return new index + * @param indexCoder final char index in the buffer, along with coder packed + * into higher bits. + * @param buf buffer to append to + * @param value short value to encode + * @return updated index (coder value retained) */ - static int prepend(int index, byte[] buf, byte coder, short value) { - return prepend(index, buf, coder, (int)value); + static long prepend(long indexCoder, byte[] buf, short value) { + return prepend(indexCoder, buf, (int)value); } /** * Prepends the stringly representation of integer value into buffer, * given the coder and final index. Index is measured in chars, not in bytes! * - * @param index final char index in the buffer - * @param buf buffer to append to - * @param coder coder to add with - * @param value integer value to encode - * @return new index + * @param indexCoder final char index in the buffer, along with coder packed + * into higher bits. + * @param buf buffer to append to + * @param value integer value to encode + * @return updated index (coder value retained) */ - static int prepend(int index, byte[] buf, byte coder, int value) { - if (coder == String.LATIN1) { - return Integer.getChars(value, index, buf); + static long prepend(long indexCoder, byte[] buf, int value) { + if (indexCoder < UTF16) { + return Integer.getChars(value, (int)indexCoder, buf); } else { - return StringUTF16.getChars(value, index, buf); + return StringUTF16.getChars(value, (int)indexCoder, buf) | UTF16; } } @@ -248,17 +234,17 @@ final class StringConcatHelper { * Prepends the stringly representation of long value into buffer, * given the coder and final index. Index is measured in chars, not in bytes! * - * @param index final char index in the buffer - * @param buf buffer to append to - * @param coder coder to add with - * @param value long value to encode - * @return new index + * @param indexCoder final char index in the buffer, along with coder packed + * into higher bits. + * @param buf buffer to append to + * @param value long value to encode + * @return updated index (coder value retained) */ - static int prepend(int index, byte[] buf, byte coder, long value) { - if (coder == String.LATIN1) { - return Long.getChars(value, index, buf); + static long prepend(long indexCoder, byte[] buf, long value) { + if (indexCoder < UTF16) { + return Long.getChars(value, (int)indexCoder, buf); } else { - return StringUTF16.getChars(value, index, buf); + return StringUTF16.getChars(value, (int)indexCoder, buf) | UTF16; } } @@ -266,39 +252,49 @@ final class StringConcatHelper { * Prepends the stringly representation of String value into buffer, * given the coder and final index. Index is measured in chars, not in bytes! * - * @param index final char index in the buffer - * @param buf buffer to append to - * @param coder coder to add with - * @param value String value to encode - * @return new index + * @param indexCoder final char index in the buffer, along with coder packed + * into higher bits. + * @param buf buffer to append to + * @param value String value to encode + * @return updated index (coder value retained) */ - static int prepend(int index, byte[] buf, byte coder, String value) { - index -= value.length(); - value.getBytes(buf, index, coder); - return index; + static long prepend(long indexCoder, byte[] buf, String value) { + indexCoder -= value.length(); + if (indexCoder < UTF16) { + value.getBytes(buf, (int)indexCoder, String.LATIN1); + } else { + value.getBytes(buf, (int)indexCoder, String.UTF16); + } + return indexCoder; } /** * Instantiates the String with given buffer and coder - * @param buf buffer to use - * @param index remaining index - * @param coder coder to use - * @return String resulting string + * @param buf buffer to use + * @param indexCoder remaining index (should be zero) and coder + * @return String resulting string */ - static String newString(byte[] buf, int index, byte coder) { + static String newString(byte[] buf, long indexCoder) { // Use the private, non-copying constructor (unsafe!) - if (index != 0) { - throw new InternalError("Storage is not completely initialized, " + index + " bytes left"); + if (indexCoder == LATIN1) { + return new String(buf, String.LATIN1); + } else if (indexCoder == UTF16) { + return new String(buf, String.UTF16); + } else { + throw new InternalError("Storage is not completely initialized, " + (int)indexCoder + " bytes left"); } - return new String(buf, coder); } + private static final long LATIN1 = (long)String.LATIN1 << 32; + + private static final long UTF16 = (long)String.UTF16 << 32; + /** * Provides the initial coder for the String. - * @return initial coder + * @return initial coder, adjusted into the upper half */ - static byte initialCoder() { - return String.COMPACT_STRINGS ? String.LATIN1 : String.UTF16; + static long initialCoder() { + return String.COMPACT_STRINGS ? LATIN1 : UTF16; } } diff --git a/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java b/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java index ddf3de7c00b..7eb90b77140 100644 --- a/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java +++ b/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java @@ -317,6 +317,13 @@ public final class StringConcatFactory { return elements.equals(recipe.elements); } + @Override + public String toString() { + return "Recipe{" + + "elements=" + elements + + '}'; + } + @Override public int hashCode() { return elements.hashCode(); @@ -367,6 +374,15 @@ public final class StringConcatFactory { return true; } + @Override + public String toString() { + return "RecipeElement{" + + "value='" + value + '\'' + + ", argPos=" + argPos + + ", tag=" + tag + + '}'; + } + @Override public int hashCode() { return (int)tag; @@ -1520,24 +1536,24 @@ public final class StringConcatFactory { } // Start building the combinator tree. The tree "starts" with ()String, and "finishes" - // with the (int, byte[], byte)String in String helper. The combinators are assembled bottom-up, - // which makes the code arguably hard to read. + // with the (byte[], long)String shape to invoke newString in StringConcatHelper. The combinators are + // assembled bottom-up, which makes the code arguably hard to read. // Drop all remaining parameter types, leave only helper arguments: MethodHandle mh; - mh = MethodHandles.dropArguments(NEW_STRING, 3, ptypes); + mh = MethodHandles.dropArguments(NEW_STRING, 2, ptypes); - // Mix in prependers. This happens when (byte[], int, byte) = (storage, index, coder) is already - // known from the combinators below. We are assembling the string backwards, so "index" is the - // *ending* index. + // Mix in prependers. This happens when (byte[], long) = (storage, indexCoder) is already + // known from the combinators below. We are assembling the string backwards, so the index coded + // into indexCoder is the *ending* index. for (RecipeElement el : recipe.getElements()) { // Do the prepend, and put "new" index at index 1 switch (el.getTag()) { case TAG_CONST: { - MethodHandle prepender = MethodHandles.insertArguments(prepender(String.class), 3, el.getValue()); + MethodHandle prepender = MethodHandles.insertArguments(prepender(String.class), 2, el.getValue()); mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, prepender, - 1, 0, 2 // index, storage, coder + 1, 0 // indexCoder, storage ); break; } @@ -1545,8 +1561,8 @@ public final class StringConcatFactory { int pos = el.getArgPos(); MethodHandle prepender = prepender(ptypes[pos]); mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, prepender, - 1, 0, 2, // index, storage, coder - 3 + pos // selected argument + 1, 0, // indexCoder, storage + 2 + pos // selected argument ); break; } @@ -1557,7 +1573,7 @@ public final class StringConcatFactory { // Fold in byte[] instantiation at argument 0 mh = MethodHandles.foldArgumentsWithCombiner(mh, 0, NEW_ARRAY, - 1, 2 // index, coder + 1 // index ); // Start combining length and coder mixers. @@ -1569,47 +1585,28 @@ public final class StringConcatFactory { // Coders are more interesting. Only Object, String and char arguments (and constants) // can have non-Latin1 encoding. It is easier to blindly convert constants to String, // and deduce the coder from there. Arguments would be either converted to Strings - // during the initial filtering, or handled by primitive specializations in CODER_MIXERS. + // during the initial filtering, or handled by specializations in MIXERS. // - // The method handle shape before and after all length and coder mixers is: - // (int, byte, )String = ("index", "coder", ) - byte initialCoder = INITIAL_CODER; - int initialLen = 0; // initial length, in characters + // The method handle shape before and after all mixers are combined in is: + // (long, )String = ("indexCoder", ) + long initialLengthCoder = INITIAL_CODER; for (RecipeElement el : recipe.getElements()) { switch (el.getTag()) { case TAG_CONST: String constant = el.getValue(); - initialCoder = (byte) coderMixer(String.class).invoke(initialCoder, constant); - initialLen += constant.length(); + initialLengthCoder = (long)mixer(String.class).invoke(initialLengthCoder, constant); break; case TAG_ARG: int ac = el.getArgPos(); Class argClass = ptypes[ac]; - MethodHandle lm = lengthMixer(argClass); + MethodHandle mix = mixer(argClass); - if (argClass.isPrimitive() && argClass != char.class) { - // Compute new "index" in-place using old value plus the appropriate argument. - mh = MethodHandles.filterArgumentsWithCombiner(mh, 0, lm, - 0, // old-index - 2 + ac // selected argument - ); - - } else { - MethodHandle cm = coderMixer(argClass); - - // Compute new "index" in-place using old value plus the appropriate argument. - mh = MethodHandles.filterArgumentsWithCombiner(mh, 0, lm, - 0, // old-index - 2 + ac // selected argument - ); - - // Compute new "coder" in-place using old value plus the appropriate argument. - mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, cm, - 1, // old-coder - 2 + ac // selected argument - ); - } + // Compute new "index" in-place using old value plus the appropriate argument. + mh = MethodHandles.filterArgumentsWithCombiner(mh, 0, mix, + 0, // old-index + 1 + ac // selected argument + ); break; default: @@ -1617,9 +1614,9 @@ public final class StringConcatFactory { } } - // Insert initial lengths and coders here. + // Insert initial length and coder value here. // The method handle shape here is (). - mh = MethodHandles.insertArguments(mh, 0, initialLen, initialCoder); + mh = MethodHandles.insertArguments(mh, 0, initialLengthCoder); // Apply filters, converting the arguments: if (filters != null) { @@ -1630,45 +1627,34 @@ public final class StringConcatFactory { } @ForceInline - private static byte[] newArray(int length, byte coder) { - return (byte[]) UNSAFE.allocateUninitializedArray(byte.class, length << coder); + private static byte[] newArray(long indexCoder) { + byte coder = (byte)(indexCoder >> 32); + int index = ((int)indexCoder & 0x7FFFFFFF); + return (byte[]) UNSAFE.allocateUninitializedArray(byte.class, index << coder); } private static MethodHandle prepender(Class cl) { return PREPENDERS.computeIfAbsent(cl, PREPEND); } - private static MethodHandle coderMixer(Class cl) { - return CODER_MIXERS.computeIfAbsent(cl, CODER_MIX); - } - - private static MethodHandle lengthMixer(Class cl) { - return LENGTH_MIXERS.computeIfAbsent(cl, LENGTH_MIX); + private static MethodHandle mixer(Class cl) { + return MIXERS.computeIfAbsent(cl, MIX); } // This one is deliberately non-lambdified to optimize startup time: private static final Function, MethodHandle> PREPEND = new Function, MethodHandle>() { @Override public MethodHandle apply(Class c) { - return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "prepend", int.class, int.class, byte[].class, byte.class, + return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "prepend", long.class, long.class, byte[].class, Wrapper.asPrimitiveType(c)); } }; // This one is deliberately non-lambdified to optimize startup time: - private static final Function, MethodHandle> CODER_MIX = new Function, MethodHandle>() { + private static final Function, MethodHandle> MIX = new Function, MethodHandle>() { @Override public MethodHandle apply(Class c) { - return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mixCoder", byte.class, byte.class, - Wrapper.asPrimitiveType(c)); - } - }; - - // This one is deliberately non-lambdified to optimize startup time: - private static final Function, MethodHandle> LENGTH_MIX = new Function, MethodHandle>() { - @Override - public MethodHandle apply(Class c) { - return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mixLen", int.class, int.class, + return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mix", long.class, long.class, Wrapper.asPrimitiveType(c)); } }; @@ -1676,26 +1662,24 @@ public final class StringConcatFactory { private static final MethodHandle NEW_STRING; private static final MethodHandle NEW_ARRAY; private static final ConcurrentMap, MethodHandle> PREPENDERS; - private static final ConcurrentMap, MethodHandle> LENGTH_MIXERS; - private static final ConcurrentMap, MethodHandle> CODER_MIXERS; - private static final byte INITIAL_CODER; + private static final ConcurrentMap, MethodHandle> MIXERS; + private static final long INITIAL_CODER; static final Class STRING_HELPER; static { try { STRING_HELPER = Class.forName("java.lang.StringConcatHelper"); - MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", byte.class); - INITIAL_CODER = (byte) initCoder.invoke(); + MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", long.class); + INITIAL_CODER = (long) initCoder.invoke(); } catch (Throwable e) { throw new AssertionError(e); } PREPENDERS = new ConcurrentHashMap<>(); - LENGTH_MIXERS = new ConcurrentHashMap<>(); - CODER_MIXERS = new ConcurrentHashMap<>(); + MIXERS = new ConcurrentHashMap<>(); - NEW_STRING = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newString", String.class, byte[].class, int.class, byte.class); - NEW_ARRAY = lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleInlineCopyStrategy.class, "newArray", byte[].class, int.class, byte.class); + NEW_STRING = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newString", String.class, byte[].class, long.class); + NEW_ARRAY = lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleInlineCopyStrategy.class, "newArray", byte[].class, long.class); } } diff --git a/src/java.base/share/classes/java/time/chrono/JapaneseEra.java b/src/java.base/share/classes/java/time/chrono/JapaneseEra.java index a9bbc5e276e..d4cc65d3f24 100644 --- a/src/java.base/share/classes/java/time/chrono/JapaneseEra.java +++ b/src/java.base/share/classes/java/time/chrono/JapaneseEra.java @@ -92,6 +92,11 @@ import sun.util.calendar.CalendarDate; * Japan introduced the Gregorian calendar starting with Meiji 6. * Only Meiji and later eras are supported; * dates before Meiji 6, January 1 are not supported. + * The number of the valid eras may increase, as new eras may be + * defined by the Japanese government. Once an era is defined, + * subsequent versions of this class will add a singleton instance + * for it. The defined era is expected to have a consecutive integer + * associated with it. * * @implSpec * This class is immutable and thread-safe. @@ -195,9 +200,13 @@ public final class JapaneseEra /** * Obtains an instance of {@code JapaneseEra} from an {@code int} value. *

- * The {@link #SHOWA} era that contains 1970-01-01 (ISO calendar system) has the value 1 + * The {@link #SHOWA} era that contains 1970-01-01 (ISO calendar system) has the value 1. * Later era is numbered 2 ({@link #HEISEI}). Earlier eras are numbered 0 ({@link #TAISHO}), * -1 ({@link #MEIJI}), only Meiji and later eras are supported. + *

+ * In addition to the known era singletons, values for additional + * eras may be defined. Those values are the {@link Era#getValue()} + * of corresponding eras from the {@link #values()} method. * * @param japaneseEra the era to represent * @return the {@code JapaneseEra} singleton, not null @@ -216,6 +225,8 @@ public final class JapaneseEra *

* The string must match exactly the name of the era. * (Extraneous whitespace characters are not permitted.) + *

+ * Valid era names are the names of eras returned from {@link #values()}. * * @param japaneseEra the japaneseEra name; non-null * @return the {@code JapaneseEra} singleton, never null @@ -232,7 +243,9 @@ public final class JapaneseEra } /** - * Returns an array of JapaneseEras. + * Returns an array of JapaneseEras. The array may contain eras defined + * by the Japanese government beyond the known era singletons. + * *

* This method may be used to iterate over the JapaneseEras as follows: *

diff --git a/src/java.base/share/classes/javax/crypto/KeyAgreement.java b/src/java.base/share/classes/javax/crypto/KeyAgreement.java
index 3f77eddccc8..96d78d0d32c 100644
--- a/src/java.base/share/classes/javax/crypto/KeyAgreement.java
+++ b/src/java.base/share/classes/javax/crypto/KeyAgreement.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -582,16 +582,22 @@ public class KeyAgreement {
     /**
      * Generates the shared secret and returns it in a new buffer.
      *
-     * 

This method resets this {@code KeyAgreement} object, so that it - * can be reused for further key agreements. Unless this key agreement is - * reinitialized with one of the {@code init} methods, the same - * private information and algorithm parameters will be used for - * subsequent key agreements. + *

This method resets this {@code KeyAgreement} object to the state that + * it was in after the most recent call to one of the {@code init} methods. + * After a call to {@code generateSecret}, the object can be reused for + * further key agreement operations by calling {@code doPhase} to supply + * new keys, and then calling {@code generateSecret} to produce a new + * secret. In this case, the private information and algorithm parameters + * supplied to {@code init} will be used for multiple key agreement + * operations. The {@code init} method can be called after + * {@code generateSecret} to change the private information used in + * subsequent operations. * * @return the new buffer with the shared secret * * @exception IllegalStateException if this key agreement has not been - * completed yet + * initialized or if {@code doPhase} has not been called to supply the + * keys for all parties in the agreement */ public final byte[] generateSecret() throws IllegalStateException { chooseFirstProvider(); @@ -606,11 +612,16 @@ public class KeyAgreement { * result, a {@code ShortBufferException} is thrown. * In this case, this call should be repeated with a larger output buffer. * - *

This method resets this {@code KeyAgreement} object, so that it - * can be reused for further key agreements. Unless this key agreement is - * reinitialized with one of the {@code init} methods, the same - * private information and algorithm parameters will be used for - * subsequent key agreements. + *

This method resets this {@code KeyAgreement} object to the state that + * it was in after the most recent call to one of the {@code init} methods. + * After a call to {@code generateSecret}, the object can be reused for + * further key agreement operations by calling {@code doPhase} to supply + * new keys, and then calling {@code generateSecret} to produce a new + * secret. In this case, the private information and algorithm parameters + * supplied to {@code init} will be used for multiple key agreement + * operations. The {@code init} method can be called after + * {@code generateSecret} to change the private information used in + * subsequent operations. * * @param sharedSecret the buffer for the shared secret * @param offset the offset in {@code sharedSecret} where the @@ -619,7 +630,8 @@ public class KeyAgreement { * @return the number of bytes placed into {@code sharedSecret} * * @exception IllegalStateException if this key agreement has not been - * completed yet + * initialized or if {@code doPhase} has not been called to supply the + * keys for all parties in the agreement * @exception ShortBufferException if the given output buffer is too small * to hold the secret */ @@ -634,18 +646,24 @@ public class KeyAgreement { * Creates the shared secret and returns it as a {@code SecretKey} * object of the specified algorithm. * - *

This method resets this {@code KeyAgreement} object, so that it - * can be reused for further key agreements. Unless this key agreement is - * reinitialized with one of the {@code init} methods, the same - * private information and algorithm parameters will be used for - * subsequent key agreements. + *

This method resets this {@code KeyAgreement} object to the state that + * it was in after the most recent call to one of the {@code init} methods. + * After a call to {@code generateSecret}, the object can be reused for + * further key agreement operations by calling {@code doPhase} to supply + * new keys, and then calling {@code generateSecret} to produce a new + * secret. In this case, the private information and algorithm parameters + * supplied to {@code init} will be used for multiple key agreement + * operations. The {@code init} method can be called after + * {@code generateSecret} to change the private information used in + * subsequent operations. * * @param algorithm the requested secret-key algorithm * * @return the shared secret key * * @exception IllegalStateException if this key agreement has not been - * completed yet + * initialized or if {@code doPhase} has not been called to supply the + * keys for all parties in the agreement * @exception NoSuchAlgorithmException if the specified secret-key * algorithm is not available * @exception InvalidKeyException if the shared secret-key material cannot diff --git a/src/java.base/share/classes/javax/crypto/KeyAgreementSpi.java b/src/java.base/share/classes/javax/crypto/KeyAgreementSpi.java index 51c8307438b..db8c05b379a 100644 --- a/src/java.base/share/classes/javax/crypto/KeyAgreementSpi.java +++ b/src/java.base/share/classes/javax/crypto/KeyAgreementSpi.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -130,17 +130,22 @@ public abstract class KeyAgreementSpi { /** * Generates the shared secret and returns it in a new buffer. * - *

This method resets this KeyAgreementSpi object, - * so that it - * can be reused for further key agreements. Unless this key agreement is - * reinitialized with one of the engineInit methods, the same - * private information and algorithm parameters will be used for - * subsequent key agreements. + *

This method resets this {@code KeyAgreementSpi} object to the state + * that it was in after the most recent call to one of the {@code init} + * methods. After a call to {@code generateSecret}, the object can be reused + * for further key agreement operations by calling {@code doPhase} to supply + * new keys, and then calling {@code generateSecret} to produce a new + * secret. In this case, the private information and algorithm parameters + * supplied to {@code init} will be used for multiple key agreement + * operations. The {@code init} method can be called after + * {@code generateSecret} to change the private information used in + * subsequent operations. * * @return the new buffer with the shared secret * * @exception IllegalStateException if this key agreement has not been - * completed yet + * initialized or if {@code doPhase} has not been called to supply the + * keys for all parties in the agreement */ protected abstract byte[] engineGenerateSecret() throws IllegalStateException; @@ -153,12 +158,16 @@ public abstract class KeyAgreementSpi { * result, a ShortBufferException is thrown. * In this case, this call should be repeated with a larger output buffer. * - *

This method resets this KeyAgreementSpi object, - * so that it - * can be reused for further key agreements. Unless this key agreement is - * reinitialized with one of the engineInit methods, the same - * private information and algorithm parameters will be used for - * subsequent key agreements. + *

This method resets this {@code KeyAgreementSpi} object to the state + * that it was in after the most recent call to one of the {@code init} + * methods. After a call to {@code generateSecret}, the object can be reused + * for further key agreement operations by calling {@code doPhase} to supply + * new keys, and then calling {@code generateSecret} to produce a new + * secret. In this case, the private information and algorithm parameters + * supplied to {@code init} will be used for multiple key agreement + * operations. The {@code init} method can be called after + * {@code generateSecret} to change the private information used in + * subsequent operations. * * @param sharedSecret the buffer for the shared secret * @param offset the offset in sharedSecret where the @@ -167,7 +176,8 @@ public abstract class KeyAgreementSpi { * @return the number of bytes placed into sharedSecret * * @exception IllegalStateException if this key agreement has not been - * completed yet + * initialized or if {@code doPhase} has not been called to supply the + * keys for all parties in the agreement * @exception ShortBufferException if the given output buffer is too small * to hold the secret */ @@ -179,19 +189,24 @@ public abstract class KeyAgreementSpi { * Creates the shared secret and returns it as a secret key object * of the requested algorithm type. * - *

This method resets this KeyAgreementSpi object, - * so that it - * can be reused for further key agreements. Unless this key agreement is - * reinitialized with one of the engineInit methods, the same - * private information and algorithm parameters will be used for - * subsequent key agreements. + *

This method resets this {@code KeyAgreementSpi} object to the state + * that it was in after the most recent call to one of the {@code init} + * methods. After a call to {@code generateSecret}, the object can be reused + * for further key agreement operations by calling {@code doPhase} to supply + * new keys, and then calling {@code generateSecret} to produce a new + * secret. In this case, the private information and algorithm parameters + * supplied to {@code init} will be used for multiple key agreement + * operations. The {@code init} method can be called after + * {@code generateSecret} to change the private information used in + * subsequent operations. * * @param algorithm the requested secret key algorithm * * @return the shared secret key * * @exception IllegalStateException if this key agreement has not been - * completed yet + * initialized or if {@code doPhase} has not been called to supply the + * keys for all parties in the agreement * @exception NoSuchAlgorithmException if the requested secret key * algorithm is not available * @exception InvalidKeyException if the shared secret key material cannot diff --git a/src/java.base/share/classes/jdk/internal/event/Event.java b/src/java.base/share/classes/jdk/internal/event/Event.java new file mode 100644 index 00000000000..0ca2840f47a --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/event/Event.java @@ -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. + *

+ * 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) { + } +} diff --git a/src/java.base/share/classes/module-info.java b/src/java.base/share/classes/module-info.java index bdd0b6cc1d8..ba1c0d724dd 100644 --- a/src/java.base/share/classes/module-info.java +++ b/src/java.base/share/classes/module-info.java @@ -136,6 +136,8 @@ module java.base { java.security.sasl; exports jdk.internal to jdk.jfr; + exports jdk.internal.event to + jdk.jfr; exports jdk.internal.jimage to jdk.jlink; exports jdk.internal.jimage.decompressor to diff --git a/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java b/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java index 4d4e6c21cd5..3ca5cb87806 100644 --- a/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java +++ b/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java @@ -63,7 +63,7 @@ enum SignatureScheme { "EC", NamedGroup.SECP384_R1, ProtocolVersion.PROTOCOLS_TO_13), - ECDSA_SECP512R1_SHA512 (0x0603, "ecdsa_secp512r1_sha512", + ECDSA_SECP521R1_SHA512 (0x0603, "ecdsa_secp521r1_sha512", "SHA512withECDSA", "EC", NamedGroup.SECP521_R1, diff --git a/src/java.base/unix/native/jspawnhelper/jspawnhelper.c b/src/java.base/unix/native/jspawnhelper/jspawnhelper.c index e522b0cf73a..13882017163 100644 --- a/src/java.base/unix/native/jspawnhelper/jspawnhelper.c +++ b/src/java.base/unix/native/jspawnhelper/jspawnhelper.c @@ -49,7 +49,10 @@ extern int errno; #define ERR_ARGS 3 void error (int fd, int err) { - write (fd, &err, sizeof(err)); + if (write (fd, &err, sizeof(err)) != sizeof(err)) { + /* Not sure what to do here. I have no one to speak to. */ + exit(0x80 + err); + } exit (1); } diff --git a/src/java.xml/share/classes/javax/xml/validation/package-info.java b/src/java.xml/share/classes/javax/xml/validation/package-info.java index b4fb61b26ae..d7a71255cc2 100644 --- a/src/java.xml/share/classes/javax/xml/validation/package-info.java +++ b/src/java.xml/share/classes/javax/xml/validation/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -53,13 +53,13 @@ * and an ISO (International Organization * for Standardization) standard. * - *

  • Schematron - + *
  • Schematron - * a rules-based XML schema language. Whereas DTD, WXS, and RNG are designed * to express the structure of a content model, Schematron is designed to * enforce individual rules that are difficult or impossible to express * with other schema languages. Schematron is intended to supplement a * schema written in structural schema language such as the aforementioned. - * Schematron is in the process of becoming an ISO standard. + * Schematron is an ISO standard. *
  • * *

    diff --git a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDHKeyAgreement.java b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDHKeyAgreement.java index fbb2838f628..23d117d6946 100644 --- a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDHKeyAgreement.java +++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDHKeyAgreement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -127,7 +127,9 @@ public final class ECDHKeyAgreement extends KeyAgreementSpi { try { - return deriveKey(s, publicValue, encodedParams); + byte[] result = deriveKey(s, publicValue, encodedParams); + publicValue = null; + return result; } catch (GeneralSecurityException e) { throw new ProviderException("Could not derive key", e); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/ValueTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/ValueTaglet.java index 5b72d224334..41f340d8054 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/ValueTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/ValueTaglet.java @@ -61,9 +61,7 @@ public class ValueTaglet extends BaseTaglet { * Construct a new ValueTaglet. */ public ValueTaglet() { - super(VALUE.tagName, true, - EnumSet.of(Site.OVERVIEW, Site.PACKAGE, Site.TYPE, Site.CONSTRUCTOR, - Site.METHOD, Site.FIELD)); // not Site.MODULE at this time! + super(VALUE.tagName, true, EnumSet.allOf(Site.class)); } /** diff --git a/src/jdk.jfr/share/classes/jdk/jfr/Event.java b/src/jdk.jfr/share/classes/jdk/jfr/Event.java index d3f8c8bdb26..9a6ddaa02f4 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/Event.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/Event.java @@ -88,7 +88,7 @@ package jdk.jfr; @Enabled(true) @StackTrace(true) @Registered(true) -abstract public class Event { +abstract public class Event extends jdk.internal.event.Event { /** * Sole constructor, for invocation by subclass constructors, typically * implicit. diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java index 7a2098ab046..2ead6c8c565 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java @@ -41,7 +41,6 @@ import java.util.Set; import jdk.internal.module.Modules; import jdk.jfr.AnnotationElement; import jdk.jfr.Enabled; -import jdk.jfr.Event; import jdk.jfr.Name; import jdk.jfr.Period; import jdk.jfr.SettingControl; @@ -109,7 +108,7 @@ public final class EventControl { } } - EventControl(PlatformEventType es, Class eventClass) { + EventControl(PlatformEventType es, Class eventClass) { this(es); defineSettings(eventClass); } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventHandlerCreator.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventHandlerCreator.java index 26b9524137b..9b0d6f77678 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventHandlerCreator.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventHandlerCreator.java @@ -93,11 +93,11 @@ final class EventHandlerCreator { return EventHandler.class.getName() + id + SUFFIX; } - public EventHandlerCreator(long id, List settingInfos, EventType type, Class eventClass) { + public EventHandlerCreator(long id, List settingInfos, EventType type, Class eventClass) { this(id, settingInfos, createFieldInfos(eventClass, type)); } - private static List createFieldInfos(Class eventClass, EventType type) throws Error { + private static List createFieldInfos(Class eventClass, EventType type) throws Error { List fieldInfos = new ArrayList<>(); for (ValueDescriptor v : type.getFields()) { // Only value descriptors that are not fields on the event class. diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventInstrumentation.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventInstrumentation.java index 8b365458716..3d04758c01c 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventInstrumentation.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventInstrumentation.java @@ -102,6 +102,7 @@ public final class EventInstrumentation { private static final Type ANNOTATION_TYPE_ENABLED = Type.getType(Enabled.class); private static final Type TYPE_EVENT_HANDLER = Type.getType(EventHandler.class); private static final Type TYPE_SETTING_CONTROL = Type.getType(SettingControl.class); + private static final Type TYPE_OBJECT = Type.getType(Object.class); private static final Method METHOD_COMMIT = new Method("commit", Type.VOID_TYPE, new Type[0]); private static final Method METHOD_BEGIN = new Method("begin", Type.VOID_TYPE, new Type[0]); private static final Method METHOD_END = new Method("end", Type.VOID_TYPE, new Type[0]); @@ -117,6 +118,7 @@ public final class EventInstrumentation { private final Method writeMethod; private final String eventHandlerXInternalName; private final String eventName; + private final boolean untypedEventHandler; private boolean guardHandlerReference; private Class superClass; @@ -125,11 +127,20 @@ public final class EventInstrumentation { this.classNode = createClassNode(bytes); this.settingInfos = buildSettingInfos(superClass, classNode); this.fieldInfos = buildFieldInfos(superClass, classNode); + this.untypedEventHandler = hasUntypedHandler(); this.writeMethod = makeWriteMethod(fieldInfos); this.eventHandlerXInternalName = ASMToolkit.getInternalName(EventHandlerCreator.makeEventHandlerName(id)); String n = annotationValue(classNode, ANNOTATION_TYPE_NAME.getDescriptor(), String.class); this.eventName = n == null ? classNode.name.replace("/", ".") : n; + } + private boolean hasUntypedHandler() { + for (FieldNode field : classNode.fields) { + if (FIELD_EVENT_HANDLER.equals(field.name)) { + return field.desc.equals(TYPE_OBJECT.getDescriptor()); + } + } + throw new InternalError("Class missing handler field"); } public String getClassName() { @@ -225,7 +236,7 @@ public final class EventInstrumentation { } } } - for (Class c = superClass; c != Event.class; c = c.getSuperclass()) { + for (Class c = superClass; c != jdk.internal.event.Event.class; c = c.getSuperclass()) { for (java.lang.reflect.Method method : c.getDeclaredMethods()) { if (!methodSet.contains(method.getName())) { // skip private method in base classes @@ -249,7 +260,6 @@ public final class EventInstrumentation { } } return settingInfos; - } private static List buildFieldInfos(Class superClass, ClassNode classNode) { @@ -264,14 +274,13 @@ public final class EventInstrumentation { fieldInfos.add(new FieldInfo("startTime", Type.LONG_TYPE.getDescriptor(), classNode.name)); fieldInfos.add(new FieldInfo("duration", Type.LONG_TYPE.getDescriptor(), classNode.name)); for (FieldNode field : classNode.fields) { - String className = Type.getType(field.desc).getClassName(); - if (!fieldSet.contains(field.name) && isValidField(field.access, className)) { + if (!fieldSet.contains(field.name) && isValidField(field.access, Type.getType(field.desc).getClassName())) { FieldInfo fi = new FieldInfo(field.name, field.desc, classNode.name); fieldInfos.add(fi); fieldSet.add(field.name); } } - for (Class c = superClass; c != Event.class; c = c.getSuperclass()) { + for (Class c = superClass; c != jdk.internal.event.Event.class; c = c.getSuperclass()) { for (Field field : c.getDeclaredFields()) { // skip private field in base classes if (!Modifier.isPrivate(field.getModifiers())) { @@ -321,10 +330,10 @@ public final class EventInstrumentation { updateMethod(METHOD_IS_ENABLED, methodVisitor -> { Label nullLabel = new Label(); if (guardHandlerReference) { - methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, TYPE_EVENT_HANDLER.getDescriptor()); + getEventHandler(methodVisitor); methodVisitor.visitJumpInsn(Opcodes.IFNULL, nullLabel); } - methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, TYPE_EVENT_HANDLER.getDescriptor()); + getEventHandler(methodVisitor); ASMToolkit.invokeVirtual(methodVisitor, TYPE_EVENT_HANDLER.getInternalName(), METHOD_IS_ENABLED); methodVisitor.visitInsn(Opcodes.IRETURN); if (guardHandlerReference) { @@ -408,7 +417,7 @@ public final class EventInstrumentation { // eventHandler.write(...); // } methodVisitor.visitJumpInsn(Opcodes.IFEQ, end); - methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(EventHandler.class)); + getEventHandler(methodVisitor); methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, eventHandlerXInternalName); for (FieldInfo fi : fieldInfos) { @@ -426,8 +435,8 @@ public final class EventInstrumentation { // MyEvent#shouldCommit() updateMethod(METHOD_EVENT_SHOULD_COMMIT, methodVisitor -> { Label fail = new Label(); - // if (!eventHandler.shoouldCommit(duration) goto fail; - methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(EventHandler.class)); + // if (!eventHandler.shouldCommit(duration) goto fail; + getEventHandler(methodVisitor); methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_DURATION, "J"); ASMToolkit.invokeVirtual(methodVisitor, TYPE_EVENT_HANDLER.getInternalName(), METHOD_EVENT_HANDLER_SHOULD_COMMIT); @@ -435,7 +444,11 @@ public final class EventInstrumentation { for (SettingInfo si : settingInfos) { // if (!settingsMethod(eventHandler.settingX)) goto fail; methodVisitor.visitIntInsn(Opcodes.ALOAD, 0); - methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(EventHandler.class)); + if (untypedEventHandler) { + methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, TYPE_OBJECT.getDescriptor()); + } else { + methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(EventHandler.class)); + } methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, eventHandlerXInternalName); methodVisitor.visitFieldInsn(Opcodes.GETFIELD, eventHandlerXInternalName, si.fieldName, TYPE_SETTING_CONTROL.getDescriptor()); methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, si.internalSettingName); @@ -452,6 +465,15 @@ public final class EventInstrumentation { }); } + private void getEventHandler(MethodVisitor methodVisitor) { + if (untypedEventHandler) { + methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, TYPE_OBJECT.getDescriptor()); + methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, TYPE_EVENT_HANDLER.getInternalName()); + } else { + methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(EventHandler.class)); + } + } + private void makeUninstrumented() { updateExistingWithReturnFalse(METHOD_EVENT_SHOULD_COMMIT); updateExistingWithReturnFalse(METHOD_IS_ENABLED); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java index c66f0cce976..d549bb1f96d 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java @@ -106,11 +106,11 @@ public final class JVM { public native void endRecording(); /** - * Return a list of all classes deriving from {@link Event} + * Return a list of all classes deriving from {@link jdk.internal.event.Event} * * @return list of event classes. */ - public native List> getAllEventClasses(); + public native List> getAllEventClasses(); /** * Return a count of the number of unloaded classes deriving from {@link Event} diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/JVMUpcalls.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/JVMUpcalls.java index 37071aafbd3..9e8ce2e4de6 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/JVMUpcalls.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/JVMUpcalls.java @@ -26,7 +26,6 @@ package jdk.jfr.internal; import java.lang.reflect.Modifier; -import jdk.jfr.Event; import jdk.jfr.internal.handlers.EventHandler; import jdk.jfr.internal.instrument.JDKEvents; @@ -53,8 +52,8 @@ final class JVMUpcalls { */ static byte[] onRetransform(long traceId, boolean dummy, Class clazz, byte[] oldBytes) throws Throwable { try { - if (Event.class.isAssignableFrom(clazz) && !Modifier.isAbstract(clazz.getModifiers())) { - EventHandler handler = Utils.getHandler(clazz.asSubclass(Event.class)); + if (jdk.internal.event.Event.class.isAssignableFrom(clazz) && !Modifier.isAbstract(clazz.getModifiers())) { + EventHandler handler = Utils.getHandler(clazz.asSubclass(jdk.internal.event.Event.class)); if (handler == null) { Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, "No event handler found for " + clazz.getName() + ". Ignoring instrumentation request."); // Probably triggered by some other agent diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataRepository.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataRepository.java index b0b0db71ddb..d27b2cd655c 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataRepository.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataRepository.java @@ -33,6 +33,7 @@ import java.io.DataOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -56,6 +57,7 @@ public final class MetadataRepository { private final List nativeControls = new ArrayList(100); private final TypeLibrary typeLibrary = TypeLibrary.getInstance(); private final SettingsManager settingsManager = new SettingsManager(); + private final Map> mirrors = new HashMap<>(); private boolean staleMetadata = true; private boolean unregistered; private long lastUnloaded = -1; @@ -105,7 +107,7 @@ public final class MetadataRepository { return eventTypes; } - public synchronized EventType getEventType(Class eventClass) { + public synchronized EventType getEventType(Class eventClass) { EventHandler h = getHandler(eventClass); if (h != null && h.isRegistered()) { return h.getEventType(); @@ -121,15 +123,20 @@ public final class MetadataRepository { } // never registered, ignore call } - public synchronized EventType register(Class eventClass) { + public synchronized EventType register(Class eventClass) { return register(eventClass, Collections.emptyList(), Collections.emptyList()); } - public synchronized EventType register(Class eventClass, List dynamicAnnotations, List dynamicFields) { + public synchronized EventType register(Class eventClass, List dynamicAnnotations, List dynamicFields) { Utils.checkRegisterPermission(); EventHandler handler = getHandler(eventClass); if (handler == null) { - handler = makeHandler(eventClass, dynamicAnnotations, dynamicFields); + if (eventClass.getAnnotation(MirrorEvent.class) != null) { + // don't register mirrors + return null; + } + PlatformEventType pe = findMirrorType(eventClass); + handler = makeHandler(eventClass, pe, dynamicAnnotations, dynamicFields); } handler.setRegistered(true); typeLibrary.addType(handler.getPlatformEventType()); @@ -143,16 +150,32 @@ public final class MetadataRepository { return handler.getEventType(); } - private EventHandler getHandler(Class eventClass) { + private PlatformEventType findMirrorType(Class eventClass) throws InternalError { + String fullName = eventClass.getModule().getName() + ":" + eventClass.getName(); + Class 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 eventClass) { Utils.ensureValidEventSubclass(eventClass); SecuritySupport.makeVisibleToJFR(eventClass); Utils.ensureInitialized(eventClass); return Utils.getHandler(eventClass); } - private EventHandler makeHandler(Class eventClass, List dynamicAnnotations, List dynamicFields) throws InternalError { + private EventHandler makeHandler(Class eventClass, PlatformEventType pEventType, List dynamicAnnotations, List dynamicFields) throws InternalError { SecuritySupport.addHandlerExport(eventClass); - PlatformEventType pEventType = (PlatformEventType) TypeLibrary.createType(eventClass, dynamicAnnotations, dynamicFields); + if (pEventType == null) { + pEventType = (PlatformEventType) TypeLibrary.createType(eventClass, dynamicAnnotations, dynamicFields); + } EventType eventType = PrivateAccess.getInstance().newEventType(pEventType); EventControl ec = new EventControl(pEventType, eventClass); Class handlerClass = null; @@ -198,9 +221,9 @@ public final class MetadataRepository { } private static List getEventHandlers() { - List> allEventClasses = jvm.getAllEventClasses(); + List> allEventClasses = jvm.getAllEventClasses(); List eventHandlers = new ArrayList<>(allEventClasses.size()); - for (Class clazz : allEventClasses) { + for (Class clazz : allEventClasses) { EventHandler eh = Utils.getHandler(clazz); if (eh != null) { eventHandlers.add(eh); @@ -252,9 +275,9 @@ public final class MetadataRepository { long unloaded = jvm.getUnloadedEventClassCount(); if (this.lastUnloaded != unloaded) { this.lastUnloaded = unloaded; - List> eventClasses = jvm.getAllEventClasses(); + List> eventClasses = jvm.getAllEventClasses(); HashSet knownIds = new HashSet<>(eventClasses.size()); - for (Class ec: eventClasses) { + for (Class ec: eventClasses) { knownIds.add(Type.getTypeId(ec)); } for (Type type : typeLibrary.getTypes()) { @@ -270,8 +293,18 @@ public final class MetadataRepository { } } - synchronized public void setUnregistered() { + synchronized void setUnregistered() { unregistered = true; } + public synchronized void registerMirror(Class 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()); + } + } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/MirrorEvent.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/MirrorEvent.java new file mode 100644 index 00000000000..79451c6d759 --- /dev/null +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/MirrorEvent.java @@ -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"; +} diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformRecorder.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformRecorder.java index 3e0c84c024e..3310444fd96 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformRecorder.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformRecorder.java @@ -81,9 +81,7 @@ public final class PlatformRecorder { Logger.log(JFR_SYSTEM, INFO, "Registered JDK events"); JDKEvents.addInstrumentation(); startDiskMonitor(); - SecuritySupport.registerEvent(ActiveRecordingEvent.class); activeRecordingEvent = EventType.getEventType(ActiveRecordingEvent.class); - SecuritySupport.registerEvent(ActiveSettingEvent.class); activeSettingEvent = EventType.getEventType(ActiveSettingEvent.class); shutdownHook = SecuritySupport.createThreadWitNoPermissions("JFR: Shutdown Hook", new ShutdownHook(this)); SecuritySupport.setUncaughtExceptionHandler(shutdownHook, new ShutdownHook.ExceptionHandler()); @@ -91,6 +89,7 @@ public final class PlatformRecorder { timer = createTimer(); } + private static Timer createTimer() { try { List result = new CopyOnWriteArrayList<>(); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/SecuritySupport.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/SecuritySupport.java index 1d1efe685a7..eacb24243e8 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/SecuritySupport.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/SecuritySupport.java @@ -262,8 +262,12 @@ public final class SecuritySupport { Modules.addExports(JFR_MODULE, Utils.HANDLERS_PACKAGE_NAME, clazz.getModule()); } - public static void registerEvent(Class eventClass) { - doPrivileged(() -> FlightRecorder.register(eventClass), new FlightRecorderPermission(Utils.REGISTER_EVENT)); + public static void registerEvent(Class eventClass) { + doPrivileged(() -> MetadataRepository.getInstance().register(eventClass), new FlightRecorderPermission(Utils.REGISTER_EVENT)); + } + + public static void registerMirror(Class eventClass) { + doPrivileged(() -> MetadataRepository.getInstance().registerMirror(eventClass), new FlightRecorderPermission(Utils.REGISTER_EVENT)); } static boolean getBooleanProperty(String propertyName) { diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/SettingsManager.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/SettingsManager.java index af27561b31f..9d0ee0abf06 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/SettingsManager.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/SettingsManager.java @@ -37,7 +37,6 @@ import java.util.Map.Entry; import java.util.Set; import java.util.StringJoiner; -import jdk.jfr.Event; import jdk.jfr.internal.handlers.EventHandler; final class SettingsManager { @@ -152,9 +151,9 @@ final class SettingsManager { } } - public void updateRetransform(List> eventClasses) { + public void updateRetransform(List> eventClasses) { List> classes = new ArrayList<>(); - for(Class eventClass: eventClasses) { + for(Class eventClass: eventClasses) { EventHandler eh = Utils.getHandler(eventClass); if (eh != null ) { PlatformEventType eventType = eh.getPlatformEventType(); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/Type.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/Type.java index 32d255eab31..188c07592ac 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/Type.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/Type.java @@ -71,10 +71,11 @@ public class Type implements Comparable { private final String name; private final String superType; private final boolean constantPool; - private final long id; private final ArrayList fields = new ArrayList<>(); private Boolean simpleType; // calculated lazy private boolean remove = true; + private long id; + /** * Creates a type * @@ -318,4 +319,8 @@ public class Type implements Comparable { public boolean getRemove() { return remove; } + + public void setId(long id) { + this.id = id; + } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/TypeLibrary.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/TypeLibrary.java index 825f9d508e4..218d94d07a3 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/TypeLibrary.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/TypeLibrary.java @@ -49,7 +49,6 @@ import java.util.stream.Stream; import jdk.jfr.AnnotationElement; import jdk.jfr.Description; -import jdk.jfr.Event; import jdk.jfr.Label; import jdk.jfr.MetadataDefinition; import jdk.jfr.Name; @@ -240,7 +239,7 @@ public final class TypeLibrary { // STRUCT String superType = null; boolean eventType = false; - if (Event.class.isAssignableFrom(clazz)) { + if (jdk.internal.event.Event.class.isAssignableFrom(clazz)) { superType = Type.SUPER_TYPE_EVENT; eventType= true; } @@ -489,4 +488,8 @@ public final class TypeLibrary { aQ.addAll(ae.getAnnotationElements()); } } + + public void removeType(long id) { + types.remove(id); + } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/Utils.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/Utils.java index c1e7c4541ab..9bc8e8d2333 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/Utils.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/Utils.java @@ -267,7 +267,7 @@ public final class Utils { return (long) (nanos * JVM.getJVM().getTimeConversionFactor()); } - static synchronized EventHandler getHandler(Class eventClass) { + static synchronized EventHandler getHandler(Class eventClass) { Utils.ensureValidEventSubclass(eventClass); try { Field f = eventClass.getDeclaredField(EventInstrumentation.FIELD_EVENT_HANDLER); @@ -278,7 +278,7 @@ public final class Utils { } } - static synchronized void setHandler(Class eventClass, EventHandler handler) { + static synchronized void setHandler(Class eventClass, EventHandler handler) { Utils.ensureValidEventSubclass(eventClass); try { Field field = eventClass.getDeclaredField(EventInstrumentation.FIELD_EVENT_HANDLER); @@ -322,7 +322,7 @@ public final class Utils { static List getVisibleEventFields(Class clazz) { Utils.ensureValidEventSubclass(clazz); List fields = new ArrayList<>(); - for (Class c = clazz; c != Event.class; c = c.getSuperclass()) { + for (Class c = clazz; c != jdk.internal.event.Event.class; c = c.getSuperclass()) { for (Field field : c.getDeclaredFields()) { // skip private field in base classes if (c == clazz || !Modifier.isPrivate(field.getModifiers())) { @@ -334,10 +334,10 @@ public final class Utils { } public static void ensureValidEventSubclass(Class eventClass) { - if (Event.class.isAssignableFrom(eventClass) && Modifier.isAbstract(eventClass.getModifiers())) { + if (jdk.internal.event.Event.class.isAssignableFrom(eventClass) && Modifier.isAbstract(eventClass.getModifiers())) { throw new IllegalArgumentException("Abstract event classes are not allowed"); } - if (eventClass == Event.class || !Event.class.isAssignableFrom(eventClass)) { + if (eventClass == Event.class || eventClass == jdk.internal.event.Event.class || !jdk.internal.event.Event.class.isAssignableFrom(eventClass)) { throw new IllegalArgumentException("Must be a subclass to " + Event.class.getName()); } } @@ -366,7 +366,7 @@ public final class Utils { } } - public static void ensureInitialized(Class eventClass) { + public static void ensureInitialized(Class eventClass) { SecuritySupport.ensureClassIsInitialized(eventClass); } @@ -499,6 +499,50 @@ public final class Utils { return eventName; } + public static void verifyMirror(Class mirror, Class real) { + Class cMirror = Objects.requireNonNull(mirror); + Class cReal = Objects.requireNonNull(real); + + while (cReal != null) { + Map mirrorFields = new HashMap<>(); + if (cMirror != null) { + for (Field f : cMirror.getDeclaredFields()) { + if (isSupportedType(f.getType())) { + mirrorFields.put(f.getName(), f); + } + } + } + for (Field realField : cReal.getDeclaredFields()) { + if (isSupportedType(realField.getType())) { + String fieldName = realField.getName(); + Field mirrorField = mirrorFields.get(fieldName); + if (mirrorField == null) { + throw new InternalError("Missing mirror field for " + cReal.getName() + "#" + fieldName); + } + if (realField.getModifiers() != mirrorField.getModifiers()) { + throw new InternalError("Incorrect modifier for mirror field "+ cMirror.getName() + "#" + fieldName); + } + mirrorFields.remove(fieldName); + } + } + if (!mirrorFields.isEmpty()) { + throw new InternalError( + "Found additional fields in mirror class " + cMirror.getName() + " " + mirrorFields.keySet()); + } + if (cMirror != null) { + cMirror = cMirror.getSuperclass(); + } + cReal = cReal.getSuperclass(); + } + } + + private static boolean isSupportedType(Class type) { + if (Modifier.isTransient(type.getModifiers()) || Modifier.isStatic(type.getModifiers())) { + return false; + } + return Type.isValidJavaFieldType(type.getName()); + } + public static String makeFilename(Recording recording) { String pid = JVM.getJVM().getPid(); String date = Repository.REPO_DATE_FORMAT.format(LocalDateTime.now()); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/JDKEvents.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/JDKEvents.java index 078586ec5fa..3ba04479c6f 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/JDKEvents.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/JDKEvents.java @@ -51,6 +51,9 @@ import jdk.jfr.internal.Utils; public final class JDKEvents { + private static final Class[] mirrorEventClasses = { + }; + private static final Class[] eventClasses = { FileForceEvent.class, FileReadEvent.class, @@ -90,6 +93,9 @@ public final class JDKEvents { Modules.addExports(jdkJfrModule, Utils.EVENTS_PACKAGE_NAME, javaBaseModule); Modules.addExports(jdkJfrModule, Utils.INSTRUMENT_PACKAGE_NAME, javaBaseModule); Modules.addExports(jdkJfrModule, Utils.HANDLERS_PACKAGE_NAME, javaBaseModule); + for (Class mirrorEventClass : mirrorEventClasses) { + SecuritySupport.registerMirror(((Class)mirrorEventClass)); + } for (Class eventClass : eventClasses) { SecuritySupport.registerEvent((Class) eventClass); } diff --git a/test/Makefile b/test/Makefile index 3b92b8d5081..cfff86fa7d6 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,5 +1,5 @@ # -# Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -27,11 +27,16 @@ # Makefile to run tests from multiple sibling directories # +$(info WARNING: This way of running tests ("cd test && make") is deprecated) +$(info Please use "make test TEST=..." instead. See doc/testing.md for details) + # Macro to run a test target in a subdir define SUBDIR_TEST # subdirectory target if [ -d $1 ] ; then \ if [ -r $1/Makefile ] ; then \ $(MAKE) --no-print-directory -k -C $1 $2 ; \ + echo 'WARNING: This way of running tests ("cd test && make") is deprecated' ; \ + echo 'Please use "make test TEST=..." instead. See doc/testing.md for details' ; \ else \ echo "ERROR: File does not exist: $1/Makefile"; \ exit 1; \ diff --git a/test/hotspot/gtest/gc/shared/test_gcTimer.cpp b/test/hotspot/gtest/gc/shared/test_gcTimer.cpp new file mode 100644 index 00000000000..c3282ffd1a1 --- /dev/null +++ b/test/hotspot/gtest/gc/shared/test_gcTimer.cpp @@ -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"; +} diff --git a/test/hotspot/gtest/memory/test_metaspace.cpp b/test/hotspot/gtest/memory/test_metaspace.cpp new file mode 100644 index 00000000000..edc3bccbf5a --- /dev/null +++ b/test/hotspot/gtest/memory/test_metaspace.cpp @@ -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); +} diff --git a/test/hotspot/gtest/utilities/test_globalCounter.cpp b/test/hotspot/gtest/utilities/test_globalCounter.cpp index e9229be6dff..9813b5102f5 100644 --- a/test/hotspot/gtest/utilities/test_globalCounter.cpp +++ b/test/hotspot/gtest/utilities/test_globalCounter.cpp @@ -47,11 +47,11 @@ public: void main_run() { _wrt_start->signal(); while (!_exit) { - GlobalCounter::critical_section_begin(this); + GlobalCounter::CSContext cs_context = GlobalCounter::critical_section_begin(this); volatile TestData* test = OrderAccess::load_acquire(_test); long value = OrderAccess::load_acquire(&test->test_value); ASSERT_EQ(value, GOOD_VALUE); - GlobalCounter::critical_section_end(this); + GlobalCounter::critical_section_end(this, cs_context); { GlobalCounter::CriticalSection cs(this); volatile TestData* test = OrderAccess::load_acquire(_test); diff --git a/test/hotspot/gtest/utilities/test_globalCounter_nested.cpp b/test/hotspot/gtest/utilities/test_globalCounter_nested.cpp new file mode 100644 index 00000000000..bb04be1c184 --- /dev/null +++ b/test/hotspot/gtest/utilities/test_globalCounter_nested.cpp @@ -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 : 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(); + } +} diff --git a/test/hotspot/jtreg/gc/g1/humongousObjects/objectGraphTest/TestObjectGraphAfterGC.java b/test/hotspot/jtreg/gc/g1/humongousObjects/objectGraphTest/TestObjectGraphAfterGC.java index 8737788a03c..aef89a5e2f8 100644 --- a/test/hotspot/jtreg/gc/g1/humongousObjects/objectGraphTest/TestObjectGraphAfterGC.java +++ b/test/hotspot/jtreg/gc/g1/humongousObjects/objectGraphTest/TestObjectGraphAfterGC.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -56,29 +56,29 @@ import java.util.stream.Collectors; * @run driver ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission * - * @run main/othervm -Xms200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * @run main/othervm -Xms200M -Xmx200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. * -XX:+UnlockExperimentalVMOptions -XX:MaxGCPauseMillis=30000 -XX:G1MixedGCLiveThresholdPercent=100 -XX:G1HeapWastePercent=0 * -XX:G1HeapRegionSize=1M -Xlog:gc=info:file=TestObjectGraphAfterGC_MIXED_GC.gc.log -XX:MaxTenuringThreshold=1 * -XX:G1MixedGCCountTarget=1 -XX:G1OldCSetRegionThresholdPercent=100 -XX:SurvivorRatio=1 -XX:InitiatingHeapOccupancyPercent=0 * gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC MIXED_GC * - * @run main/othervm -Xms200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * @run main/othervm -Xms200M -Xmx200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. * -XX:G1HeapRegionSize=1M -Xlog:gc*=debug:file=TestObjectGraphAfterGC_YOUNG_GC.gc.log * gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC YOUNG_GC * - * @run main/othervm -Xms200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * @run main/othervm -Xms200M -Xmx200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. * -XX:G1HeapRegionSize=1M -Xlog:gc=info:file=TestObjectGraphAfterGC_FULL_GC.gc.log * gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC FULL_GC * - * @run main/othervm -Xms200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * @run main/othervm -Xms200M -Xmx200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. * -XX:G1HeapRegionSize=1M -Xlog:gc=info:file=TestObjectGraphAfterGC_FULL_GC_MEMORY_PRESSURE.gc.log * gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC FULL_GC_MEMORY_PRESSURE * - * @run main/othervm -Xms200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * @run main/othervm -Xms200M -Xmx200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. * -XX:G1HeapRegionSize=1M -Xlog:gc=info:file=TestObjectGraphAfterGC_CMC.gc.log -XX:MaxTenuringThreshold=16 * gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC CMC * - * @run main/othervm -Xms200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * @run main/othervm -Xms200M -Xmx200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. * -XX:G1HeapRegionSize=1M -Xlog:gc=info:file=TestObjectGraphAfterGC_CMC_NO_SURV_ROOTS.gc.log -XX:MaxTenuringThreshold=1 * gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC CMC_NO_SURV_ROOTS * diff --git a/test/hotspot/jtreg/runtime/SharedArchiveFile/serviceability/ReplaceCriticalClasses.java b/test/hotspot/jtreg/runtime/SharedArchiveFile/serviceability/ReplaceCriticalClasses.java new file mode 100644 index 00000000000..696d2afaeba --- /dev/null +++ b/test/hotspot/jtreg/runtime/SharedArchiveFile/serviceability/ReplaceCriticalClasses.java @@ -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 { + 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"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/cacheObject/CheckArchivedModuleApp.java b/test/hotspot/jtreg/runtime/appcds/cacheObject/CheckArchivedModuleApp.java index 5568e1cd6bb..d7774187c3f 100644 --- a/test/hotspot/jtreg/runtime/appcds/cacheObject/CheckArchivedModuleApp.java +++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/CheckArchivedModuleApp.java @@ -52,7 +52,8 @@ public class CheckArchivedModuleApp { boolean expectArchivedConfiguration = "yes".equals(args[1]); // -XX:+EnableJVMCI adds extra system modules, in which case the system // module objects are not archived. - if (wb.getBooleanVMFlag("EnableJVMCI")) { + Boolean enableJVMCI = wb.getBooleanVMFlag("EnableJVMCI"); + if (enableJVMCI != null && enableJVMCI) { expectArchivedDescriptors = false; expectArchivedConfiguration = false; } diff --git a/test/hotspot/jtreg/runtime/handshake/HandshakeWalkSuspendExitTest.java b/test/hotspot/jtreg/runtime/handshake/HandshakeWalkSuspendExitTest.java new file mode 100644 index 00000000000..26b6f63e9ef --- /dev/null +++ b/test/hotspot/jtreg/runtime/handshake/HandshakeWalkSuspendExitTest.java @@ -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(); + } + } +} diff --git a/test/hotspot/jtreg/testlibrary/jvmti/libSimpleClassFileLoadHook.c b/test/hotspot/jtreg/testlibrary/jvmti/libSimpleClassFileLoadHook.c index 2424e87c880..a95d7b28a89 100644 --- a/test/hotspot/jtreg/testlibrary/jvmti/libSimpleClassFileLoadHook.c +++ b/test/hotspot/jtreg/testlibrary/jvmti/libSimpleClassFileLoadHook.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -91,17 +91,23 @@ ClassFileLoadHook(jvmtiEnv *jvmti_env, JNIEnv *env, jclass class_beeing_redefine *new_class_data_len = class_data_len; *new_class_data = new_data; - fprintf(stderr, "Rewriting done. Replaced %d occurrence(s)\n", count); + fprintf(stderr, "Rewriting done. Replaced %d occurrence(s) of \"%s\" to \"%s\"\n", count, FROM, TO); } } } +static int early = 0; + static jint init_options(char *options) { char* class_name; char* from; char* to; fprintf(stderr, "Agent library loaded with options = %s\n", options); + if (options != NULL && strncmp(options, "-early,", 7) == 0) { + early = 1; + options += 7; + } if ((class_name = options) != NULL && (from = strchr(class_name, ',')) != NULL && (from[1] != 0)) { *from = 0; @@ -132,6 +138,7 @@ static jint init_options(char *options) { static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) { int rc; + jvmtiCapabilities caps; if ((rc = (*jvm)->GetEnv(jvm, (void **)&jvmti, JVMTI_VERSION_1_1)) != JNI_OK) { fprintf(stderr, "Unable to create jvmtiEnv, GetEnv failed, error = %d\n", rc); @@ -141,6 +148,19 @@ static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) { return JNI_ERR; } + memset(&caps, 0, sizeof(caps)); + + caps.can_redefine_classes = 1; + if (early) { + fprintf(stderr, "can_generate_all_class_hook_events/can_generate_early_vmstart/can_generate_early_class_hook_events == 1\n"); + caps.can_generate_all_class_hook_events = 1; + caps.can_generate_early_class_hook_events = 1; + } + if ((rc = (*jvmti)->AddCapabilities(jvmti, &caps)) != JNI_OK) { + fprintf(stderr, "AddCapabilities failed, error = %d\n", rc); + return JNI_ERR; + } + (void) memset(&callbacks, 0, sizeof(callbacks)); callbacks.ClassFileLoadHook = &ClassFileLoadHook; if ((rc = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks))) != JNI_OK) { diff --git a/test/jdk/java/security/KeyAgreement/KeyAgreementTest.java b/test/jdk/java/security/KeyAgreement/KeyAgreementTest.java index 8f705abd772..8b16b9b6b8d 100644 --- a/test/jdk/java/security/KeyAgreement/KeyAgreementTest.java +++ b/test/jdk/java/security/KeyAgreement/KeyAgreementTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 4936763 8184359 + * @bug 4936763 8184359 8205476 * @summary KeyAgreement Test with all supported algorithms from JCE. * Arguments order * It removes com/sun/crypto/provider/KeyAgreement/DHGenSecretKey.java @@ -150,5 +150,17 @@ public class KeyAgreementTest { if (!Arrays.equals(secret1, secret2)) { throw new Exception("KeyAgreement secret mismatch."); } + + // ensure that a new secret cannot be produced before the next doPhase + try { + ka2.generateSecret(); + throw new RuntimeException("state not reset"); + } catch (IllegalStateException ex) { + // this is expected + } + + // calling doPhase and then generateSecret should succeed + ka2.doPhase(kp1.getPublic(), true); + ka2.generateSecret(); } } diff --git a/test/jdk/jdk/jfr/jvm/TestGetAllEventClasses.java b/test/jdk/jdk/jfr/jvm/TestGetAllEventClasses.java index 9a5d63dcd20..298dca2b254 100644 --- a/test/jdk/jdk/jfr/jvm/TestGetAllEventClasses.java +++ b/test/jdk/jdk/jfr/jvm/TestGetAllEventClasses.java @@ -104,8 +104,9 @@ public class TestGetAllEventClasses { } @SafeVarargs + @SuppressWarnings("rawtypes") private static void assertEvents(JVM jvm, boolean inclusion, Class... targetEvents) { - final List> list = jvm.getAllEventClasses(); + final List list = jvm.getAllEventClasses(); for (Class ev : targetEvents) { if (list.contains(ev)) { if (inclusion) { diff --git a/test/langtools/ProblemList.txt b/test/langtools/ProblemList.txt index 72c7572b34f..d8e9fad98ce 100644 --- a/test/langtools/ProblemList.txt +++ b/test/langtools/ProblemList.txt @@ -56,11 +56,6 @@ tools/javac/warnings/suppress/TypeAnnotations.java tools/javac/modules/SourceInSymlinkTest.java 8180263 windows-all fails when run on a subst drive tools/javac/importscope/T8193717.java 8203925 generic-all the test requires too much memory - -tools/javac/options/smokeTests/OptionSmokeTest.java 8205493 generic-all hard-coded release values in strings - -tools/javac/file/zip/8003512/LoadClassFromJava6CreatedJarTest.java 8206874 generic-all test requires a JDK 6 environment to be useful - ########################################################################### # # javap diff --git a/test/langtools/jdk/javadoc/doclet/testTaglets/TestTaglets.out b/test/langtools/jdk/javadoc/doclet/testTaglets/TestTaglets.out index 0fa52c176e7..f96568e2f58 100644 --- a/test/langtools/jdk/javadoc/doclet/testTaglets/TestTaglets.out +++ b/test/langtools/jdk/javadoc/doclet/testTaglets/TestTaglets.out @@ -26,5 +26,5 @@ @throws: ........ ...... ....... .... constructor method ..... ...... ........ @treatAsPrivate: ........ ...... ....... type ........... method field ...... ........ @uses: ........ module ....... .... ........... ...... ..... ...... ........ - {@value}: overview ...... package type constructor method field inline ........ + {@value}: overview module package type constructor method field inline ........ @version: overview module package type ........... ...... ..... ...... disabled diff --git a/test/langtools/jdk/javadoc/doclet/testValueTag/TestValueTagInModule.java b/test/langtools/jdk/javadoc/doclet/testValueTag/TestValueTagInModule.java new file mode 100644 index 00000000000..2c21385a91c --- /dev/null +++ b/test/langtools/jdk/javadoc/doclet/testValueTag/TestValueTagInModule.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8210244 + * @summary {@value} should be permitted in module documentation + * @library /tools/lib ../lib + * @modules jdk.javadoc/jdk.javadoc.internal.tool + * @build JavadocTester + * @run main TestValueTagInModule + */ + + +import java.nio.file.Path; +import java.nio.file.Paths; + +import toolbox.ModuleBuilder; +import toolbox.ToolBox; + +public class TestValueTagInModule extends JavadocTester { + + final ToolBox tb; + + public static void main(String... args) throws Exception { + TestValueTagInModule tester = new TestValueTagInModule(); + tester.runTests(m -> new Object[]{Paths.get(m.getName())}); + } + + TestValueTagInModule() { + tb = new ToolBox(); + } + + @Test + void test(Path base) throws Exception { + Path srcDir = base.resolve("src"); + createTestClass(srcDir); + + Path outDir = base.resolve("out"); + javadoc("-d", outDir.toString(), + "--module-source-path", srcDir.toString(), + "--module", "m1"); + + checkExit(Exit.OK); + + checkOutput("m1/module-summary.html", true, + "\n" + + "\n" + + "\n" + + "

    value of field CONS : 100
    "); + } + + void createTestClass(Path srcDir) throws Exception { + new ModuleBuilder(tb, "m1") + .comment("value of field CONS : {@value pkg.A#CONS}") + .exports("pkg") + .classes("package pkg; public class A{ public static final int CONS = 100;}") + .write(srcDir); + } +} diff --git a/test/langtools/tools/javac/options/smokeTests/OptionSmokeTest.java b/test/langtools/tools/javac/options/smokeTests/OptionSmokeTest.java index 3b480200274..faf3bb01eb8 100644 --- a/test/langtools/tools/javac/options/smokeTests/OptionSmokeTest.java +++ b/test/langtools/tools/javac/options/smokeTests/OptionSmokeTest.java @@ -32,9 +32,11 @@ * jdk.compiler/com.sun.tools.javac.util * jdk.jdeps/com.sun.tools.javap * @build toolbox.ToolBox toolbox.JavacTask toolbox.TestRunner - * @run main OptionSmokeTest + * @run main/othervm OptionSmokeTest */ +import java.util.Locale; + import java.nio.file.Path; import java.nio.file.Paths; @@ -51,6 +53,7 @@ public class OptionSmokeTest extends TestRunner { public OptionSmokeTest() { super(System.err); + Locale.setDefault(Locale.US); } protected void runTests() throws Exception { @@ -117,7 +120,7 @@ public class OptionSmokeTest extends TestRunner { @Test public void requiresArg(Path base) throws Exception { - doTestNoSource(base, "error: -target requires an argument", "-target"); + doTestNoSource(base, "error: --target requires an argument", "-target"); } @Test @@ -209,7 +212,7 @@ public class OptionSmokeTest extends TestRunner { @Test public void optionCantBeUsedWithRelease(Path base) throws Exception { - doTestNoSource(base, "error: option -source cannot be used together with --release", + doTestNoSource(base, "error: option --source cannot be used together with --release", String.format("--release %s -source %s", Source.DEFAULT.name, Source.DEFAULT.name)); } @@ -232,9 +235,9 @@ public class OptionSmokeTest extends TestRunner { String.format("--release %s -endorseddirs any", Source.DEFAULT.name)); doTestNoSource(base, "error: option -extdirs cannot be used together with --release", String.format("--release %s -extdirs any", Source.DEFAULT.name)); - doTestNoSource(base, "error: option -source cannot be used together with --release", + doTestNoSource(base, "error: option --source cannot be used together with --release", String.format("--release %s -source %s", Source.MIN.name, Source.DEFAULT.name)); - doTestNoSource(base, "error: option -target cannot be used together with --release", + doTestNoSource(base, "error: option --target cannot be used together with --release", String.format("--release %s -target %s", Source.MIN.name, Source.DEFAULT.name)); doTestNoSource(base, "error: option --system cannot be used together with --release", String.format("--release %s --system none", Source.DEFAULT.name)); diff --git a/test/lib/jdk/test/lib/cds/CDSOptions.java b/test/lib/jdk/test/lib/cds/CDSOptions.java index dbf7b8c9ff1..3f457e20744 100644 --- a/test/lib/jdk/test/lib/cds/CDSOptions.java +++ b/test/lib/jdk/test/lib/cds/CDSOptions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,7 @@ public class CDSOptions { public String archiveName; public ArrayList prefix = new ArrayList(); public ArrayList suffix = new ArrayList(); + public boolean useSystemArchive = false; // Indicate whether to append "-version" when using CDS Archive. // Most of tests will use '-version' @@ -68,4 +69,9 @@ public class CDSOptions { this.useVersion = use; return this; } + + public CDSOptions setUseSystemArchive(boolean use) { + this.useSystemArchive = use; + return this; + } } diff --git a/test/lib/jdk/test/lib/cds/CDSTestUtils.java b/test/lib/jdk/test/lib/cds/CDSTestUtils.java index 2989aad607c..2c84bb7e1d8 100644 --- a/test/lib/jdk/test/lib/cds/CDSTestUtils.java +++ b/test/lib/jdk/test/lib/cds/CDSTestUtils.java @@ -82,21 +82,21 @@ public class CDSTestUtils { * * Instead, the test case should be written as * - * CCDSTestUtils.run(args).assertNormalExit("Hi"); + * CDSTestUtils.run(args).assertNormalExit("Hi"); * * EXAMPLES/HOWTO * * 1. For simple substring matching: * - * CCDSTestUtils.run(args).assertNormalExit("Hi"); - * CCDSTestUtils.run(args).assertNormalExit("a", "b", "x"); - * CCDSTestUtils.run(args).assertAbnormalExit("failure 1", "failure2"); + * CDSTestUtils.run(args).assertNormalExit("Hi"); + * CDSTestUtils.run(args).assertNormalExit("a", "b", "x"); + * CDSTestUtils.run(args).assertAbnormalExit("failure 1", "failure2"); * * 2. For more complex output matching: using Lambda expressions * - * CCDSTestUtils.run(args) + * CDSTestUtils.run(args) * .assertNormalExit(output -> output.shouldNotContain("this should not be printed"); - * CCDSTestUtils.run(args) + * CDSTestUtils.run(args) * .assertAbnormalExit(output -> { * output.shouldNotContain("this should not be printed"); * output.shouldHaveExitValue(123); @@ -104,13 +104,13 @@ public class CDSTestUtils { * * 3. Chaining several checks: * - * CCDSTestUtils.run(args) + * CDSTestUtils.run(args) * .assertNormalExit(output -> output.shouldNotContain("this should not be printed") * .assertNormalExit("should have this", "should have that"); * * 4. [Rare use case] if a test sometimes exit normally, and sometimes abnormally: * - * CCDSTestUtils.run(args) + * CDSTestUtils.run(args) * .ifNormalExit("ths string is printed when exiting with 0") * .ifAbNormalExit("ths string is printed when exiting with 1"); * @@ -388,9 +388,11 @@ public class CDSTestUtils { cmd.add("-Xshare:" + opts.xShareMode); cmd.add("-Dtest.timeout.factor=" + TestTimeoutFactor); - if (opts.archiveName == null) - opts.archiveName = getDefaultArchiveName(); - cmd.add("-XX:SharedArchiveFile=" + opts.archiveName); + if (!opts.useSystemArchive) { + if (opts.archiveName == null) + opts.archiveName = getDefaultArchiveName(); + cmd.add("-XX:SharedArchiveFile=" + opts.archiveName); + } if (opts.useVersion) cmd.add("-version"); diff --git a/test/make/TestMake.gmk b/test/make/TestMake.gmk index f274f7c2ea2..e4681c5ec75 100644 --- a/test/make/TestMake.gmk +++ b/test/make/TestMake.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -36,13 +36,17 @@ java-compilation: copy-files: +$(MAKE) -f TestCopyFiles.gmk $(TEST_SUBTARGET) -test-idea: +idea: +$(MAKE) -f TestIdea.gmk $(TEST_SUBTARGET) -test-compile-commands: +compile-commands: +$(MAKE) -f TestCompileCommands.gmk $(TEST_SUBTARGET) +TARGETS += make-base java-compilation copy-files idea compile-commands -all: make-base java-compilation copy-files test-idea +all: $(TARGETS) -.PHONY: default all make-base java-compilation copy-files test-idea test-compile-commands +print-targets: + $(ECHO) "$(TARGETS)" + +.PHONY: default all $(TARGETS) diff --git a/test/make/TestMakeBase.gmk b/test/make/TestMakeBase.gmk index 26b6cdad9ce..1b6d1344b20 100644 --- a/test/make/TestMakeBase.gmk +++ b/test/make/TestMakeBase.gmk @@ -344,7 +344,7 @@ $(eval $(call assert-equals, \ KWBASE := APA=banan;GURKA=tomat;COUNT=1%202%203%204%205;SUM=1+2+3+4+5;MANY_WORDS=I have the best words. $(eval $(call ParseKeywordVariable, KWBASE, \ - KEYWORDS := APA GURKA SUM, \ + SINGLE_KEYWORDS := APA GURKA SUM, \ STRING_KEYWORDS := COUNT MANY_WORDS, \ )) @@ -377,7 +377,7 @@ override KWBASE_WEIRD_GURKA := paprika KWBASE_WEIRD := ;;APA=banan;;;GURKA=apelsin;APA=skansen;; $(eval $(call ParseKeywordVariable, KWBASE_WEIRD, \ - KEYWORDS := APA GURKA SUM, \ + SINGLE_KEYWORDS := APA GURKA SUM, \ STRING_KEYWORDS := COUNT, \ ))