This commit is contained in:
Jiangli Zhou 2015-11-23 14:38:20 -05:00
commit 6ca042cb32
29 changed files with 868 additions and 367 deletions

View file

@ -51,7 +51,8 @@ BUILD_HOTSPOT_JTREG_NATIVE_SRC := \
# Add conditional directories here when needed.
ifeq ($(OPENJDK_TARGET_OS)-$(OPENJDK_TARGET_CPU_ARCH), solaris-sparc)
BUILD_HOTSPOT_JTREG_NATIVE_SRC += \
$(HOTSPOT_TOPDIR)/test/runtime/libadimalloc.solaris.sparc
$(HOTSPOT_TOPDIR)/test/runtime/libadimalloc.solaris.sparc \
$(HOTSPOT_TOPDIR)/test/runtime/ThreadSignalMask
endif
ifeq ($(TOOLCHAIN_TYPE), solstudio)

View file

@ -137,7 +137,7 @@ juint* CompactHashtableWriter::dump_buckets(juint* compact_table, juint* p,
if (_type == CompactHashtable<Symbol*, char>::_symbol_table) {
base_address = uintx(MetaspaceShared::shared_rs()->base());
max_delta = uintx(MetaspaceShared::shared_rs()->size());
assert(max_delta <= 0x7fffffff, "range check");
assert(max_delta <= MAX_SHARED_DELTA, "range check");
} else {
assert((_type == CompactHashtable<oop, char>::_string_table), "unknown table");
assert(UseCompressedOops, "UseCompressedOops is required");

View file

@ -499,11 +499,14 @@ HeapWord* G1BlockOffsetArrayContigSpace::initialize_threshold() {
return _next_offset_threshold;
}
void G1BlockOffsetArrayContigSpace::set_for_starts_humongous(HeapWord* obj_top) {
void G1BlockOffsetArrayContigSpace::set_for_starts_humongous(HeapWord* obj_top, size_t fill_size) {
// The first BOT entry should have offset 0.
reset_bot();
alloc_block(_bottom, obj_top);
if (fill_size > 0) {
alloc_block(obj_top, fill_size);
}
}
#ifndef PRODUCT
void G1BlockOffsetArrayContigSpace::print_on(outputStream* out) {

View file

@ -372,7 +372,7 @@ class G1BlockOffsetArrayContigSpace: public G1BlockOffsetArray {
HeapWord* block_start_unsafe(const void* addr);
HeapWord* block_start_unsafe_const(const void* addr) const;
void set_for_starts_humongous(HeapWord* obj_top);
void set_for_starts_humongous(HeapWord* obj_top, size_t fill_size);
virtual void print_on(outputStream* out) PRODUCT_RETURN;
};

View file

@ -339,11 +339,18 @@ G1CollectedHeap::humongous_obj_allocate_initialize_regions(uint first,
// thread to calculate the object size incorrectly.
Copy::fill_to_words(new_obj, oopDesc::header_size(), 0);
size_t fill_size = word_size_sum - word_size;
if (fill_size >= min_fill_size()) {
fill_with_objects(obj_top, fill_size);
} else {
fill_size = 0;
}
// We will set up the first region as "starts humongous". This
// will also update the BOT covering all the regions to reflect
// that there is a single object that starts at the bottom of the
// first region.
first_hr->set_starts_humongous(obj_top);
first_hr->set_starts_humongous(obj_top, fill_size);
first_hr->set_allocation_context(context);
// Then, if there are any, we will set up the "continues
// humongous" regions.
@ -365,9 +372,9 @@ G1CollectedHeap::humongous_obj_allocate_initialize_regions(uint first,
// Now that the BOT and the object header have been initialized,
// we can update top of the "starts humongous" region.
first_hr->set_top(MIN2(first_hr->end(), obj_top));
first_hr->set_top(first_hr->end());
if (_hr_printer.is_active()) {
_hr_printer.alloc(G1HRPrinter::StartsHumongous, first_hr, first_hr->top());
_hr_printer.alloc(G1HRPrinter::StartsHumongous, first_hr, first_hr->end());
}
// Now, we will update the top fields of the "continues humongous"
@ -375,25 +382,18 @@ G1CollectedHeap::humongous_obj_allocate_initialize_regions(uint first,
hr = NULL;
for (uint i = first + 1; i < last; ++i) {
hr = region_at(i);
if ((i + 1) == last) {
// last continues humongous region
assert(hr->bottom() < obj_top && obj_top <= hr->end(),
"new_top should fall on this region");
hr->set_top(obj_top);
_hr_printer.alloc(G1HRPrinter::ContinuesHumongous, hr, obj_top);
} else {
// not last one
assert(obj_top > hr->end(), "obj_top should be above this region");
hr->set_top(hr->end());
if (_hr_printer.is_active()) {
_hr_printer.alloc(G1HRPrinter::ContinuesHumongous, hr, hr->end());
}
}
// If we have continues humongous regions (hr != NULL), its top should
// match obj_top.
assert(hr == NULL || (hr->top() == obj_top), "sanity");
assert(hr == NULL || (hr->bottom() < obj_top && obj_top <= hr->end()),
"obj_top should be in last region");
check_bitmaps("Humongous Region Allocation", first_hr);
increase_used(word_size * HeapWordSize);
increase_used(word_size_sum * HeapWordSize);
for (uint i = first; i < last; ++i) {
_humongous_set.add(region_at(i));
@ -1202,9 +1202,8 @@ void G1CollectedHeap::print_hrm_post_compaction() {
heap_region_iterate(&cl);
}
bool G1CollectedHeap::do_collection(bool explicit_gc,
bool clear_all_soft_refs,
size_t word_size) {
bool G1CollectedHeap::do_full_collection(bool explicit_gc,
bool clear_all_soft_refs) {
assert_at_safepoint(true /* should_be_vm_thread */);
if (GC_locker::check_active_before_gc()) {
@ -1362,8 +1361,7 @@ bool G1CollectedHeap::do_collection(bool explicit_gc,
clear_rsets_post_compaction();
check_gc_time_stamps();
// Resize the heap if necessary.
resize_if_necessary_after_full_collection(explicit_gc ? 0 : word_size);
resize_if_necessary_after_full_collection();
if (_hr_printer.is_active()) {
// We should do this after we potentially resize the heap so
@ -1471,22 +1469,15 @@ bool G1CollectedHeap::do_collection(bool explicit_gc,
}
void G1CollectedHeap::do_full_collection(bool clear_all_soft_refs) {
// do_collection() will return whether it succeeded in performing
// the GC. Currently, there is no facility on the
// do_full_collection() API to notify the caller than the collection
// did not succeed (e.g., because it was locked out by the GC
// locker). So, right now, we'll ignore the return value.
bool dummy = do_collection(true, /* explicit_gc */
clear_all_soft_refs,
0 /* word_size */);
// Currently, there is no facility in the do_full_collection(bool) API to notify
// the caller that the collection did not succeed (e.g., because it was locked
// out by the GC locker). So, right now, we'll ignore the return value.
bool dummy = do_full_collection(true, /* explicit_gc */
clear_all_soft_refs);
}
// This code is mostly copied from TenuredGeneration.
void
G1CollectedHeap::
resize_if_necessary_after_full_collection(size_t word_size) {
// Include the current allocation, if any, and bytes that will be
// pre-allocated to support collections, as "used".
void G1CollectedHeap::resize_if_necessary_after_full_collection() {
// Include bytes that will be pre-allocated to support collections, as "used".
const size_t used_after_gc = used();
const size_t capacity_after_gc = capacity();
const size_t free_after_gc = capacity_after_gc - used_after_gc;
@ -1598,9 +1589,8 @@ HeapWord* G1CollectedHeap::satisfy_failed_allocation_helper(size_t word_size,
if (do_gc) {
// Expansion didn't work, we'll try to do a Full GC.
*gc_succeeded = do_collection(false, /* explicit_gc */
clear_all_soft_refs,
word_size);
*gc_succeeded = do_full_collection(false, /* explicit_gc */
clear_all_soft_refs);
}
return NULL;

View file

@ -471,26 +471,20 @@ protected:
void retire_gc_alloc_region(HeapRegion* alloc_region,
size_t allocated_bytes, InCSetState dest);
// - if explicit_gc is true, the GC is for a System.gc() or a heap
// inspection request and should collect the entire heap
// - if explicit_gc is true, the GC is for a System.gc() etc,
// otherwise it's for a failed allocation.
// - if clear_all_soft_refs is true, all soft references should be
// cleared during the GC
// - if explicit_gc is false, word_size describes the allocation that
// the GC should attempt (at least) to satisfy
// cleared during the GC.
// - it returns false if it is unable to do the collection due to the
// GC locker being active, true otherwise
bool do_collection(bool explicit_gc,
bool clear_all_soft_refs,
size_t word_size);
// GC locker being active, true otherwise.
bool do_full_collection(bool explicit_gc,
bool clear_all_soft_refs);
// Callback from VM_G1CollectFull operation.
// Perform a full collection.
// 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. If this is
// after a collect-for allocation, "word_size" is the allocation size,
// and will be considered part of the used portion of the heap.
void resize_if_necessary_after_full_collection(size_t word_size);
// 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
@ -1150,9 +1144,6 @@ public:
// "CollectedHeap" supports.
virtual void collect(GCCause::Cause cause);
// The same as above but assume that the caller holds the Heap_lock.
void collect_locked(GCCause::Cause cause);
virtual bool copy_allocation_context_stats(const jint* contexts,
jlong* totals,
jbyte* accuracy,
@ -1352,14 +1343,6 @@ public:
return (region_size / 2);
}
// Update mod union table with the set of dirty cards.
void updateModUnion();
// Set the mod union bits corresponding to the given memRegion. Note
// that this is always a safe operation, since it doesn't clear any
// bits.
void markModUnionRange(MemRegion mr);
// Print the maximum heap capacity.
virtual size_t max_capacity() const;

View file

@ -123,16 +123,13 @@ enum G1Mark {
template <G1Barrier barrier, G1Mark do_mark_object>
class G1ParCopyClosure : public G1ParCopyHelper {
private:
template <class T> void do_oop_work(T* p);
public:
G1ParCopyClosure(G1CollectedHeap* g1, G1ParScanThreadState* par_scan_state) :
G1ParCopyHelper(g1, par_scan_state) {
assert(ref_processor() == NULL, "sanity");
}
template <class T> void do_oop_nv(T* p) { do_oop_work(p); }
template <class T> void do_oop_nv(T* p);
virtual void do_oop(oop* p) { do_oop_nv(p); }
virtual void do_oop(narrowOop* p) { do_oop_nv(p); }
};

View file

@ -251,7 +251,7 @@ void G1ParCopyHelper::mark_forwarded_object(oop from_obj, oop to_obj) {
template <G1Barrier barrier, G1Mark do_mark_object>
template <class T>
void G1ParCopyClosure<barrier, do_mark_object>::do_oop_work(T* p) {
void G1ParCopyClosure<barrier, do_mark_object>::do_oop_nv(T* p) {
T heap_oop = oopDesc::load_heap_oop(p);
if (oopDesc::is_null(heap_oop)) {

View file

@ -73,23 +73,7 @@ G1RemSet::~G1RemSet() {
FREE_C_HEAP_ARRAY(G1ParPushHeapRSClosure*, _cset_rs_update_cl);
}
class ScanRSClosure : public HeapRegionClosure {
size_t _cards_done, _cards;
G1CollectedHeap* _g1h;
G1ParPushHeapRSClosure* _oc;
CodeBlobClosure* _code_root_cl;
G1BlockOffsetSharedArray* _bot_shared;
G1SATBCardTableModRefBS *_ct_bs;
double _strong_code_root_scan_time_sec;
uint _worker_i;
size_t _block_size;
bool _try_claimed;
public:
ScanRSClosure(G1ParPushHeapRSClosure* oc,
ScanRSClosure::ScanRSClosure(G1ParPushHeapRSClosure* oc,
CodeBlobClosure* code_root_cl,
uint worker_i) :
_oc(oc),
@ -98,17 +82,14 @@ public:
_cards(0),
_cards_done(0),
_worker_i(worker_i),
_try_claimed(false)
{
_try_claimed(false) {
_g1h = G1CollectedHeap::heap();
_bot_shared = _g1h->bot_shared();
_ct_bs = _g1h->g1_barrier_set();
_block_size = MAX2<size_t>(G1RSetScanBlockSize, 1);
}
}
void set_try_claimed() { _try_claimed = true; }
void scanCard(size_t index, HeapRegion *r) {
void ScanRSClosure::scanCard(size_t index, HeapRegion *r) {
// Stack allocate the DirtyCardToOopClosure instance
HeapRegionDCTOC cl(_g1h, r, _oc,
CardTableModRefBS::Precise);
@ -126,9 +107,9 @@ public:
_cards_done++;
cl.do_MemRegion(mr);
}
}
}
void printCard(HeapRegion* card_region, size_t card_index,
void ScanRSClosure::printCard(HeapRegion* card_region, size_t card_index,
HeapWord* card_start) {
gclog_or_tty->print_cr("T %u Region [" PTR_FORMAT ", " PTR_FORMAT ") "
"RS names card " SIZE_FORMAT_HEX ": "
@ -137,15 +118,15 @@ public:
p2i(card_region->bottom()), p2i(card_region->end()),
card_index,
p2i(card_start), p2i(card_start + G1BlockOffsetSharedArray::N_words));
}
}
void scan_strong_code_roots(HeapRegion* r) {
void ScanRSClosure::scan_strong_code_roots(HeapRegion* r) {
double scan_start = os::elapsedTime();
r->strong_code_roots_do(_code_root_cl);
_strong_code_root_scan_time_sec += (os::elapsedTime() - scan_start);
}
}
bool doHeapRegion(HeapRegion* r) {
bool ScanRSClosure::doHeapRegion(HeapRegion* r) {
assert(r->in_collection_set(), "should only be called on elements of CS.");
HeapRegionRemSet* hrrs = r->rem_set();
if (hrrs->iter_is_complete()) return false; // All done.
@ -196,15 +177,7 @@ public:
hrrs->set_iter_complete();
}
return false;
}
double strong_code_root_scan_time_sec() {
return _strong_code_root_scan_time_sec;
}
size_t cards_done() { return _cards_done;}
size_t cards_looked_up() { return _cards;}
};
}
size_t G1RemSet::scanRS(G1ParPushHeapRSClosure* oc,
CodeBlobClosure* heap_region_codeblobs,

View file

@ -156,6 +156,41 @@ public:
}
};
class ScanRSClosure : public HeapRegionClosure {
size_t _cards_done, _cards;
G1CollectedHeap* _g1h;
G1ParPushHeapRSClosure* _oc;
CodeBlobClosure* _code_root_cl;
G1BlockOffsetSharedArray* _bot_shared;
G1SATBCardTableModRefBS *_ct_bs;
double _strong_code_root_scan_time_sec;
uint _worker_i;
size_t _block_size;
bool _try_claimed;
public:
ScanRSClosure(G1ParPushHeapRSClosure* oc,
CodeBlobClosure* code_root_cl,
uint worker_i);
bool doHeapRegion(HeapRegion* r);
double strong_code_root_scan_time_sec() {
return _strong_code_root_scan_time_sec;
}
size_t cards_done() { return _cards_done;}
size_t cards_looked_up() { return _cards;}
void set_try_claimed() { _try_claimed = true; }
private:
void scanCard(size_t index, HeapRegion *r);
void printCard(HeapRegion* card_region, size_t card_index,
HeapWord* card_start);
void scan_strong_code_roots(HeapRegion* r);
};
class UpdateRSOopClosure: public ExtendedOopClosure {
HeapRegion* _from;
G1RemSet* _rs;

View file

@ -211,14 +211,14 @@ void HeapRegion::calc_gc_efficiency() {
_gc_efficiency = (double) reclaimable_bytes() / region_elapsed_time_ms;
}
void HeapRegion::set_starts_humongous(HeapWord* obj_top) {
void HeapRegion::set_starts_humongous(HeapWord* obj_top, size_t fill_size) {
assert(!is_humongous(), "sanity / pre-condition");
assert(top() == bottom(), "should be empty");
_type.set_starts_humongous();
_humongous_start_region = this;
_offsets.set_for_starts_humongous(obj_top);
_offsets.set_for_starts_humongous(obj_top, fill_size);
}
void HeapRegion::set_continues_humongous(HeapRegion* first_hr) {
@ -756,16 +756,6 @@ void HeapRegion::verify(VerifyOption vo,
size_t obj_size = block_size(p);
object_num += 1;
if (is_region_humongous != g1->is_humongous(obj_size) &&
!g1->is_obj_dead(obj, this)) { // Dead objects may have bigger block_size since they span several objects.
gclog_or_tty->print_cr("obj " PTR_FORMAT " is of %shumongous size ("
SIZE_FORMAT " words) in a %shumongous region",
p2i(p), g1->is_humongous(obj_size) ? "" : "non-",
obj_size, is_region_humongous ? "" : "non-");
*failures = true;
return;
}
if (!g1->is_obj_dead_cond(obj, this, vo)) {
if (obj->is_oop()) {
Klass* klass = obj->klass();
@ -876,14 +866,6 @@ void HeapRegion::verify(VerifyOption vo,
}
}
if (is_region_humongous && object_num > 1) {
gclog_or_tty->print_cr("region [" PTR_FORMAT "," PTR_FORMAT "] is humongous "
"but has " SIZE_FORMAT ", objects",
p2i(bottom()), p2i(end()), object_num);
*failures = true;
return;
}
verify_strong_code_roots(vo, failures);
}

View file

@ -455,9 +455,9 @@ class HeapRegion: public G1OffsetTableContigSpace {
// the first region in a series of one or more contiguous regions
// that will contain a single "humongous" object.
//
// obj_top : points to the end of the humongous object that's being
// allocated.
void set_starts_humongous(HeapWord* obj_top);
// obj_top : points to the top of the humongous object.
// fill_size : size of the filler object at the end of the region series.
void set_starts_humongous(HeapWord* obj_top, size_t fill_size);
// Makes the current region be a "continues humongous'
// region. first_hr is the "start humongous" region of the series

View file

@ -37,10 +37,10 @@ void Test_log_length() {
// Write long message to output file
MutexLocker ml(LogConfiguration_lock);
LogConfiguration::parse_log_arguments("loglengthoutput.txt", "logging=develop",
LogConfiguration::parse_log_arguments("loglengthoutput.txt", "logging=trace",
NULL, NULL, NULL);
ResourceMark rm;
outputStream* logstream = LogHandle(logging)::develop_stream();
outputStream* logstream = LogHandle(logging)::trace_stream();
logstream->print_cr("01:1234567890-"
"02:1234567890-"
"03:1234567890-"

View file

@ -49,11 +49,21 @@
#define log_info(...) (!log_is_enabled(Info, __VA_ARGS__)) ? (void)0 : Log<LOG_TAGS(__VA_ARGS__)>::write<LogLevel::Info>
#define log_debug(...) (!log_is_enabled(Debug, __VA_ARGS__)) ? (void)0 : Log<LOG_TAGS(__VA_ARGS__)>::write<LogLevel::Debug>
#define log_trace(...) (!log_is_enabled(Trace, __VA_ARGS__)) ? (void)0 : Log<LOG_TAGS(__VA_ARGS__)>::write<LogLevel::Trace>
// Macros for logging that should be excluded in product builds.
// Available for levels Info, Debug and Trace. Includes test macro that
// evaluates to false in product builds.
#ifndef PRODUCT
#define log_develop(...) (!log_is_enabled(Develop, __VA_ARGS__)) ? (void)0 : Log<LOG_TAGS(__VA_ARGS__)>::write<LogLevel::Develop>
#define log_develop_info(...) (!log_is_enabled(Info, __VA_ARGS__)) ? (void)0 : Log<LOG_TAGS(__VA_ARGS__)>::write<LogLevel::Info>
#define log_develop_debug(...) (!log_is_enabled(Debug, __VA_ARGS__)) ? (void)0 : Log<LOG_TAGS(__VA_ARGS__)>::write<LogLevel::Debug>
#define log_develop_trace(...) (!log_is_enabled(Trace, __VA_ARGS__)) ? (void)0 : Log<LOG_TAGS(__VA_ARGS__)>::write<LogLevel::Trace>
#define develop_log_is_enabled(level, ...) log_is_enabled(level, __VA_ARGS__)
#else
#define DUMMY_ARGUMENT_CONSUMER(...)
#define log_develop(...) DUMMY_ARGUMENT_CONSUMER
#define log_develop_info(...) DUMMY_ARGUMENT_CONSUMER
#define log_develop_debug(...) DUMMY_ARGUMENT_CONSUMER
#define log_develop_trace(...) DUMMY_ARGUMENT_CONSUMER
#define develop_log_is_enabled(...) false
#endif
// Convenience macro to test if the logging is enabled on the specified level for given tags.
@ -88,6 +98,11 @@ class Log VALUE_OBJ_CLASS_SPEC {
// is not __NO_TAG, the number of tags given exceeds the maximum allowed.
STATIC_ASSERT(GuardTag == LogTag::__NO_TAG); // Number of logging tags exceeds maximum supported!
// Empty constructor to avoid warnings on MSVC about unused variables
// when the log instance is only used for static functions.
Log() {
}
static bool is_level(LogLevelType level) {
return LogTagSetMapping<T0, T1, T2, T3, T4>::tagset().is_level(level);
}

View file

@ -44,6 +44,7 @@ void LogConfiguration::post_initialize() {
LogDiagnosticCommand::registerCommand();
LogHandle(logging) log;
log.info("Log configuration fully initialized.");
log_develop_info(logging)("Develop logging is available.");
if (log.is_trace()) {
ResourceMark rm;
MutexLocker ml(LogConfiguration_lock);

View file

@ -29,14 +29,8 @@
// The list of log levels:
//
// develop - A non-product level that is finer than trace.
// Should be used for really expensive and/or
// extensive logging, or logging that shouldn't
// or can't be included in a product build.
//
// trace - Finest level of logging in product builds.
// Use for extensive/noisy logging that can
// give slow-down when enabled.
// trace - Finest level of logging. Use for extensive/noisy
// logging that can give slow-down when enabled.
//
// debug - A finer level of logging. Use for semi-noisy
// logging that is does not fit the info level.
@ -49,7 +43,6 @@
// error - Critical messages caused by errors.
//
#define LOG_LEVEL_LIST \
NOT_PRODUCT(LOG_LEVEL(Develop, develop)) \
LOG_LEVEL(Trace, trace) \
LOG_LEVEL(Debug, debug) \
LOG_LEVEL(Info, info) \

View file

@ -3230,36 +3230,6 @@ void Metaspace::global_initialize() {
SharedMiscDataSize = align_size_up(SharedMiscDataSize, max_alignment);
SharedMiscCodeSize = align_size_up(SharedMiscCodeSize, max_alignment);
// make sure SharedReadOnlySize and SharedReadWriteSize are not less than
// the minimum values.
if (SharedReadOnlySize < MetaspaceShared::min_ro_size){
report_out_of_shared_space(SharedReadOnly);
}
if (SharedReadWriteSize < MetaspaceShared::min_rw_size){
report_out_of_shared_space(SharedReadWrite);
}
// the min_misc_data_size and min_misc_code_size estimates are based on
// MetaspaceShared::generate_vtable_methods().
// The minimum size only accounts for the vtable methods. Any size less than the
// minimum required size would cause vm crash when allocating the vtable methods.
uint min_misc_data_size = align_size_up(
MetaspaceShared::num_virtuals * MetaspaceShared::vtbl_list_size * sizeof(void*), max_alignment);
if (SharedMiscDataSize < min_misc_data_size) {
report_out_of_shared_space(SharedMiscData);
}
uintx min_misc_code_size = align_size_up(
(MetaspaceShared::num_virtuals * MetaspaceShared::vtbl_list_size) *
(sizeof(void*) + MetaspaceShared::vtbl_method_size) + MetaspaceShared::vtbl_common_code_size,
max_alignment);
if (SharedMiscCodeSize < min_misc_code_size) {
report_out_of_shared_space(SharedMiscCode);
}
// Initialize with the sum of the shared space sizes. The read-only
// and read write metaspace chunks will be allocated out of this and the
// remainder is the misc code and data chunks.

View file

@ -32,6 +32,47 @@
#include "utilities/exceptions.hpp"
#include "utilities/macros.hpp"
#define DEFAULT_VTBL_LIST_SIZE (17) // number of entries in the shared space vtable list.
#define DEFAULT_VTBL_VIRTUALS_COUNT (200) // maximum number of virtual functions
// If virtual functions are added to Metadata,
// this number needs to be increased. Also,
// SharedMiscCodeSize will need to be increased.
// The following 2 sizes were based on
// MetaspaceShared::generate_vtable_methods()
#define DEFAULT_VTBL_METHOD_SIZE (16) // conservative size of the mov1 and jmp instructions
// for the x64 platform
#define DEFAULT_VTBL_COMMON_CODE_SIZE (1*K) // conservative size of the "common_code" for the x64 platform
#define DEFAULT_SHARED_READ_WRITE_SIZE (NOT_LP64(12*M) LP64_ONLY(16*M))
#define MIN_SHARED_READ_WRITE_SIZE (NOT_LP64(7*M) LP64_ONLY(12*M))
#define DEFAULT_SHARED_READ_ONLY_SIZE (NOT_LP64(12*M) LP64_ONLY(16*M))
#define MIN_SHARED_READ_ONLY_SIZE (NOT_LP64(8*M) LP64_ONLY(9*M))
// the MIN_SHARED_MISC_DATA_SIZE and MIN_SHARED_MISC_CODE_SIZE estimates are based on
// MetaspaceShared::generate_vtable_methods().
// The minimum size only accounts for the vtable methods. Any size less than the
// minimum required size would cause vm crash when allocating the vtable methods.
#define SHARED_MISC_SIZE_FOR(size) (DEFAULT_VTBL_VIRTUALS_COUNT*DEFAULT_VTBL_LIST_SIZE*size)
#define DEFAULT_SHARED_MISC_DATA_SIZE (NOT_LP64(2*M) LP64_ONLY(4*M))
#define MIN_SHARED_MISC_DATA_SIZE (SHARED_MISC_SIZE_FOR(sizeof(void*)))
#define DEFAULT_SHARED_MISC_CODE_SIZE (120*K)
#define MIN_SHARED_MISC_CODE_SIZE (SHARED_MISC_SIZE_FOR(sizeof(void*))+SHARED_MISC_SIZE_FOR(DEFAULT_VTBL_METHOD_SIZE)+DEFAULT_VTBL_COMMON_CODE_SIZE)
#define DEFAULT_COMBINED_SIZE (DEFAULT_SHARED_READ_WRITE_SIZE+DEFAULT_SHARED_READ_ONLY_SIZE+DEFAULT_SHARED_MISC_DATA_SIZE+DEFAULT_SHARED_MISC_CODE_SIZE)
// the max size is the MAX size (ie. 0x7FFFFFFF) - the total size of
// the other 3 sections - page size (to avoid overflow in case the final
// size will get aligned up on page size)
#define SHARED_PAGE ((size_t)os::vm_page_size())
#define MAX_SHARED_DELTA (0x7FFFFFFF)
#define MAX_SHARED_READ_WRITE_SIZE (MAX_SHARED_DELTA-(MIN_SHARED_READ_ONLY_SIZE+MIN_SHARED_MISC_DATA_SIZE+MIN_SHARED_MISC_CODE_SIZE)-SHARED_PAGE)
#define MAX_SHARED_READ_ONLY_SIZE (MAX_SHARED_DELTA-(MIN_SHARED_READ_WRITE_SIZE+MIN_SHARED_MISC_DATA_SIZE+MIN_SHARED_MISC_CODE_SIZE)-SHARED_PAGE)
#define MAX_SHARED_MISC_DATA_SIZE (MAX_SHARED_DELTA-(MIN_SHARED_READ_WRITE_SIZE+MIN_SHARED_READ_ONLY_SIZE+MIN_SHARED_MISC_CODE_SIZE)-SHARED_PAGE)
#define MAX_SHARED_MISC_CODE_SIZE (MAX_SHARED_DELTA-(MIN_SHARED_READ_WRITE_SIZE+MIN_SHARED_READ_ONLY_SIZE+MIN_SHARED_MISC_DATA_SIZE)-SHARED_PAGE)
#define LargeSharedArchiveSize (300*M)
#define HugeSharedArchiveSize (800*M)
#define ReadOnlyRegionPercentage 0.4
@ -69,21 +110,10 @@ class MetaspaceShared : AllStatic {
static bool _archive_loading_failed;
public:
enum {
vtbl_list_size = 17, // number of entries in the shared space vtable list.
num_virtuals = 200, // maximum number of virtual functions
// If virtual functions are added to Metadata,
// this number needs to be increased. Also,
// SharedMiscCodeSize will need to be increased.
// The following 2 sizes were based on
// MetaspaceShared::generate_vtable_methods()
vtbl_method_size = 16, // conservative size of the mov1 and jmp instructions
// for the x64 platform
vtbl_common_code_size = (1*K) // conservative size of the "common_code" for the x64 platform
};
enum {
min_ro_size = NOT_LP64(8*M) LP64_ONLY(9*M), // minimum ro and rw regions sizes based on dumping
min_rw_size = NOT_LP64(7*M) LP64_ONLY(12*M) // of a shared archive using the default classlist
vtbl_list_size = DEFAULT_VTBL_LIST_SIZE,
num_virtuals = DEFAULT_VTBL_VIRTUALS_COUNT,
vtbl_method_size = DEFAULT_VTBL_METHOD_SIZE,
vtbl_common_code_size = DEFAULT_VTBL_COMMON_CODE_SIZE
};
enum {

View file

@ -223,7 +223,7 @@ void emit_constraint_double(const char* name, CommandLineFlagConstraintFunc_doub
#define EMIT_CONSTRAINT_CHECK(func, type) , func, CommandLineFlagConstraint::type
// the "name" argument must be a string literal
#define INITIAL_CONSTRAINTS_SIZE 45
#define INITIAL_CONSTRAINTS_SIZE 69
GrowableArray<CommandLineFlagConstraint*>* CommandLineFlagConstraintList::_constraints = NULL;
CommandLineFlagConstraint::ConstraintType CommandLineFlagConstraintList::_validating_type = CommandLineFlagConstraint::AtParse;

View file

@ -279,7 +279,7 @@ void emit_range_double(const char* name, double min, double max) {
// Generate func argument to pass into emit_range_xxx functions
#define EMIT_RANGE_CHECK(a, b) , a, b
#define INITIAL_RANGES_SIZE 205
#define INITIAL_RANGES_SIZE 320
GrowableArray<CommandLineFlagRange*>* CommandLineFlagRangeList::_ranges = NULL;
// Check the ranges of all flags that have them

View file

@ -25,6 +25,7 @@
#ifndef SHARE_VM_RUNTIME_COMMANDLINEFLAGRANGELIST_HPP
#define SHARE_VM_RUNTIME_COMMANDLINEFLAGRANGELIST_HPP
#include "memory/metaspaceShared.hpp"
#include "runtime/globals.hpp"
#include "utilities/growableArray.hpp"

View file

@ -4110,21 +4110,26 @@ public:
"If PrintSharedArchiveAndExit is true, also print the shared " \
"dictionary") \
\
product(size_t, SharedReadWriteSize, NOT_LP64(12*M) LP64_ONLY(16*M), \
product(size_t, SharedReadWriteSize, DEFAULT_SHARED_READ_WRITE_SIZE, \
"Size of read-write space for metadata (in bytes)") \
range(MIN_SHARED_READ_WRITE_SIZE, MAX_SHARED_READ_WRITE_SIZE) \
\
product(size_t, SharedReadOnlySize, NOT_LP64(12*M) LP64_ONLY(16*M), \
product(size_t, SharedReadOnlySize, DEFAULT_SHARED_READ_ONLY_SIZE, \
"Size of read-only space for metadata (in bytes)") \
range(MIN_SHARED_READ_ONLY_SIZE, MAX_SHARED_READ_ONLY_SIZE) \
\
product(uintx, SharedMiscDataSize, NOT_LP64(2*M) LP64_ONLY(4*M), \
product(size_t, SharedMiscDataSize, DEFAULT_SHARED_MISC_DATA_SIZE, \
"Size of the shared miscellaneous data area (in bytes)") \
range(MIN_SHARED_MISC_DATA_SIZE, MAX_SHARED_MISC_DATA_SIZE) \
\
product(uintx, SharedMiscCodeSize, 120*K, \
product(size_t, SharedMiscCodeSize, DEFAULT_SHARED_MISC_CODE_SIZE, \
"Size of the shared miscellaneous code area (in bytes)") \
range(MIN_SHARED_MISC_CODE_SIZE, MAX_SHARED_MISC_CODE_SIZE) \
\
product(uintx, SharedBaseAddress, LP64_ONLY(32*G) \
product(size_t, SharedBaseAddress, LP64_ONLY(32*G) \
NOT_LP64(LINUX_ONLY(2*G) NOT_LINUX(0)), \
"Address to allocate shared memory region for class data") \
range(0, SIZE_MAX) \
\
product(uintx, SharedSymbolTableBucketSize, 4, \
"Average number of symbols per bucket in shared table") \

View file

@ -58,6 +58,25 @@ public class TestOptionsWithRanges {
*/
allOptionsAsMap.remove("ThreadStackSize");
/*
* JDK-8141650
* Temporarily exclude SharedMiscDataSize as it will exit the VM with exit code 2 and
* "The shared miscellaneous data space is not large enough to preload requested classes."
* message at min value.
*/
allOptionsAsMap.remove("SharedMiscDataSize");
/*
* JDK-8142874
* Temporarily exclude Shared* flagse as they will exit the VM with exit code 2 and
* "The shared miscellaneous data space is not large enough to preload requested classes."
* message at max values.
*/
allOptionsAsMap.remove("SharedReadWriteSize");
allOptionsAsMap.remove("SharedReadOnlySize");
allOptionsAsMap.remove("SharedMiscDataSize");
allOptionsAsMap.remove("SharedMiscCodeSize");
/*
* Exclude MallocMaxTestWords as it is expected to exit VM at small values (>=0)
*/

View file

@ -223,7 +223,7 @@ public class IntJVMOption extends JVMOption {
validValues.add("1");
}
if (max.compareTo(MAX_4_BYTE_INT_PLUS_ONE) == 1) {
if ((min.compareTo(MAX_4_BYTE_INT_PLUS_ONE) == -1) && (max.compareTo(MAX_4_BYTE_INT_PLUS_ONE) == 1)) {
/*
* Check for overflow when flag is assigned to the
* 4 byte int variable
@ -231,7 +231,7 @@ public class IntJVMOption extends JVMOption {
validValues.add(MAX_4_BYTE_INT_PLUS_ONE.toString());
}
if (max.compareTo(MAX_4_BYTE_UNSIGNED_INT_PLUS_ONE) == 1) {
if ((min.compareTo(MAX_4_BYTE_UNSIGNED_INT_PLUS_ONE) == -1) && (max.compareTo(MAX_4_BYTE_UNSIGNED_INT_PLUS_ONE) == 1)) {
/*
* Check for overflow when flag is assigned to the
* 4 byte unsigned int variable

View file

@ -27,6 +27,7 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@ -49,6 +50,8 @@ public class JVMOptionsUtils {
/* Used to start the JVM with the same type as current */
static String VMType;
private static Map<String, JVMOption> optionsAsMap;
static {
if (Platform.isServer()) {
VMType = "-server";
@ -63,6 +66,84 @@ public class JVMOptionsUtils {
}
}
public static boolean fitsRange(String optionName, BigDecimal number) throws Exception {
JVMOption option;
String minRangeString = null;
String maxRangeString = null;
boolean fits = true;
if (optionsAsMap == null) {
optionsAsMap = getOptionsWithRangeAsMap();
}
option = optionsAsMap.get(optionName);
if (option != null) {
minRangeString = option.getMin();
if (minRangeString != null) {
fits = (number.compareTo(new BigDecimal(minRangeString)) >= 0);
}
maxRangeString = option.getMax();
if (maxRangeString != null) {
fits &= (number.compareTo(new BigDecimal(maxRangeString)) <= 0);
}
}
return fits;
}
public static boolean fitsRange(String optionName, String number) throws Exception {
String lowerCase = number.toLowerCase();
String multiplier = "1";
if (lowerCase.endsWith("k")) {
multiplier = "1024";
lowerCase = lowerCase.substring(0, lowerCase.length()-1);
} else if (lowerCase.endsWith("m")) {
multiplier = "1048576"; //1024*1024
lowerCase = lowerCase.substring(0, lowerCase.length()-1);
} else if (lowerCase.endsWith("g")) {
multiplier = "1073741824"; //1024*1024*1024
lowerCase = lowerCase.substring(0, lowerCase.length()-1);
} else if (lowerCase.endsWith("t")) {
multiplier = "1099511627776"; //1024*1024*1024*1024
lowerCase = lowerCase.substring(0, lowerCase.length()-1);
}
BigDecimal valueBig = new BigDecimal(lowerCase);
BigDecimal multiplierBig = new BigDecimal(multiplier);
return fitsRange(optionName, valueBig.multiply(multiplierBig));
}
public static String getMinOptionRange(String optionName) throws Exception {
JVMOption option;
String minRange = null;
if (optionsAsMap == null) {
optionsAsMap = getOptionsWithRangeAsMap();
}
option = optionsAsMap.get(optionName);
if (option != null) {
minRange = option.getMin();
}
return minRange;
}
public static String getMaxOptionRange(String optionName) throws Exception {
JVMOption option;
String maxRange = null;
if (optionsAsMap == null) {
optionsAsMap = getOptionsWithRangeAsMap();
}
option = optionsAsMap.get(optionName);
if (option != null) {
maxRange = option.getMax();
}
return maxRange;
}
/**
* Add dependency for option depending on it's name. E.g. enable G1 GC for
* G1 options or add prepend options to not hit constraints.
@ -80,6 +161,13 @@ public class JVMOptionsUtils {
option.addPrepend("-XX:+UseConcMarkSweepGC");
}
if (name.startsWith("Shared")) {
option.addPrepend("-XX:+UnlockDiagnosticVMOptions");
String fileName = "Test" + name + ".jsa";
option.addPrepend("-XX:SharedArchiveFile=" + fileName);
option.addPrepend("-Xshare:dump");
}
switch (name) {
case "MinHeapFreeRatio":
option.addPrepend("-XX:MaxHeapFreeRatio=100");
@ -112,7 +200,6 @@ public class JVMOptionsUtils {
/* Do nothing */
break;
}
}
/**

View file

@ -23,50 +23,72 @@
/* @test LimitSharedSizes
* @summary Test handling of limits on shared space size
* @library /testlibrary
* @library /testlibrary /runtime/CommandLine/OptionsValidation/common
* @modules java.base/sun.misc
* java.management
* @run main LimitSharedSizes
*/
import jdk.test.lib.*;
import optionsvalidation.JVMOptionsUtils;
public class LimitSharedSizes {
static enum Result {
OUT_OF_RANGE,
TOO_SMALL,
VALID,
VALID_ARCHIVE
}
static enum Region {
RO, RW, MD, MC
}
private static final boolean fitsRange(String name, String value) throws RuntimeException {
boolean fits = true;
try {
fits = JVMOptionsUtils.fitsRange(name, value);
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
return fits;
}
private static class SharedSizeTestData {
public String optionName;
public String optionValue;
public String expectedErrorMsg;
public Result optionResult;
public SharedSizeTestData(Region region, String value, String msg) {
optionName = getName(region);
public SharedSizeTestData(Region region, String value) {
optionName = "-XX:"+getName(region);
optionValue = value;
expectedErrorMsg = msg;
if (fitsRange(getName(region), value) == false) {
optionResult = Result.OUT_OF_RANGE;
} else {
optionResult = Result.TOO_SMALL;
}
}
public SharedSizeTestData(Region region, String msg) {
optionName = getName(region);
optionValue = getValue(region);
expectedErrorMsg = msg;
public SharedSizeTestData(Region region, String value, Result result) {
optionName = "-XX:"+getName(region);
optionValue = value;
optionResult = result;
}
private String getName(Region region) {
String name;
switch (region) {
case RO:
name = "-XX:SharedReadOnlySize";
name = "SharedReadOnlySize";
break;
case RW:
name = "-XX:SharedReadWriteSize";
name = "SharedReadWriteSize";
break;
case MD:
name = "-XX:SharedMiscDataSize";
name = "SharedMiscDataSize";
break;
case MC:
name = "-XX:SharedMiscCodeSize";
name = "SharedMiscCodeSize";
break;
default:
name = "Unknown";
@ -75,53 +97,37 @@ public class LimitSharedSizes {
return name;
}
private String getValue(Region region) {
String value;
switch (region) {
case RO:
value = Platform.is64bit() ? "9M" : "8M";
break;
case RW:
value = Platform.is64bit() ? "12M" : "7M";
break;
case MD:
value = Platform.is64bit() ? "4M" : "2M";
break;
case MC:
value = "120k";
break;
default:
value = "0M";
break;
}
return value;
public Result getResult() {
return optionResult;
}
}
private static final SharedSizeTestData[] testTable = {
// Too small of a region size should not cause a vm crash.
// It should result in an error message like the following:
// It should result in an error message either like the following #1:
// The shared miscellaneous code space is not large enough
// to preload requested classes. Use -XX:SharedMiscCodeSize=
// to increase the initial size of shared miscellaneous code space.
new SharedSizeTestData(Region.RO, "4M", "read only"),
new SharedSizeTestData(Region.RW, "4M", "read write"),
new SharedSizeTestData(Region.MD, "50k", "miscellaneous data"),
new SharedSizeTestData(Region.MC, "20k", "miscellaneous code"),
// or #2:
// The shared miscellaneous code space is outside the allowed range
new SharedSizeTestData(Region.RO, "4M"),
new SharedSizeTestData(Region.RW, "4M"),
new SharedSizeTestData(Region.MD, "50k"),
new SharedSizeTestData(Region.MC, "20k"),
// these values are larger than default ones, but should
// these values are larger than default ones, and should
// be acceptable and not cause failure
new SharedSizeTestData(Region.RO, "20M", null),
new SharedSizeTestData(Region.RW, "20M", null),
new SharedSizeTestData(Region.MD, "20M", null),
new SharedSizeTestData(Region.MC, "20M", null),
new SharedSizeTestData(Region.RO, "20M", Result.VALID),
new SharedSizeTestData(Region.RW, "20M", Result.VALID),
new SharedSizeTestData(Region.MD, "20M", Result.VALID),
new SharedSizeTestData(Region.MC, "20M", Result.VALID),
// test with sizes which just meet the minimum required sizes
// the following tests also attempt to use the shared archive
new SharedSizeTestData(Region.RO, "UseArchive"),
new SharedSizeTestData(Region.RW, "UseArchive"),
new SharedSizeTestData(Region.MD, "UseArchive"),
new SharedSizeTestData(Region.MC, "UseArchive")
new SharedSizeTestData(Region.RO, Platform.is64bit() ? "9M":"8M", Result.VALID_ARCHIVE),
new SharedSizeTestData(Region.RW, Platform.is64bit() ? "12M":"7M", Result.VALID_ARCHIVE),
new SharedSizeTestData(Region.MD, Platform.is64bit() ? "4M":"2M", Result.VALID_ARCHIVE),
new SharedSizeTestData(Region.MC, "120k", Result.VALID_ARCHIVE),
};
public static void main(String[] args) throws Exception {
@ -131,6 +137,7 @@ public class LimitSharedSizes {
counter++;
String option = td.optionName + "=" + td.optionValue;
System.out.println("testing option number <" + counter + ">");
System.out.println("testing option <" + option + ">");
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
@ -141,16 +148,14 @@ public class LimitSharedSizes {
OutputAnalyzer output = new OutputAnalyzer(pb.start());
if (td.expectedErrorMsg != null) {
if (!td.expectedErrorMsg.equals("UseArchive")) {
output.shouldContain("The shared " + td.expectedErrorMsg
+ " space is not large enough");
output.shouldHaveExitValue(2);
} else {
switch (td.getResult()) {
case VALID:
case VALID_ARCHIVE:
{
output.shouldNotContain("space is not large enough");
output.shouldHaveExitValue(0);
if (td.getResult() == Result.VALID_ARCHIVE) {
// try to use the archive
pb = ProcessTools.createJavaProcessBuilder(
"-XX:+UnlockDiagnosticVMOptions",
@ -175,9 +180,20 @@ public class LimitSharedSizes {
}
output.shouldHaveExitValue(0);
}
} else {
output.shouldNotContain("space is not large enough");
output.shouldHaveExitValue(0);
}
break;
case TOO_SMALL:
{
output.shouldContain("space is not large enough");
output.shouldHaveExitValue(2);
}
break;
case OUT_OF_RANGE:
{
output.shouldContain("outside the allowed range");
output.shouldHaveExitValue(1);
}
break;
}
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2015, 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.
*/
public class Prog {
public static void main(String args[]) {
System.out.println("Java class invoked: " + args[0]);
}
}

View file

@ -0,0 +1,118 @@
/*
* Copyright (c) 2015, 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.
*/
import java.lang.ProcessBuilder.Redirect;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
import java.util.List;
import jdk.test.lib.Asserts;
/*
* @test
* @key cte_test
* @bug 4345157
* @summary JDK 1.3.0 alters thread signal mask
* @requires (os.simpleArch == "sparcv9")
* @library /testlibrary
* @compile Prog.java
* @run main/native ThreadSignalMask
*/
public class ThreadSignalMask {
public static void main(String args[]) throws Exception {
String testClasses = getSystemProperty("test.classes");
String testNativePath = getSystemProperty("test.nativepath");
String testJdk = getSystemProperty("test.jdk");
Path currentDirPath = Paths.get(".");
Path classFilePath = Paths.get(testClasses,
Prog.class.getSimpleName() + ".class");
// copy Prog.class file to be invoked from native
Files.copy(classFilePath,
currentDirPath.resolve(Prog.class.getSimpleName() + ".class"),
StandardCopyOption.REPLACE_EXISTING);
Path executableFilePath = Paths.get(testNativePath,
ThreadSignalMask.class.getSimpleName());
Path executableFileLocalPath = currentDirPath.resolve(
ThreadSignalMask.class.getSimpleName());
// copy compiled native executable ThreadSignalMask
Files.copy(executableFilePath,
executableFileLocalPath,
StandardCopyOption.REPLACE_EXISTING);
executableFileLocalPath.toFile().setExecutable(true);
long[] intervalsArray = {2000, 5000, 10000, 20000};
List<String> processArgs = Arrays.asList(
executableFileLocalPath.toString(),
testJdk);
ProcessBuilder pb = new ProcessBuilder(processArgs);
pb.redirectOutput(Redirect.INHERIT);
pb.redirectError(Redirect.INHERIT);
int result = 0;
for (long interval : intervalsArray) {
Process p = pb.start();
// sleep for a specified period of time to let native run
sleep(interval);
p.destroy();
// wait for process to finish, get exit value and validate it
result = p.waitFor();
System.out.println("Result = " + result);
if (result == 0) {
break;
}
}
Asserts.assertEquals(result, 0);
}
// Utility method to handle Thread.sleep
private static void sleep(long millis) throws InterruptedException {
System.out.println("Sleep for " + millis);
Thread.sleep(millis);
}
// Utility method to retrieve and validate system properties
private static String getSystemProperty(String propertyName) throws Error {
String systemProperty = System.getProperty(propertyName, "").trim();
System.out.println(propertyName + " = " + systemProperty);
if (systemProperty.isEmpty()) {
throw new Error("TESTBUG: property " + propertyName + " is empty");
}
return systemProperty;
}
}

View file

@ -0,0 +1,253 @@
/*
* Copyright (c) 2015, 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.
*/
#define _POSIX_PTHREAD_SEMANTICS // to enable POSIX semantics for certain common APIs
#include <jni.h>
#include <dlfcn.h>
#include <limits.h>
#include <pthread.h>
#include <signal.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void *handle;
char *error;
char path[PATH_MAX];
jint(JNICALL *jni_create_java_vm)(JavaVM **, JNIEnv **, void *) = NULL;
JavaVM *jvm;
// method to perform dlclose on an open dynamic library handle
void closeHandle() {
dlclose(handle);
if ((error = dlerror()) != NULL) {
fputs("Error occurred while closing handle\n", stderr);
}
}
// method to exit with a fail status
void fail() {
if (handle) {
closeHandle();
}
exit(1);
}
// method to handle occurred error and fail
void handleError(char *messageTitle, char *messageBody) {
fprintf(stderr, "%s: %s\n", messageTitle, messageBody);
fail();
}
// method to load the dynamic library libjvm
void loadJVM() {
char lib[PATH_MAX];
snprintf(lib, sizeof (lib), "%s/lib/sparcv9/server/libjvm.so", path);
handle = dlopen(lib, RTLD_LAZY);
if (!handle) {
handleError(dlerror(), "2");
}
fputs("Will load JVM...\n", stdout);
// find the address of function
*(void **) (&jni_create_java_vm) = dlsym(handle, "JNI_CreateJavaVM");
if ((error = dlerror()) != NULL) {
handleError(error, "3");
}
fputs("JVM loaded okay.\n", stdout);
}
// method to get created jvm environment
JNIEnv* initJVM() {
JNIEnv *env = NULL;
JavaVMInitArgs vm_args;
JavaVMOption options[1];
jint res;
options[0].optionString = "-Xrs";
vm_args.version = JNI_VERSION_1_2;
vm_args.nOptions = 1;
vm_args.options = options;
vm_args.ignoreUnrecognized = JNI_FALSE;
fputs("Will create JVM...\n", stdout);
res = (*jni_create_java_vm)(&jvm, &env, &vm_args);
if (res < 0) {
handleError("Can't create Java VM", strerror(res));
}
fputs("JVM created OK!\n", stdout);
return env;
}
// method to invoke java method from java class
void callJava(JNIEnv *env) {
jclass cls;
jmethodID mid;
jstring jstr;
jobjectArray args;
cls = (*env)->FindClass(env, "Prog");
if (cls == 0) {
handleError("FindClass", "Can't find Prog class");
}
mid = (*env)->GetStaticMethodID(env, cls, "main", "([Ljava/lang/String;)V");
if (mid == 0) {
handleError("GetStaticMethodID", "Can't find Prog.main");
}
jstr = (*env)->NewStringUTF(env, "from C!");
if (jstr == 0) {
handleError("NewStringUTF", "Out of memory");
}
args = (*env)->NewObjectArray(env, 1,
(*env)->FindClass(env, "java/lang/String"), jstr);
if (args == 0) {
handleError("NewObjectArray", "Out of memory");
}
(*env)->CallStaticVoidMethod(env, cls, mid, args);
}
// method to load, init jvm and then invoke java method
void* loadAndCallJava(void* x) {
JNIEnv *env;
fputs("Some thread will create JVM.\n", stdout);
loadJVM();
env = initJVM();
fputs("Some thread will call Java.\n", stdout);
callJava(env);
if ((*jvm)->DetachCurrentThread(jvm) != 0)
fputs("Error: thread not detached!\n", stderr);
fputs("Some thread exiting.\n", stdout);
return env;
}
int main(int argc, char **argv) {
JNIEnv *env;
sigset_t set;
pthread_t thr1;
pthread_attr_t attr;
size_t ss = 0;
int sig;
int rc; // return code for pthread_* methods
// verify input
if (argc != 2) {
handleError("usage", "a.out jdk_path");
}
// copy input jdk path into a char buffer
strncpy(path, argv[1], PATH_MAX);
// add null termination character
path[PATH_MAX - 1] = '\0';
fputs("Main thread will set signal mask.\n", stdout);
// initialize the signal set
sigemptyset(&set);
// add a number of signals to a signal set
sigaddset(&set, SIGPIPE);
sigaddset(&set, SIGTERM);
sigaddset(&set, SIGHUP);
sigaddset(&set, SIGINT);
// examine and change mask of blocked signal
if ((rc = pthread_sigmask(SIG_BLOCK, &set, NULL))) {
// handle error if occurred
handleError("main: pthread_sigmask() error", strerror(rc));
}
// initializes the thread attributes object with default attribute values
if ((rc = pthread_attr_init(&attr))) {
// handle error if occurred
handleError("main: pthread_attr_init() error", strerror(rc));
}
ss = 1024 * 1024;
// set the stack size attribute of the thread attributes object
if ((rc = pthread_attr_setstacksize(&attr, ss))) {
// handle error if occurred
handleError("main: pthread_attr_setstacksize() error", strerror(rc));
}
// get the stack size attribute of the thread attributes object
if ((rc = pthread_attr_getstacksize(&attr, &ss))) {
// handle error if occurred
handleError("main: pthread_attr_getstacksize() error", strerror(rc));
}
fprintf(stderr, "Stack size: %zu\n", ss);
// start a new thread in the calling process,
// loadAndCallJava logic is passed as a start_routine argument
if ((rc = pthread_create(&thr1, NULL, loadAndCallJava, NULL))) {
// handle error if occurred
handleError("main: pthread_create() error", strerror(rc));
}
// initialize the signal set
sigemptyset(&set);
// add a number of signals to a signal set
sigaddset(&set, SIGTERM);
sigaddset(&set, SIGHUP);
sigaddset(&set, SIGINT);
fputs("Main thread waiting for signal.\n", stdout);
do {
int err;
sig = 0;
err = sigwait(&set, &sig);
if (err != 0) {
// print error message if unexpected signal occurred
fprintf(stderr, "main: sigwait() error: %s\n", strerror(err));
} else {
// print success message and exit if expected signal occurred
// this branch generally acts when JVM executes destroy()
fprintf(stdout, "main: sigwait() got: %d\nSucceed!\n", sig);
exit(0);
}
} while (sig != SIGTERM && sig != SIGINT); // exit the loop condition
// join with a terminated thread
if ((rc = pthread_join(thr1, NULL))) {
// handle error if occurred
handleError("main: pthread_join() error", strerror(rc));
}
// close an open dynamic library handle
closeHandle();
fputs("Main thread exiting.\n", stdout);
return 0;
}