diff --git a/.hgtags b/.hgtags index 1a3660fb26..a5f9652f53 100644 --- a/.hgtags +++ b/.hgtags @@ -1398,3 +1398,5 @@ a0eb08e2db5a40956a9c2d6b7dea76a894559033 jdk8u272-b08 b36c3f635d937798abe5e7c5a40a868705fed15e jdk8u275-b01 b36c3f635d937798abe5e7c5a40a868705fed15e jdk8u275-ga 312e9cb580c5c3f8f6ebf660b1f78ff060590ac4 jdk8u282-b02 +83661fdee9f08747a1d7a98123c00732b6a3a43d jdk8u282-b03 +d7c102fe9c4736bca65b25da69093d84da141e65 jdk8u282-b04 diff --git a/THIRD_PARTY_README b/THIRD_PARTY_README index 4863a22f1d..ce360ad0db 100644 --- a/THIRD_PARTY_README +++ b/THIRD_PARTY_README @@ -155,11 +155,11 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ------------------------------------------------------------------------------- %% This notice is provided with respect to CUP Parser Generator for -Java 0.10k, which may be included with JRE 8, JDK 8, and OpenJDK 8. +Java 0.10b, which may be included with JRE 8, JDK 8, and OpenJDK 8. --- begin of LICENSE --- -Copyright 1996-1999 by Scott Hudson, Frank Flannery, C. Scott Ananian +Copyright 1996-2015 by Scott Hudson, Frank Flannery, C. Scott Ananian, Michael Petter Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided @@ -3028,7 +3028,7 @@ included with JRE 8, JDK 8, and OpenJDK 8. Apache Commons Math 3.2 Apache Derby 10.11.1.2 Apache Jakarta BCEL 5.1 - Apache Santuario XML Security for Java 2.1.1 + Apache Santuario XML Security for Java 2.1.3 Apache Xalan-Java 2.7.2 Apache Xerces Java 2.10.0 Apache XML Resolver 1.1 diff --git a/src/os/linux/vm/osContainer_linux.cpp b/src/os/linux/vm/osContainer_linux.cpp index 55a298172b..cc9af588a3 100644 --- a/src/os/linux/vm/osContainer_linux.cpp +++ b/src/os/linux/vm/osContainer_linux.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2019, 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 @@ -34,12 +34,15 @@ bool OSContainer::_is_initialized = false; bool OSContainer::_is_containerized = false; +int OSContainer::_active_processor_count = 1; julong _unlimited_memory; class CgroupSubsystem: CHeapObj { friend class OSContainer; private: + volatile jlong _next_check_counter; + /* mountinfo contents */ char *_root; char *_mount_point; @@ -52,6 +55,7 @@ class CgroupSubsystem: CHeapObj { _root = os::strdup(root); _mount_point = os::strdup(mountpoint); _path = NULL; + _next_check_counter = min_jlong; } /* @@ -80,14 +84,14 @@ class CgroupSubsystem: CHeapObj { buf[MAXPATHLEN-1] = '\0'; _path = os::strdup(buf); } else { - char *p = strstr(_root, cgroup_path); + char *p = strstr(cgroup_path, _root); if (p != NULL && p == _root) { if (strlen(cgroup_path) > strlen(_root)) { int buflen; strncpy(buf, _mount_point, MAXPATHLEN); buf[MAXPATHLEN-1] = '\0'; buflen = strlen(buf); - if ((buflen + strlen(cgroup_path)) > (MAXPATHLEN-1)) { + if ((buflen + strlen(cgroup_path) - strlen(_root)) > (MAXPATHLEN-1)) { return; } strncat(buf, cgroup_path + strlen(_root), MAXPATHLEN-buflen); @@ -101,6 +105,14 @@ class CgroupSubsystem: CHeapObj { } char *subsystem_path() { return _path; } + + bool cache_has_expired() { + return os::elapsed_counter() > _next_check_counter; + } + + void set_cache_expiry_time(jlong timeout) { + _next_check_counter = os::elapsed_counter() + timeout; + } }; CgroupSubsystem* memory = NULL; @@ -214,16 +226,11 @@ PRAGMA_DIAG_POP * we are running under cgroup control. */ void OSContainer::init() { - int mountid; - int parentid; - int major; - int minor; FILE *mntinfo = NULL; FILE *cgroup = NULL; char buf[MAXPATHLEN+1]; char tmproot[MAXPATHLEN+1]; char tmpmount[MAXPATHLEN+1]; - char tmpbase[MAXPATHLEN+1]; char *p; jlong mem_limit; @@ -263,99 +270,27 @@ void OSContainer::init() { return; } - while ( (p = fgets(buf, MAXPATHLEN, mntinfo)) != NULL) { - // Look for the filesystem type and see if it's cgroup - char fstype[MAXPATHLEN+1]; - fstype[0] = '\0'; - char *s = strstr(p, " - "); - if (s != NULL && - sscanf(s, " - %s", fstype) == 1 && - strcmp(fstype, "cgroup") == 0) { - - if (strstr(p, "memory") != NULL) { - int matched = sscanf(p, "%d %d %d:%d %s %s", - &mountid, - &parentid, - &major, - &minor, - tmproot, - tmpmount); - if (matched == 6) { - memory = new CgroupSubsystem(tmproot, tmpmount); - } - else - if (PrintContainerInfo) { - tty->print_cr("Incompatible str containing cgroup and memory: %s", p); - } - } else if (strstr(p, "cpuset") != NULL) { - int matched = sscanf(p, "%d %d %d:%d %s %s", - &mountid, - &parentid, - &major, - &minor, - tmproot, - tmpmount); - if (matched == 6) { - cpuset = new CgroupSubsystem(tmproot, tmpmount); - } - else { - if (PrintContainerInfo) { - tty->print_cr("Incompatible str containing cgroup and cpuset: %s", p); - } - } - } else if (strstr(p, "cpu,cpuacct") != NULL || strstr(p, "cpuacct,cpu") != NULL) { - int matched = sscanf(p, "%d %d %d:%d %s %s", - &mountid, - &parentid, - &major, - &minor, - tmproot, - tmpmount); - if (matched == 6) { - cpu = new CgroupSubsystem(tmproot, tmpmount); - cpuacct = new CgroupSubsystem(tmproot, tmpmount); - } - else { - if (PrintContainerInfo) { - tty->print_cr("Incompatible str containing cgroup and cpu,cpuacct: %s", p); - } - } - } else if (strstr(p, "cpuacct") != NULL) { - int matched = sscanf(p, "%d %d %d:%d %s %s", - &mountid, - &parentid, - &major, - &minor, - tmproot, - tmpmount); - if (matched == 6) { - cpuacct = new CgroupSubsystem(tmproot, tmpmount); - } - else { - if (PrintContainerInfo) { - tty->print_cr("Incompatible str containing cgroup and cpuacct: %s", p); - } - } - } else if (strstr(p, "cpu") != NULL) { - int matched = sscanf(p, "%d %d %d:%d %s %s", - &mountid, - &parentid, - &major, - &minor, - tmproot, - tmpmount); - if (matched == 6) { - cpu = new CgroupSubsystem(tmproot, tmpmount); - } - else { - if (PrintContainerInfo) { - tty->print_cr("Incompatible str containing cgroup and cpu: %s", p); - } - } + while ((p = fgets(buf, MAXPATHLEN, mntinfo)) != NULL) { + char tmpcgroups[MAXPATHLEN+1]; + char *cptr = tmpcgroups; + char *token; + + // mountinfo format is documented at https://www.kernel.org/doc/Documentation/filesystems/proc.txt + if (sscanf(p, "%*d %*d %*d:%*d %s %s %*[^-]- cgroup %*s %s", tmproot, tmpmount, tmpcgroups) != 3) { + continue; + } + while ((token = strsep(&cptr, ",")) != NULL) { + if (strcmp(token, "memory") == 0) { + memory = new CgroupSubsystem(tmproot, tmpmount); + } else if (strcmp(token, "cpuset") == 0) { + cpuset = new CgroupSubsystem(tmproot, tmpmount); + } else if (strcmp(token, "cpu") == 0) { + cpu = new CgroupSubsystem(tmproot, tmpmount); + } else if (strcmp(token, "cpuacct") == 0) { + cpuacct= new CgroupSubsystem(tmproot, tmpmount); } } } - fclose(mntinfo); if (memory == NULL) { @@ -415,30 +350,30 @@ void OSContainer::init() { return; } - while ( (p = fgets(buf, MAXPATHLEN, cgroup)) != NULL) { - int cgno; - int matched; - char *controller; + while ((p = fgets(buf, MAXPATHLEN, cgroup)) != NULL) { + char *controllers; + char *token; char *base; /* Skip cgroup number */ strsep(&p, ":"); - /* Get controller and base */ - controller = strsep(&p, ":"); + /* Get controllers and base */ + controllers = strsep(&p, ":"); base = strsep(&p, "\n"); - if (controller != NULL) { - if (strstr(controller, "memory") != NULL) { + if (controllers == NULL) { + continue; + } + + while ((token = strsep(&controllers, ",")) != NULL) { + if (strcmp(token, "memory") == 0) { memory->set_subsystem_path(base); - } else if (strstr(controller, "cpuset") != NULL) { + } else if (strcmp(token, "cpuset") == 0) { cpuset->set_subsystem_path(base); - } else if (strstr(controller, "cpu,cpuacct") != NULL || strstr(controller, "cpuacct,cpu") != NULL) { + } else if (strcmp(token, "cpu") == 0) { cpu->set_subsystem_path(base); + } else if (strcmp(token, "cpuacct") == 0) { cpuacct->set_subsystem_path(base); - } else if (strstr(controller, "cpuacct") != NULL) { - cpuacct->set_subsystem_path(base); - } else if (strstr(controller, "cpu") != NULL) { - cpu->set_subsystem_path(base); } } } @@ -584,6 +519,17 @@ int OSContainer::active_processor_count() { int cpu_count, limit_count; int result; + // We use a cache with a timeout to avoid performing expensive + // computations in the event this function is called frequently. + // [See 8227006]. + if (!cpu->cache_has_expired()) { + if (PrintContainerInfo) { + tty->print_cr("OSContainer::active_processor_count (cached): %d", OSContainer::_active_processor_count); + } + + return OSContainer::_active_processor_count; + } + cpu_count = limit_count = os::Linux::active_processor_count(); int quota = cpu_quota(); int period = cpu_period(); @@ -622,6 +568,11 @@ int OSContainer::active_processor_count() { if (PrintContainerInfo) { tty->print_cr("OSContainer::active_processor_count: %d", result); } + + // Update the value and reset the cache timeout + OSContainer::_active_processor_count = result; + cpu->set_cache_expiry_time(OSCONTAINER_CACHE_TIMEOUT); + return result; } diff --git a/src/os/linux/vm/osContainer_linux.hpp b/src/os/linux/vm/osContainer_linux.hpp index 3edeab720e..3505f584c8 100644 --- a/src/os/linux/vm/osContainer_linux.hpp +++ b/src/os/linux/vm/osContainer_linux.hpp @@ -31,11 +31,15 @@ #define OSCONTAINER_ERROR (-2) +// 20ms timeout between re-reads of _active_processor_count. +#define OSCONTAINER_CACHE_TIMEOUT (NANOSECS_PER_SEC/50) + class OSContainer: AllStatic { private: static bool _is_initialized; static bool _is_containerized; + static int _active_processor_count; public: static void init(); diff --git a/src/os/linux/vm/os_linux.cpp b/src/os/linux/vm/os_linux.cpp index 372025020c..0228bdaa0f 100644 --- a/src/os/linux/vm/os_linux.cpp +++ b/src/os/linux/vm/os_linux.cpp @@ -5179,7 +5179,7 @@ jint os::init_2(void) Linux::capture_initial_stack(JavaThread::stack_size_at_create()); -#if defined(IA32) +#if defined(IA32) && !defined(ZERO) workaround_expand_exec_shield_cs_limit(); #endif diff --git a/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp b/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp index ea6f0a0708..3903da074f 100644 --- a/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp @@ -998,18 +998,13 @@ size_t CompactibleFreeListSpace::block_size(const HeapWord* p) const { // and the klass read. OrderAccess::loadload(); - // must read from what 'p' points to in each loop. - Klass* k = ((volatile oopDesc*)p)->klass_or_null(); + // Ensure klass read before size. + Klass* k = oop(p)->klass_or_null_acquire(); if (k != NULL) { assert(k->is_klass(), "Should really be klass oop."); oop o = (oop)p; assert(o->is_oop(true /* ignore mark word */), "Should be an oop."); - // Bugfix for systems with weak memory model (PPC64/IA64). - // The object o may be an array. Acquire to make sure that the array - // size (third word) is consistent. - OrderAccess::acquire(); - size_t res = o->size_given_klass(k); res = adjustObjectSize(res); assert(res != 0, "Block size should not be 0"); @@ -1057,21 +1052,13 @@ const { // and the klass read. OrderAccess::loadload(); - // must read from what 'p' points to in each loop. - Klass* k = ((volatile oopDesc*)p)->klass_or_null(); - // We trust the size of any object that has a non-NULL - // klass and (for those in the perm gen) is parsable - // -- irrespective of its conc_safe-ty. + // Ensure klass read before size. + Klass* k = oop(p)->klass_or_null_acquire(); if (k != NULL) { assert(k->is_klass(), "Should really be klass oop."); oop o = (oop)p; assert(o->is_oop(), "Should be an oop"); - // Bugfix for systems with weak memory model (PPC64/IA64). - // The object o may be an array. Acquire to make sure that the array - // size (third word) is consistent. - OrderAccess::acquire(); - size_t res = o->size_given_klass(k); res = adjustObjectSize(res); assert(res != 0, "Block size should not be 0"); @@ -1124,7 +1111,7 @@ bool CompactibleFreeListSpace::block_is_obj(const HeapWord* p) const { // and the klass read. OrderAccess::loadload(); - Klass* k = oop(p)->klass_or_null(); + Klass* k = oop(p)->klass_or_null_acquire(); if (k != NULL) { // Ignore mark word because it may have been used to // chain together promoted objects (the last one diff --git a/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp b/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp index e0f31a4f29..fb94f455b7 100644 --- a/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp @@ -6741,7 +6741,7 @@ size_t CMSCollector::block_size_if_printezis_bits(HeapWord* addr) const { HeapWord* CMSCollector::next_card_start_after_block(HeapWord* addr) const { size_t sz = 0; oop p = (oop)addr; - if (p->klass_or_null() != NULL) { + if (p->klass_or_null_acquire() != NULL) { sz = CompactibleFreeListSpace::adjustObjectSize(p->size()); } else { sz = block_size_using_printezis_bits(addr); @@ -7199,7 +7199,7 @@ size_t ScanMarkedObjectsAgainCarefullyClosure::do_object_careful_m( } if (_bitMap->isMarked(addr)) { // it's marked; is it potentially uninitialized? - if (p->klass_or_null() != NULL) { + if (p->klass_or_null_acquire() != NULL) { // an initialized object; ignore mark word in verification below // since we are running concurrent with mutators assert(p->is_oop(true), "should be an oop"); @@ -7240,7 +7240,7 @@ size_t ScanMarkedObjectsAgainCarefullyClosure::do_object_careful_m( } } else { // Either a not yet marked object or an uninitialized object - if (p->klass_or_null() == NULL) { + if (p->klass_or_null_acquire() == NULL) { // An uninitialized object, skip to the next card, since // we may not be able to read its P-bits yet. assert(size == 0, "Initial value"); @@ -7451,7 +7451,7 @@ bool MarkFromRootsClosure::do_bit(size_t offset) { assert(_skipBits == 0, "tautology"); _skipBits = 2; // skip next two marked bits ("Printezis-marks") oop p = oop(addr); - if (p->klass_or_null() == NULL) { + if (p->klass_or_null_acquire() == NULL) { DEBUG_ONLY(if (!_verifying) {) // We re-dirty the cards on which this object lies and increase // the _threshold so that we'll come back to scan this object @@ -7471,7 +7471,7 @@ bool MarkFromRootsClosure::do_bit(size_t offset) { if (_threshold < end_card_addr) { _threshold = end_card_addr; } - if (p->klass_or_null() != NULL) { + if (p->klass_or_null_acquire() != NULL) { // Redirty the range of cards... _mut->mark_range(redirty_range); } // ...else the setting of klass will dirty the card anyway. @@ -7622,7 +7622,7 @@ bool Par_MarkFromRootsClosure::do_bit(size_t offset) { assert(_skip_bits == 0, "tautology"); _skip_bits = 2; // skip next two marked bits ("Printezis-marks") oop p = oop(addr); - if (p->klass_or_null() == NULL) { + if (p->klass_or_null_acquire() == NULL) { // in the case of Clean-on-Enter optimization, redirty card // and avoid clearing card by increasing the threshold. return true; @@ -8609,7 +8609,7 @@ size_t SweepClosure::do_live_chunk(FreeChunk* fc) { "alignment problem"); #ifdef ASSERT - if (oop(addr)->klass_or_null() != NULL) { + if (oop(addr)->klass_or_null_acquire() != NULL) { // Ignore mark word because we are running concurrent with mutators assert(oop(addr)->is_oop(true), "live block should be an oop"); assert(size == @@ -8620,7 +8620,7 @@ size_t SweepClosure::do_live_chunk(FreeChunk* fc) { } else { // This should be an initialized object that's alive. - assert(oop(addr)->klass_or_null() != NULL, + assert(oop(addr)->klass_or_null_acquire() != NULL, "Should be an initialized object"); // Ignore mark word because we are running concurrent with mutators assert(oop(addr)->is_oop(true), "live block should be an oop"); diff --git a/src/share/vm/gc_implementation/g1/g1RemSet.cpp b/src/share/vm/gc_implementation/g1/g1RemSet.cpp index da4d632487..4cad9234c4 100644 --- a/src/share/vm/gc_implementation/g1/g1RemSet.cpp +++ b/src/share/vm/gc_implementation/g1/g1RemSet.cpp @@ -568,20 +568,18 @@ bool G1RemSet::refine_card(jbyte* card_ptr, uint worker_i, // fail arbitrarily). We tell the iteration code to perform this // filtering when it has been determined that there has been an actual // allocation in this region and making it safe to check the young type. - bool filter_young = true; - HeapWord* stop_point = + bool card_processed = r->oops_on_card_seq_iterate_careful(dirtyRegion, &filter_then_update_rs_oop_cl, - filter_young, card_ptr); - // If stop_point is non-null, then we encountered an unallocated region - // (perhaps the unfilled portion of a TLAB.) For now, we'll dirty the - // card and re-enqueue: if we put off the card until a GC pause, then the - // unallocated portion will be filled in. Alternatively, we might try - // the full complexity of the technique used in "regular" precleaning. - if (stop_point != NULL) { + // If unable to process the card then we encountered an unparsable + // part of the heap (e.g. a partially allocated object). Redirty + // and re-enqueue: if we put off the card until a GC pause, then the + // allocation will have completed. + if (!card_processed) { + assert(!_g1->is_gc_active(), "Unparsable heap during GC"); // The card might have gotten re-dirtied and re-enqueued while we // worked. (In fact, it's pretty likely.) if (*card_ptr != CardTableModRefBS::dirty_card_val()) { diff --git a/src/share/vm/gc_implementation/g1/heapRegion.cpp b/src/share/vm/gc_implementation/g1/heapRegion.cpp index b9f71069d4..794911ef62 100644 --- a/src/share/vm/gc_implementation/g1/heapRegion.cpp +++ b/src/share/vm/gc_implementation/g1/heapRegion.cpp @@ -407,19 +407,10 @@ HeapRegion::object_iterate_mem_careful(MemRegion mr, return NULL; } -HeapWord* -HeapRegion:: -oops_on_card_seq_iterate_careful(MemRegion mr, - FilterOutOfRegionClosure* cl, - bool filter_young, - jbyte* card_ptr) { - // Currently, we should only have to clean the card if filter_young - // is true and vice versa. - if (filter_young) { - assert(card_ptr != NULL, "pre-condition"); - } else { - assert(card_ptr == NULL, "pre-condition"); - } +bool HeapRegion::oops_on_card_seq_iterate_careful(MemRegion mr, + FilterOutOfRegionClosure* cl, + jbyte* card_ptr) { + assert(card_ptr != NULL, "pre-condition"); G1CollectedHeap* g1h = G1CollectedHeap::heap(); // If we're within a stop-world GC, then we might look at a card in a @@ -430,7 +421,9 @@ oops_on_card_seq_iterate_careful(MemRegion mr, } else { mr = mr.intersection(used_region()); } - if (mr.is_empty()) return NULL; + if (mr.is_empty()) { + return true; + } // Otherwise, find the obj that extends onto mr.start(). // The intersection of the incoming mr (for the card) and the @@ -439,27 +432,21 @@ oops_on_card_seq_iterate_careful(MemRegion mr, // G1CollectedHeap.cpp that allocates a new region sets the // is_young tag on the region before allocating. Thus we // safely know if this region is young. - if (is_young() && filter_young) { - return NULL; + if (is_young()) { + return true; } - assert(!is_young(), "check value of filter_young"); - // We can only clean the card here, after we make the decision that - // the card is not young. And we only clean the card if we have been - // asked to (i.e., card_ptr != NULL). - if (card_ptr != NULL) { - *card_ptr = CardTableModRefBS::clean_card_val(); - // We must complete this write before we do any of the reads below. - OrderAccess::storeload(); - } + // the card is not young. + *card_ptr = CardTableModRefBS::clean_card_val(); + // We must complete this write before we do any of the reads below. + OrderAccess::storeload(); // Cache the boundaries of the memory region in some const locals HeapWord* const start = mr.start(); HeapWord* const end = mr.end(); - // We used to use "block_start_careful" here. But we're actually happy - // to update the BOT while we do this... + // Update BOT as needed while finding start of (potential) object. HeapWord* cur = block_start(start); assert(cur <= start, "Postcondition"); @@ -471,7 +458,9 @@ oops_on_card_seq_iterate_careful(MemRegion mr, obj = oop(cur); if (obj->klass_or_null() == NULL) { // Ran into an unparseable point. - return cur; + assert(!g1h->is_gc_active(), + err_msg("Unparsable heap during GC at " PTR_FORMAT, p2i(cur))); + return false; } // Otherwise... next = cur + block_size(cur); @@ -488,7 +477,9 @@ oops_on_card_seq_iterate_careful(MemRegion mr, assert((cur + block_size(cur)) > (HeapWord*)obj, "Loop invariant"); if (obj->klass_or_null() == NULL) { // Ran into an unparseable point. - return cur; + assert(!g1h->is_gc_active(), + err_msg("Unparsable heap during GC at " PTR_FORMAT, p2i(cur))); + return false; } // Advance the current pointer. "obj" still points to the object to iterate. @@ -507,7 +498,7 @@ oops_on_card_seq_iterate_careful(MemRegion mr, } } while (cur < end); - return NULL; + return true; } // Code roots support diff --git a/src/share/vm/gc_implementation/g1/heapRegion.hpp b/src/share/vm/gc_implementation/g1/heapRegion.hpp index e9146db718..52ef1d0d23 100644 --- a/src/share/vm/gc_implementation/g1/heapRegion.hpp +++ b/src/share/vm/gc_implementation/g1/heapRegion.hpp @@ -718,16 +718,17 @@ class HeapRegion: public G1OffsetTableContigSpace { HeapWord* object_iterate_mem_careful(MemRegion mr, ObjectClosure* cl); - // filter_young: if true and the region is a young region then we - // skip the iteration. - // card_ptr: if not NULL, and we decide that the card is not young - // and we iterate over it, we'll clean the card before we start the - // iteration. - HeapWord* - oops_on_card_seq_iterate_careful(MemRegion mr, - FilterOutOfRegionClosure* cl, - bool filter_young, - jbyte* card_ptr); + // Iterate over the card in the card designated by card_ptr, + // applying cl to all references in the region. + // mr: the memory region covered by the card. + // card_ptr: if we decide that the card is not young and we iterate + // over it, we'll clean the card before we start the iteration. + // Returns true if card was successfully processed, false if an + // unparsable part of the heap was encountered, which should only + // happen when invoked concurrently with the mutator. + bool oops_on_card_seq_iterate_careful(MemRegion mr, + FilterOutOfRegionClosure* cl, + jbyte* card_ptr); size_t recorded_rs_length() const { return _recorded_rs_length; } double predicted_elapsed_time_ms() const { return _predicted_elapsed_time_ms; } diff --git a/src/share/vm/gc_interface/collectedHeap.cpp b/src/share/vm/gc_interface/collectedHeap.cpp index 514469bac3..17a4cfe059 100644 --- a/src/share/vm/gc_interface/collectedHeap.cpp +++ b/src/share/vm/gc_interface/collectedHeap.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2020, 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 @@ -286,8 +286,6 @@ HeapWord* CollectedHeap::allocate_from_tlab_slow(KlassHandle klass, Thread* thre return NULL; } - AllocTracer::send_allocation_in_new_tlab_event(klass, obj, new_tlab_size * HeapWordSize, size * HeapWordSize, Thread::current()); - if (ZeroTLAB) { // ..and clear it. Copy::zero_to_words(obj, new_tlab_size); diff --git a/src/share/vm/gc_interface/collectedHeap.inline.hpp b/src/share/vm/gc_interface/collectedHeap.inline.hpp index 818c88bf83..dcae6713bc 100644 --- a/src/share/vm/gc_interface/collectedHeap.inline.hpp +++ b/src/share/vm/gc_interface/collectedHeap.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2020, 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 @@ -65,8 +65,22 @@ void CollectedHeap::post_allocation_setup_no_klass_install(KlassHandle klass, } } -// Support for jvmti and dtrace +inline void send_jfr_allocation_event(KlassHandle klass, HeapWord* obj, size_t size) { + Thread* t = Thread::current(); + ThreadLocalAllocBuffer& tlab = t->tlab(); + if (obj == tlab.start()) { + // allocate in new TLAB + size_t new_tlab_size = tlab.hard_size_bytes(); + AllocTracer::send_allocation_in_new_tlab_event(klass, obj, new_tlab_size, size * HeapWordSize, t); + } else if (!tlab.in_used(obj)) { + // allocate outside TLAB + AllocTracer::send_allocation_outside_tlab_event(klass, obj, size * HeapWordSize, t); + } +} + +// Support for jvmti, dtrace and jfr inline void post_allocation_notify(KlassHandle klass, oop obj, int size) { + send_jfr_allocation_event(klass, (HeapWord*)obj, size); // support low memory notifications (no-op if not enabled) LowMemoryDetector::detect_low_memory_for_collected_pools(); @@ -137,8 +151,6 @@ HeapWord* CollectedHeap::common_mem_allocate_noinit(KlassHandle klass, size_t si "Unexpected exception, will result in uninitialized storage"); THREAD->incr_allocated_bytes(size * HeapWordSize); - AllocTracer::send_allocation_outside_tlab_event(klass, result, size * HeapWordSize, Thread::current()); - return result; } diff --git a/src/share/vm/jfr/recorder/repository/jfrChunkWriter.cpp b/src/share/vm/jfr/recorder/repository/jfrChunkWriter.cpp index a963966839..64253c7064 100644 --- a/src/share/vm/jfr/recorder/repository/jfrChunkWriter.cpp +++ b/src/share/vm/jfr/recorder/repository/jfrChunkWriter.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019, 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 @@ -46,13 +46,9 @@ bool JfrChunkWriter::initialize() { return _chunkstate != NULL; } -static fio_fd open_existing(const char* path) { - return os::open(path, O_RDWR, S_IREAD | S_IWRITE); -} - static fio_fd open_chunk(const char* path) { assert(JfrStream_lock->owned_by_self(), "invariant"); - return path != NULL ? open_existing(path) : invalid_fd; + return path != NULL ? os::open(path, O_CREAT | O_RDWR, S_IREAD | S_IWRITE) : invalid_fd; } bool JfrChunkWriter::open() { diff --git a/src/share/vm/jfr/recorder/repository/jfrEmergencyDump.cpp b/src/share/vm/jfr/recorder/repository/jfrEmergencyDump.cpp index 3b0ccffbce..debe852b03 100644 --- a/src/share/vm/jfr/recorder/repository/jfrEmergencyDump.cpp +++ b/src/share/vm/jfr/recorder/repository/jfrEmergencyDump.cpp @@ -24,17 +24,314 @@ #include "precompiled.hpp" #include "jfr/jfrEvents.hpp" +#include "jfr/jni/jfrJavaSupport.hpp" #include "jfr/leakprofiler/leakProfiler.hpp" #include "jfr/recorder/repository/jfrEmergencyDump.hpp" #include "jfr/recorder/service/jfrPostBox.hpp" #include "jfr/recorder/service/jfrRecorderService.hpp" #include "jfr/utilities/jfrTypes.hpp" #include "memory/resourceArea.hpp" +#include "runtime/arguments.hpp" #include "runtime/atomic.hpp" #include "runtime/handles.hpp" #include "runtime/globals.hpp" #include "runtime/mutexLocker.hpp" -#include "runtime/thread.hpp" +#include "runtime/os.hpp" +#include "runtime/thread.inline.hpp" +#include "utilities/growableArray.hpp" + +static const char vm_error_filename_fmt[] = "hs_err_pid%p.jfr"; +static const char vm_oom_filename_fmt[] = "hs_oom_pid%p.jfr"; +static const char vm_soe_filename_fmt[] = "hs_soe_pid%p.jfr"; +static const char chunk_file_jfr_ext[] = ".jfr"; +static const size_t iso8601_len = 19; // "YYYY-MM-DDTHH:MM:SS" + +static fio_fd open_exclusivly(const char* path) { + return os::open(path, O_CREAT | O_RDWR, S_IREAD | S_IWRITE); +} + +static int file_sort(const char** const file1, const char** file2) { + assert(NULL != *file1 && NULL != *file2, "invariant"); + int cmp = strncmp(*file1, *file2, iso8601_len); + if (0 == cmp) { + const char* const dot1 = strchr(*file1, '.'); + assert(NULL != dot1, "invariant"); + const char* const dot2 = strchr(*file2, '.'); + assert(NULL != dot2, "invariant"); + ptrdiff_t file1_len = dot1 - *file1; + ptrdiff_t file2_len = dot2 - *file2; + if (file1_len < file2_len) { + return -1; + } + if (file1_len > file2_len) { + return 1; + } + assert(file1_len == file2_len, "invariant"); + cmp = strncmp(*file1, *file2, file1_len); + } + assert(cmp != 0, "invariant"); + return cmp; +} + +static void iso8601_to_date_time(char* iso8601_str) { + assert(iso8601_str != NULL, "invariant"); + assert(strlen(iso8601_str) == iso8601_len, "invariant"); + // "YYYY-MM-DDTHH:MM:SS" + for (size_t i = 0; i < iso8601_len; ++i) { + switch (iso8601_str[i]) { + case 'T': + case '-': + case ':': + iso8601_str[i] = '_'; + break; + } + } + // "YYYY_MM_DD_HH_MM_SS" +} + +static void date_time(char* buffer, size_t buffer_len) { + assert(buffer != NULL, "invariant"); + assert(buffer_len >= iso8601_len, "buffer too small"); + os::iso8601_time(buffer, buffer_len); + assert(strlen(buffer) >= iso8601_len + 1, "invariant"); + // "YYYY-MM-DDTHH:MM:SS" + buffer[iso8601_len] = '\0'; + iso8601_to_date_time(buffer); +} + +static int64_t file_size(fio_fd fd) { + assert(fd != invalid_fd, "invariant"); + const int64_t current_offset = os::current_file_offset(fd); + const int64_t size = os::lseek(fd, 0, SEEK_END); + os::seek_to_file_offset(fd, current_offset); + return size; +} + +class RepositoryIterator : public StackObj { + private: + const char* const _repo; + const size_t _repository_len; + GrowableArray* _files; + const char* const fully_qualified(const char* entry) const; + mutable int _iterator; + + public: + RepositoryIterator(const char* repository, size_t repository_len); + ~RepositoryIterator() {} + const char* const filter(const char* entry) const; + bool has_next() const; + const char* const next() const; +}; + +const char* const RepositoryIterator::fully_qualified(const char* entry) const { + assert(NULL != entry, "invariant"); + char* file_path_entry = NULL; + // only use files that have content, not placeholders + const char* const file_separator = os::file_separator(); + if (NULL != file_separator) { + const size_t entry_len = strlen(entry); + const size_t file_separator_length = strlen(file_separator); + const size_t file_path_entry_length = _repository_len + file_separator_length + entry_len; + file_path_entry = NEW_RESOURCE_ARRAY_RETURN_NULL(char, file_path_entry_length + 1); + if (NULL == file_path_entry) { + return NULL; + } + int position = 0; + position += jio_snprintf(&file_path_entry[position], _repository_len + 1, "%s", _repo); + position += jio_snprintf(&file_path_entry[position], file_separator_length + 1, "%s", os::file_separator()); + position += jio_snprintf(&file_path_entry[position], entry_len + 1, "%s", entry); + file_path_entry[position] = '\0'; + assert((size_t)position == file_path_entry_length, "invariant"); + assert(strlen(file_path_entry) == (size_t)position, "invariant"); + } + return file_path_entry; +} + +const char* const RepositoryIterator::filter(const char* entry) const { + if (entry == NULL) { + return NULL; + } + const size_t entry_len = strlen(entry); + if (entry_len <= 2) { + // for "." and ".." + return NULL; + } + char* entry_name = NEW_RESOURCE_ARRAY_RETURN_NULL(char, entry_len + 1); + if (entry_name == NULL) { + return NULL; + } + strncpy(entry_name, entry, entry_len + 1); + const char* const fully_qualified_path_entry = fully_qualified(entry_name); + if (NULL == fully_qualified_path_entry) { + return NULL; + } + const fio_fd entry_fd = open_exclusivly(fully_qualified_path_entry); + if (invalid_fd == entry_fd) { + return NULL; + } + const int64_t entry_size = file_size(entry_fd); + os::close(entry_fd); + if (0 == entry_size) { + return NULL; + } + return entry_name; +} + +RepositoryIterator::RepositoryIterator(const char* repository, size_t repository_len) : + _repo(repository), + _repository_len(repository_len), + _files(NULL), + _iterator(0) { + if (NULL != _repo) { + assert(strlen(_repo) == _repository_len, "invariant"); + _files = new GrowableArray(10); + DIR* dirp = os::opendir(_repo); + if (dirp == NULL) { + if (true) tty->print_cr("Unable to open repository %s", _repo); + return; + } + struct dirent* dentry; + while ((dentry = os::readdir(dirp)) != NULL) { + const char* const entry_path = filter(dentry->d_name); + if (NULL != entry_path) { + _files->append(entry_path); + } + } + os::closedir(dirp); + if (_files->length() > 1) { + _files->sort(file_sort); + } + } +} + +bool RepositoryIterator::has_next() const { + return (_files != NULL && _iterator < _files->length()); +} + +const char* const RepositoryIterator::next() const { + return _iterator >= _files->length() ? NULL : fully_qualified(_files->at(_iterator++)); +} + +static void write_emergency_file(fio_fd emergency_fd, const RepositoryIterator& iterator) { + assert(emergency_fd != invalid_fd, "invariant"); + const size_t size_of_file_copy_block = 1 * M; // 1 mb + jbyte* const file_copy_block = NEW_RESOURCE_ARRAY_RETURN_NULL(jbyte, size_of_file_copy_block); + if (file_copy_block == NULL) { + return; + } + while (iterator.has_next()) { + fio_fd current_fd = invalid_fd; + const char* const fqn = iterator.next(); + if (fqn != NULL) { + current_fd = open_exclusivly(fqn); + if (current_fd != invalid_fd) { + const int64_t current_filesize = file_size(current_fd); + assert(current_filesize > 0, "invariant"); + int64_t bytes_read = 0; + int64_t bytes_written = 0; + while (bytes_read < current_filesize) { + const ssize_t read_result = os::read_at(current_fd, file_copy_block, size_of_file_copy_block, bytes_read); + if (-1 == read_result) { + if (LogJFR) tty->print_cr("Unable to recover JFR data"); + break; + } + bytes_read += (int64_t)read_result; + assert(bytes_read - bytes_written <= (int64_t)size_of_file_copy_block, "invariant"); + bytes_written += (int64_t)os::write(emergency_fd, file_copy_block, bytes_read - bytes_written); + assert(bytes_read == bytes_written, "invariant"); + } + os::close(current_fd); + } + } + } +} + +static const char* create_emergency_dump_path() { + assert(JfrStream_lock->owned_by_self(), "invariant"); + char* buffer = NEW_RESOURCE_ARRAY_RETURN_NULL(char, JVM_MAXPATHLEN); + if (NULL == buffer) { + return NULL; + } + const char* const cwd = os::get_current_directory(buffer, JVM_MAXPATHLEN); + if (NULL == cwd) { + return NULL; + } + size_t pos = strlen(cwd); + const int fsep_len = jio_snprintf(&buffer[pos], JVM_MAXPATHLEN - pos, "%s", os::file_separator()); + const char* filename_fmt = NULL; + // fetch specific error cause + switch (JfrJavaSupport::cause()) { + case JfrJavaSupport::OUT_OF_MEMORY: + filename_fmt = vm_oom_filename_fmt; + break; + case JfrJavaSupport::STACK_OVERFLOW: + filename_fmt = vm_soe_filename_fmt; + break; + default: + filename_fmt = vm_error_filename_fmt; + } + char* emergency_dump_path = NULL; + pos += fsep_len; + if (Arguments::copy_expand_pid(filename_fmt, strlen(filename_fmt), &buffer[pos], JVM_MAXPATHLEN - pos)) { + const size_t emergency_filename_length = strlen(buffer); + emergency_dump_path = NEW_RESOURCE_ARRAY_RETURN_NULL(char, emergency_filename_length + 1); + if (NULL == emergency_dump_path) { + return NULL; + } + strncpy(emergency_dump_path, buffer, emergency_filename_length + 1); + } + if (emergency_dump_path != NULL) { + if (LogJFR) tty->print_cr( // For user, should not be "jfr, system" + "Attempting to recover JFR data, emergency jfr file: %s", emergency_dump_path); + } + return emergency_dump_path; +} + +// Caller needs ResourceMark +static const char* create_emergency_chunk_path(const char* repository_path) { + assert(repository_path != NULL, "invariant"); + assert(JfrStream_lock->owned_by_self(), "invariant"); + const size_t repository_path_len = strlen(repository_path); + // date time + char date_time_buffer[32] = { 0 }; + date_time(date_time_buffer, sizeof(date_time_buffer)); + size_t date_time_len = strlen(date_time_buffer); + size_t chunkname_max_len = repository_path_len // repository_base_path + + 1 // "/" + + date_time_len // date_time + + strlen(chunk_file_jfr_ext) // .jfr + + 1; + char* chunk_path = NEW_RESOURCE_ARRAY_RETURN_NULL(char, chunkname_max_len); + if (chunk_path == NULL) { + return NULL; + } + // append the individual substrings + jio_snprintf(chunk_path, chunkname_max_len, "%s%s%s%s", repository_path_len, os::file_separator(), date_time_buffer, chunk_file_jfr_ext); + return chunk_path; +} + +static fio_fd emergency_dump_file_descriptor() { + assert(JfrStream_lock->owned_by_self(), "invariant"); + ResourceMark rm; + const char* const emergency_dump_path = create_emergency_dump_path(); + return emergency_dump_path != NULL ? open_exclusivly(emergency_dump_path) : invalid_fd; +} + +const char* JfrEmergencyDump::build_dump_path(const char* repository_path) { + return repository_path == NULL ? create_emergency_dump_path() : create_emergency_chunk_path(repository_path); +} + +void JfrEmergencyDump::on_vm_error(const char* repository_path) { + assert(repository_path != NULL, "invariant"); + ResourceMark rm; + MutexLockerEx stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag); + const fio_fd emergency_fd = emergency_dump_file_descriptor(); + if (emergency_fd != invalid_fd) { + RepositoryIterator iterator(repository_path, strlen(repository_path)); + write_emergency_file(emergency_fd, iterator); + os::close(emergency_fd); + } +} /* * We are just about to exit the VM, so we will be very aggressive diff --git a/src/share/vm/jfr/recorder/repository/jfrEmergencyDump.hpp b/src/share/vm/jfr/recorder/repository/jfrEmergencyDump.hpp index 8b18f42e78..f487cea0da 100644 --- a/src/share/vm/jfr/recorder/repository/jfrEmergencyDump.hpp +++ b/src/share/vm/jfr/recorder/repository/jfrEmergencyDump.hpp @@ -33,6 +33,8 @@ class JfrEmergencyDump : AllStatic { public: static void on_vm_shutdown(bool exception_handler); + static void on_vm_error(const char* repository_path); + static const char* build_dump_path(const char* repository_path); }; #endif // SHARE_VM_JFR_RECORDER_INTERNAL_JFREMERGENCY_HPP diff --git a/src/share/vm/jfr/recorder/repository/jfrRepository.cpp b/src/share/vm/jfr/recorder/repository/jfrRepository.cpp index 7b9d9b18b7..f2acb84780 100644 --- a/src/share/vm/jfr/recorder/repository/jfrRepository.cpp +++ b/src/share/vm/jfr/recorder/repository/jfrRepository.cpp @@ -28,12 +28,12 @@ #include "jfr/recorder/jfrRecorder.hpp" #include "jfr/recorder/repository/jfrChunkState.hpp" #include "jfr/recorder/repository/jfrChunkWriter.hpp" +#include "jfr/recorder/repository/jfrEmergencyDump.hpp" #include "jfr/recorder/repository/jfrRepository.hpp" #include "jfr/recorder/service/jfrPostBox.hpp" #include "memory/resourceArea.hpp" #include "runtime/mutex.hpp" #include "runtime/arguments.hpp" -#include "runtime/os.hpp" #include "runtime/thread.inline.hpp" static JfrRepository* _instance = NULL; @@ -84,320 +84,13 @@ void JfrRepository::destroy() { _instance = NULL; } -static const char vm_error_filename_fmt[] = "hs_err_pid%p.jfr"; -static const char vm_oom_filename_fmt[] = "hs_oom_pid%p.jfr"; -static const char vm_soe_filename_fmt[] = "hs_soe_pid%p.jfr"; -static const char chunk_file_jfr_ext[] = ".jfr"; -static const size_t iso8601_len = 19; // "YYYY-MM-DDTHH:MM:SS" - -static fio_fd open_exclusivly(const char* path) { - return os::open(path, O_CREAT | O_WRONLY, S_IREAD | S_IWRITE); -} - -static fio_fd open_existing(const char* path) { - return os::open(path, O_RDWR, S_IREAD | S_IWRITE); -} - -static int file_sort(const char** const file1, const char** file2) { - assert(NULL != *file1 && NULL != *file2, "invariant"); - int cmp = strncmp(*file1, *file2, iso8601_len); - if (0 == cmp) { - const char* const dot1 = strchr(*file1, '.'); - assert(NULL != dot1, "invariant"); - const char* const dot2 = strchr(*file2, '.'); - assert(NULL != dot2, "invariant"); - ptrdiff_t file1_len = dot1 - *file1; - ptrdiff_t file2_len = dot2 - *file2; - if (file1_len < file2_len) { - return -1; - } - if (file1_len > file2_len) { - return 1; - } - assert(file1_len == file2_len, "invariant"); - cmp = strncmp(*file1, *file2, file1_len); - } - assert(cmp != 0, "invariant"); - return cmp; -} - -static void iso8601_to_date_time(char* iso8601_str) { - assert(iso8601_str != NULL, "invariant"); - assert(strlen(iso8601_str) == iso8601_len, "invariant"); - // "YYYY-MM-DDTHH:MM:SS" - for (size_t i = 0; i < iso8601_len; ++i) { - switch(iso8601_str[i]) { - case 'T' : - case '-' : - case ':' : - iso8601_str[i] = '_'; - break; - } - } - // "YYYY_MM_DD_HH_MM_SS" -} - -static void date_time(char* buffer, size_t buffer_len) { - assert(buffer != NULL, "invariant"); - assert(buffer_len >= iso8601_len, "buffer too small"); - os::iso8601_time(buffer, buffer_len); - assert(strlen(buffer) >= iso8601_len + 1, "invariant"); - // "YYYY-MM-DDTHH:MM:SS" - buffer[iso8601_len] = '\0'; - iso8601_to_date_time(buffer); -} - -static int64_t file_size(fio_fd fd) { - assert(fd != invalid_fd, "invariant"); - const int64_t current_offset = os::current_file_offset(fd); - const int64_t size = os::lseek(fd, 0, SEEK_END); - os::seek_to_file_offset(fd, current_offset); - return size; -} - -class RepositoryIterator : public StackObj { - private: - const char* const _repo; - const size_t _repository_len; - GrowableArray* _files; - const char* const fully_qualified(const char* entry) const; - mutable int _iterator; - - public: - RepositoryIterator(const char* repository, size_t repository_len); - ~RepositoryIterator() {} - debug_only(void print_repository_files() const;) - const char* const filter(const char* entry) const; - bool has_next() const; - const char* const next() const; -}; - -const char* const RepositoryIterator::fully_qualified(const char* entry) const { - assert(NULL != entry, "invariant"); - char* file_path_entry = NULL; - // only use files that have content, not placeholders - const char* const file_separator = os::file_separator(); - if (NULL != file_separator) { - const size_t entry_len = strlen(entry); - const size_t file_separator_length = strlen(file_separator); - const size_t file_path_entry_length = _repository_len + file_separator_length + entry_len; - file_path_entry = NEW_RESOURCE_ARRAY_RETURN_NULL(char, file_path_entry_length + 1); - if (NULL == file_path_entry) { - return NULL; - } - int position = 0; - position += jio_snprintf(&file_path_entry[position], _repository_len + 1, "%s", _repo); - position += jio_snprintf(&file_path_entry[position], file_separator_length + 1, "%s", os::file_separator()); - position += jio_snprintf(&file_path_entry[position], entry_len + 1, "%s", entry); - file_path_entry[position] = '\0'; - assert((size_t)position == file_path_entry_length, "invariant"); - assert(strlen(file_path_entry) == (size_t)position, "invariant"); - } - return file_path_entry; -} - -const char* const RepositoryIterator::filter(const char* entry) const { - if (entry == NULL) { - return NULL; - } - const size_t entry_len = strlen(entry); - if (entry_len <= 2) { - // for "." and ".." - return NULL; - } - char* entry_name = NEW_RESOURCE_ARRAY_RETURN_NULL(char, entry_len + 1); - if (entry_name == NULL) { - return NULL; - } - strncpy(entry_name, entry, entry_len + 1); - const char* const fully_qualified_path_entry = fully_qualified(entry_name); - if (NULL == fully_qualified_path_entry) { - return NULL; - } - const fio_fd entry_fd = open_existing(fully_qualified_path_entry); - if (invalid_fd == entry_fd) { - return NULL; - } - const int64_t entry_size = file_size(entry_fd); - os::close(entry_fd); - if (0 == entry_size) { - return NULL; - } - return entry_name; -} - -RepositoryIterator::RepositoryIterator(const char* repository, size_t repository_len) : - _repo(repository), - _repository_len(repository_len), - _files(NULL), - _iterator(0) { - if (NULL != _repo) { - assert(strlen(_repo) == _repository_len, "invariant"); - _files = new GrowableArray(10); - DIR* dirp = os::opendir(_repo); - if (dirp == NULL) { - if (true) tty->print_cr("Unable to open repository %s", _repo); - return; - } - struct dirent* dentry; - while ((dentry = os::readdir(dirp)) != NULL) { - const char* const entry_path = filter(dentry->d_name); - if (NULL != entry_path) { - _files->append(entry_path); - } - } - os::closedir(dirp); - if (_files->length() > 1) { - _files->sort(file_sort); - } - } -} - -#ifdef ASSERT -void RepositoryIterator::print_repository_files() const { - while (has_next()) { - if (true) tty->print_cr( "%s", next()); - } -} -#endif - -bool RepositoryIterator::has_next() const { - return (_files != NULL && _iterator < _files->length()); -} - -const char* const RepositoryIterator::next() const { - return _iterator >= _files->length() ? NULL : fully_qualified(_files->at(_iterator++)); -} - -static void write_emergency_file(fio_fd emergency_fd, const RepositoryIterator& iterator) { - assert(emergency_fd != invalid_fd, "invariant"); - const size_t size_of_file_copy_block = 1 * M; // 1 mb - jbyte* const file_copy_block = NEW_RESOURCE_ARRAY_RETURN_NULL(jbyte, size_of_file_copy_block); - if (file_copy_block == NULL) { - return; - } - int64_t bytes_written_total = 0; - while (iterator.has_next()) { - fio_fd current_fd = invalid_fd; - const char* const fqn = iterator.next(); - if (fqn != NULL) { - current_fd = open_existing(fqn); - if (current_fd != invalid_fd) { - const int64_t current_filesize = file_size(current_fd); - assert(current_filesize > 0, "invariant"); - int64_t bytes_read = 0; - int64_t bytes_written = 0; - while (bytes_read < current_filesize) { - const ssize_t read_result = os::read_at(current_fd, file_copy_block, size_of_file_copy_block, bytes_read); - if (-1 == read_result) { - if (LogJFR) tty->print_cr("Unable to recover JFR data"); - break; - } - bytes_read += (int64_t)read_result; - assert(bytes_read - bytes_written <= (int64_t)size_of_file_copy_block, "invariant"); - bytes_written += (int64_t)os::write(emergency_fd, file_copy_block, bytes_read - bytes_written); - assert(bytes_read == bytes_written, "invariant"); - } - os::close(current_fd); - bytes_written_total += bytes_written; - } - } - } -} - -static const char* create_emergency_dump_path() { - assert(JfrStream_lock->owned_by_self(), "invariant"); - char* buffer = NEW_RESOURCE_ARRAY_RETURN_NULL(char, O_BUFLEN); - if (NULL == buffer) { - return NULL; - } - const char* const cwd = os::get_current_directory(buffer, O_BUFLEN); - if (NULL == cwd) { - return NULL; - } - size_t pos = strlen(cwd); - const int fsep_len = jio_snprintf(&buffer[pos], O_BUFLEN - pos, "%s", os::file_separator()); - const char* filename_fmt = NULL; - // fetch specific error cause - switch (JfrJavaSupport::cause()) { - case JfrJavaSupport::OUT_OF_MEMORY: - filename_fmt = vm_oom_filename_fmt; - break; - case JfrJavaSupport::STACK_OVERFLOW: - filename_fmt = vm_soe_filename_fmt; - break; - default: - filename_fmt = vm_error_filename_fmt; - } - char* emergency_dump_path = NULL; - pos += fsep_len; - if (Arguments::copy_expand_pid(filename_fmt, strlen(filename_fmt), &buffer[pos], O_BUFLEN - pos)) { - const size_t emergency_filename_length = strlen(buffer); - emergency_dump_path = NEW_RESOURCE_ARRAY_RETURN_NULL(char, emergency_filename_length + 1); - if (NULL == emergency_dump_path) { - return NULL; - } - strncpy(emergency_dump_path, buffer, emergency_filename_length + 1); - } - return emergency_dump_path; -} - -// Caller needs ResourceMark -static const char* create_emergency_chunk_path(const char* repository_base, size_t repository_len) { - assert(repository_base != NULL, "invariant"); - assert(JfrStream_lock->owned_by_self(), "invariant"); - // date time - char date_time_buffer[32] = {0}; - date_time(date_time_buffer, sizeof(date_time_buffer)); - size_t date_time_len = strlen(date_time_buffer); - size_t chunkname_max_len = repository_len // repository_base - + 1 // "/" - + date_time_len // date_time - + strlen(chunk_file_jfr_ext) // .jfr - + 1; - char* chunk_path = NEW_RESOURCE_ARRAY_RETURN_NULL(char, chunkname_max_len); - if (chunk_path == NULL) { - return NULL; - } - // append the individual substrings - jio_snprintf(chunk_path, chunkname_max_len, "%s%s%s%s", repository_base, os::file_separator(), date_time_buffer, chunk_file_jfr_ext); - return chunk_path; -} - -static fio_fd emergency_dump_file() { - assert(JfrStream_lock->owned_by_self(), "invariant"); - ResourceMark rm; - const char* const emergency_dump_path = create_emergency_dump_path(); - if (emergency_dump_path == NULL) { - return invalid_fd; - } - const fio_fd fd = open_exclusivly(emergency_dump_path); - if (fd != invalid_fd) { - if (LogJFR) tty->print_cr( // For user, should not be "jfr, system" - "Attempting to recover JFR data, emergency jfr file: %s", emergency_dump_path); - } - return fd; -} - -static const char* emergency_path(const char* repository, size_t repository_len) { - return repository == NULL ? create_emergency_dump_path() : create_emergency_chunk_path(repository, repository_len); -} - void JfrRepository::on_vm_error() { assert(!JfrStream_lock->owned_by_self(), "invariant"); - const char* path = _path; - if (path == NULL) { + if (_path == NULL) { // completed already return; } - ResourceMark rm; - MutexLockerEx stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag); - const fio_fd emergency_fd = emergency_dump_file(); - if (emergency_fd != invalid_fd) { - RepositoryIterator iterator(path, strlen(path)); - write_emergency_file(emergency_fd, iterator); - os::close(emergency_fd); - } + JfrEmergencyDump::on_vm_error(_path); } bool JfrRepository::set_path(const char* path) { @@ -466,10 +159,7 @@ bool JfrRepository::open_chunk(bool vm_error /* false */) { assert(JfrStream_lock->owned_by_self(), "invariant"); if (vm_error) { ResourceMark rm; - const char* repository_path = _path; - const size_t repository_path_len = repository_path != NULL ? strlen(repository_path) : 0; - const char* const path = emergency_path(repository_path, repository_path_len); - _chunkwriter->set_chunk_path(path); + _chunkwriter->set_chunk_path(JfrEmergencyDump::build_dump_path(_path)); } return _chunkwriter->open(); } diff --git a/src/share/vm/memory/genCollectedHeap.cpp b/src/share/vm/memory/genCollectedHeap.cpp index 8021878e41..8c1805a8c1 100644 --- a/src/share/vm/memory/genCollectedHeap.cpp +++ b/src/share/vm/memory/genCollectedHeap.cpp @@ -1197,7 +1197,7 @@ GenCollectedHeap* GenCollectedHeap::heap() { void GenCollectedHeap::prepare_for_compaction() { - guarantee(_n_gens = 2, "Wrong number of generations"); + guarantee(_n_gens == 2, "Wrong number of generations"); Generation* old_gen = _gens[1]; // Start by compacting into same gen. CompactPoint cp(old_gen); diff --git a/src/share/vm/memory/threadLocalAllocBuffer.hpp b/src/share/vm/memory/threadLocalAllocBuffer.hpp index 07308ff7ed..06eee94ba3 100644 --- a/src/share/vm/memory/threadLocalAllocBuffer.hpp +++ b/src/share/vm/memory/threadLocalAllocBuffer.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2020, 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 @@ -120,6 +120,8 @@ class ThreadLocalAllocBuffer: public CHeapObj { size_t free() const { return pointer_delta(end(), top()); } // Don't discard tlab if remaining space is larger than this. size_t refill_waste_limit() const { return _refill_waste_limit; } + size_t hard_size_bytes() const { return pointer_delta(hard_end(), start(), 1); } + bool in_used(HeapWord* addr) const { return addr >= start() && addr < top(); } // Allocate size HeapWords. The memory is NOT initialized to zero. inline HeapWord* allocate(size_t size); diff --git a/src/share/vm/oops/oop.hpp b/src/share/vm/oops/oop.hpp index 94c35ad58d..f326eef86c 100644 --- a/src/share/vm/oops/oop.hpp +++ b/src/share/vm/oops/oop.hpp @@ -83,6 +83,7 @@ class oopDesc { Klass* klass() const; Klass* klass_or_null() const volatile; + Klass* klass_or_null_acquire() const volatile; Klass** klass_addr(); narrowKlass* compressed_klass_addr(); diff --git a/src/share/vm/oops/oop.inline.hpp b/src/share/vm/oops/oop.inline.hpp index 6c041e86bf..5374cc9cd7 100644 --- a/src/share/vm/oops/oop.inline.hpp +++ b/src/share/vm/oops/oop.inline.hpp @@ -78,7 +78,6 @@ inline Klass* oopDesc::klass() const { } inline Klass* oopDesc::klass_or_null() const volatile { - // can be NULL in CMS if (UseCompressedClassPointers) { return Klass::decode_klass(_metadata._compressed_klass); } else { @@ -86,6 +85,17 @@ inline Klass* oopDesc::klass_or_null() const volatile { } } +inline Klass* oopDesc::klass_or_null_acquire() const volatile { + if (UseCompressedClassPointers) { + // Workaround for non-const load_acquire parameter. + const volatile narrowKlass* addr = &_metadata._compressed_klass; + volatile narrowKlass* xaddr = const_cast(addr); + return Klass::decode_klass(OrderAccess::load_acquire(xaddr)); + } else { + return (Klass*)OrderAccess::load_ptr_acquire(&_metadata._klass); + } +} + inline int oopDesc::klass_gap_offset_in_bytes() { assert(UseCompressedClassPointers, "only applicable to compressed klass pointers"); return oopDesc::klass_offset_in_bytes() + sizeof(narrowKlass); diff --git a/src/share/vm/opto/c2_globals.hpp b/src/share/vm/opto/c2_globals.hpp index 96b5cd1648..7cf36dbd7a 100644 --- a/src/share/vm/opto/c2_globals.hpp +++ b/src/share/vm/opto/c2_globals.hpp @@ -60,10 +60,10 @@ #define C2_FLAGS(develop, develop_pd, product, product_pd, diagnostic, experimental, notproduct) \ \ - develop(bool, StressLCM, false, \ + diagnostic(bool, StressLCM, false, \ "Randomize instruction scheduling in LCM") \ \ - develop(bool, StressGCM, false, \ + diagnostic(bool, StressGCM, false, \ "Randomize instruction scheduling in GCM") \ \ notproduct(intx, CompileZapFirst, 0, \ diff --git a/src/share/vm/runtime/objectMonitor.cpp b/src/share/vm/runtime/objectMonitor.cpp index 7b1983953b..87617d00b3 100644 --- a/src/share/vm/runtime/objectMonitor.cpp +++ b/src/share/vm/runtime/objectMonitor.cpp @@ -2316,6 +2316,7 @@ ObjectWaiter::ObjectWaiter(Thread* thread) { _next = NULL; _prev = NULL; _notified = 0; + _notifier_tid = 0; TState = TS_RUN ; _thread = thread; _event = thread->_ParkEvent ; diff --git a/src/share/vm/runtime/os.cpp b/src/share/vm/runtime/os.cpp index 27b02f1a9b..ff44c20707 100644 --- a/src/share/vm/runtime/os.cpp +++ b/src/share/vm/runtime/os.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, 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 @@ -96,18 +96,6 @@ void os_init_globals() { os::init_globals(); } -static time_t get_timezone(const struct tm* time_struct) { -#if defined(_ALLBSD_SOURCE) - return time_struct->tm_gmtoff; -#elif defined(_WINDOWS) - long zone; - _get_timezone(&zone); - return static_cast(zone); -#else - return timezone; -#endif -} - int os::snprintf(char* buf, size_t len, const char* fmt, ...) { va_list args; va_start(args, fmt); @@ -154,17 +142,31 @@ char* os::iso8601_time(char* buffer, size_t buffer_length) { assert(false, "Failed localtime_pd"); return NULL; } - const time_t zone = get_timezone(&time_struct); - // If daylight savings time is in effect, - // we are 1 hour East of our time zone const time_t seconds_per_minute = 60; const time_t minutes_per_hour = 60; const time_t seconds_per_hour = seconds_per_minute * minutes_per_hour; - time_t UTC_to_local = zone; + + time_t UTC_to_local = 0; +#if defined(_ALLBSD_SOURCE) || defined(_GNU_SOURCE) + UTC_to_local = -(time_struct.tm_gmtoff); +#elif defined(_WINDOWS) + long zone; + _get_timezone(&zone); + UTC_to_local = static_cast(zone); +#else + UTC_to_local = timezone; +#endif + + // tm_gmtoff already includes adjustment for daylight saving +#if !defined(_ALLBSD_SOURCE) && !defined(_GNU_SOURCE) + // If daylight savings time is in effect, + // we are 1 hour East of our time zone if (time_struct.tm_isdst > 0) { UTC_to_local = UTC_to_local - seconds_per_hour; } +#endif + // Compute the time zone offset. // localtime_pd() sets timezone to the difference (in seconds) // between UTC and and local time. diff --git a/test/compiler/5091921/Test7005594.sh b/test/compiler/5091921/Test7005594.sh index 8d9fc36ead..2ca434ae87 100644 --- a/test/compiler/5091921/Test7005594.sh +++ b/test/compiler/5091921/Test7005594.sh @@ -78,7 +78,7 @@ cp ${TESTSRC}/Test7005594.sh . ${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} -d . Test7005594.java -${TESTJAVA}/bin/java ${TESTVMOPTS} -Xms1600m -XX:+IgnoreUnrecognizedVMOptions -XX:-ZapUnusedHeapArea -Xcomp -XX:CompileOnly=Test7005594.test Test7005594 > test.out 2>&1 +${TESTJAVA}/bin/java ${TESTVMOPTS} -Xmx1600m -Xms1600m -XX:+IgnoreUnrecognizedVMOptions -XX:-ZapUnusedHeapArea -Xcomp -XX:CompileOnly=Test7005594.test Test7005594 > test.out 2>&1 result=$? @@ -97,7 +97,7 @@ then fi # The test should pass when no enough space for object heap -grep "Could not reserve enough space for object heap" test.out +grep "Could not reserve enough space for .*object heap" test.out if [ $? = 0 ] then echo "Passed" diff --git a/test/compiler/membars/DekkerTest.java b/test/compiler/membars/DekkerTest.java index f4f2826b6e..65e8cd4248 100644 --- a/test/compiler/membars/DekkerTest.java +++ b/test/compiler/membars/DekkerTest.java @@ -25,9 +25,15 @@ * @test * @bug 8007898 * @summary Incorrect optimization of Memory Barriers in Matcher::post_store_load_barrier(). - * @run main/othervm -Xbatch -XX:+IgnoreUnrecognizedVMOptions -XX:CICompilerCount=1 -XX:-TieredCompilation -XX:+StressGCM -XX:+StressLCM DekkerTest - * @run main/othervm -Xbatch -XX:+IgnoreUnrecognizedVMOptions -XX:CICompilerCount=1 -XX:-TieredCompilation -XX:+StressGCM -XX:+StressLCM DekkerTest - * @run main/othervm -Xbatch -XX:+IgnoreUnrecognizedVMOptions -XX:CICompilerCount=1 -XX:-TieredCompilation -XX:+StressGCM -XX:+StressLCM DekkerTest + * @run main/othervm -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+IgnoreUnrecognizedVMOptions + * -XX:-TieredCompilation -XX:CICompilerCount=1 -XX:+StressGCM -XX:+StressLCM + * DekkerTest + * @run main/othervm -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+IgnoreUnrecognizedVMOptions + * -XX:-TieredCompilation -XX:CICompilerCount=1 -XX:+StressGCM -XX:+StressLCM + * DekkerTest + * @run main/othervm -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+IgnoreUnrecognizedVMOptions + * -XX:-TieredCompilation -XX:CICompilerCount=1 -XX:+StressGCM -XX:+StressLCM + * DekkerTest * @author Martin Doerr martin DOT doerr AT sap DOT com * * Run 3 times since the failure is intermittent. diff --git a/test/runtime/containers/docker/DockerBasicTest.java b/test/runtime/containers/docker/DockerBasicTest.java index fe5d441793..fb89c9f4d6 100644 --- a/test/runtime/containers/docker/DockerBasicTest.java +++ b/test/runtime/containers/docker/DockerBasicTest.java @@ -37,8 +37,6 @@ public class DockerBasicTest { private static final String imageNameAndTag = "jdk8-internal:test"; - // Diganostics: set to false to examine image after the test - private static final boolean removeImageAfterTest = true; public static void main(String[] args) throws Exception { if (!DockerTestUtils.canTestDocker()) { @@ -50,8 +48,9 @@ public static void main(String[] args) throws Exception { testJavaVersion(); testHelloDocker(); } finally { - if (removeImageAfterTest) + if (!DockerTestUtils.RETAIN_IMAGE_AFTER_TEST) { DockerTestUtils.removeDockerImage(imageNameAndTag); + } } } diff --git a/test/testlibrary/com/oracle/java/testlibrary/DockerTestUtils.java b/test/testlibrary/com/oracle/java/testlibrary/DockerTestUtils.java index 438e7cca0f..70b327e967 100644 --- a/test/testlibrary/com/oracle/java/testlibrary/DockerTestUtils.java +++ b/test/testlibrary/com/oracle/java/testlibrary/DockerTestUtils.java @@ -47,8 +47,23 @@ public class DockerTestUtils { private static boolean isDockerEngineAvailable = false; private static boolean wasDockerEngineChecked = false; - // Diagnostics: set to true to enable more diagnostic info - private static final boolean DEBUG = false; + // Use this property to specify docker location on your system. + // E.g.: "/usr/local/bin/docker". + private static final String DOCKER_COMMAND = + System.getProperty("jdk.test.docker.command", "docker"); + + // Set this property to true to retain image after test. By default + // images are removed after test execution completes. + // Retaining the image can be useful for diagnostics and image inspection. + // E.g.: start image interactively: docker run -it . + public static final boolean RETAIN_IMAGE_AFTER_TEST = + Boolean.getBoolean("jdk.test.docker.retain.image"); + + // Path to a JDK under test. + // This may be useful when developing tests on non-Linux platforms. + public static final String JDK_UNDER_TEST = + System.getProperty("jdk.test.docker.jdk", Utils.TEST_JDK); + /** * Optimized check of whether the docker engine is available in a given @@ -96,7 +111,7 @@ public static boolean canTestDocker() throws Exception { */ private static boolean isDockerEngineAvailableCheck() throws Exception { try { - execute("docker", "ps") + execute(DOCKER_COMMAND, "ps") .shouldHaveExitValue(0) .shouldContain("CONTAINER") .shouldContain("IMAGE"); @@ -127,7 +142,7 @@ private static boolean isDockerEngineAvailableCheck() throws Exception { throw new RuntimeException("The docker build directory already exists: " + buildDir); } - Path jdkSrcDir = Paths.get(Utils.TEST_JDK); + Path jdkSrcDir = Paths.get(JDK_UNDER_TEST); Path jdkDstDir = buildDir.resolve("jdk"); Files.createDirectories(jdkDstDir); @@ -157,7 +172,7 @@ private static boolean isDockerEngineAvailableCheck() throws Exception { DockerfileConfig.getBaseImageVersion()); // Build the docker - execute("docker", "build", "--no-cache", "--tag", imageName, buildDir.toString()) + execute(DOCKER_COMMAND, "build", "--no-cache", "--tag", imageName, buildDir.toString()) .shouldHaveExitValue(0) .shouldContain("Successfully built"); } @@ -174,7 +189,7 @@ private static boolean isDockerEngineAvailableCheck() throws Exception { public static OutputAnalyzer dockerRunJava(DockerRunOptions opts) throws Exception { ArrayList cmd = new ArrayList<>(); - cmd.add("docker"); + cmd.add(DOCKER_COMMAND); cmd.add("run"); if (opts.tty) cmd.add("--tty=true"); @@ -200,11 +215,10 @@ public static OutputAnalyzer dockerRunJava(DockerRunOptions opts) throws Excepti * Remove docker image * * @param DockerRunOptions optins for running docker - * @return output of the command * @throws Exception */ - public static OutputAnalyzer removeDockerImage(String imageNameAndTag) throws Exception { - return execute("docker", "rmi", "--force", imageNameAndTag); + public static void removeDockerImage(String imageNameAndTag) throws Exception { + execute(DOCKER_COMMAND, "rmi", "--force", imageNameAndTag); }