--- old/src/share/vm/code/codeBlob.cpp 2019-08-16 11:53:59.902826282 +0800 +++ new/src/share/vm/code/codeBlob.cpp 2019-08-16 11:53:59.839824087 +0800 @@ -58,7 +58,7 @@ #include "c1/c1_Runtime1.hpp" #endif -unsigned int align_code_offset(int offset) { +unsigned int CodeBlob::align_code_offset(int offset) { // align the size to CodeEntryAlignment return ((offset + (int)CodeHeap::header_size() + (CodeEntryAlignment-1)) & ~(CodeEntryAlignment-1)) --- old/src/share/vm/code/codeBlob.hpp 2019-08-16 11:54:00.202836737 +0800 +++ new/src/share/vm/code/codeBlob.hpp 2019-08-16 11:54:00.138834507 +0800 @@ -30,6 +30,15 @@ #include "runtime/frame.hpp" #include "runtime/handles.hpp" +// CodeBlob Types +// Used in the CodeCache to assign CodeBlobs to different CodeHeaps +struct CodeBlobType { + enum { + All = 0, // All types (No code cache segmentation) + NumTypes = 1 // Number of CodeBlobTypes + }; +}; + // CodeBlob - superclass for all entries in the CodeCache. // // Suptypes are: @@ -71,6 +80,7 @@ public: // Returns the space needed for CodeBlob static unsigned int allocation_size(CodeBuffer* cb, int header_size); + static unsigned int align_code_offset(int offset); // Creation // a) simple CodeBlob @@ -205,6 +215,7 @@ friend class AdapterBlob; friend class VtableBlob; friend class MethodHandlesAdapterBlob; + friend class WhiteBox; private: // Creation support --- old/src/share/vm/code/codeCache.cpp 2019-08-16 11:54:00.502847191 +0800 +++ new/src/share/vm/code/codeCache.cpp 2019-08-16 11:54:00.437844926 +0800 @@ -189,6 +189,12 @@ if (cb != NULL) break; if (!_heap->expand_by(CodeCacheExpansionSize)) { // Expansion failed + if (CodeCache_lock->owned_by_self()) { + MutexUnlockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + report_codemem_full(); + } else { + report_codemem_full(); + } return NULL; } if (PrintCodeCacheExtension) { @@ -780,6 +786,7 @@ _codemem_full_count++; EventCodeCacheFull event; if (event.should_commit()) { + event.set_codeBlobType((u1)CodeBlobType::All); event.set_startAddress((u8)low_bound()); event.set_commitedTopAddress((u8)high()); event.set_reservedTopAddress((u8)high_bound()); --- old/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp 2019-08-16 11:54:00.814858064 +0800 +++ new/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp 2019-08-16 11:54:00.741855520 +0800 @@ -3572,6 +3572,28 @@ } #endif // PRODUCT +G1HeapSummary G1CollectedHeap::create_g1_heap_summary() { + + size_t eden_used_bytes = _young_list->eden_used_bytes(); + size_t survivor_used_bytes = _young_list->survivor_used_bytes(); + size_t heap_used = Heap_lock->owned_by_self() ? used() : used_unlocked(); + + size_t eden_capacity_bytes = + (g1_policy()->young_list_target_length() * HeapRegion::GrainBytes) - survivor_used_bytes; + + VirtualSpaceSummary heap_summary = create_heap_space_summary(); + return G1HeapSummary(heap_summary, heap_used, eden_used_bytes, + eden_capacity_bytes, survivor_used_bytes, num_regions()); +} + +void G1CollectedHeap::trace_heap(GCWhen::Type when, GCTracer* gc_tracer) { + const G1HeapSummary& heap_summary = create_g1_heap_summary(); + gc_tracer->report_gc_heap_summary(when, heap_summary); + + const MetaspaceSummary& metaspace_summary = create_metaspace_summary(); + gc_tracer->report_metaspace_summary(when, metaspace_summary); +} + G1CollectedHeap* G1CollectedHeap::heap() { assert(_sh->kind() == CollectedHeap::G1CollectedHeap, "not a garbage-first heap"); --- old/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp 2019-08-16 11:54:01.172870539 +0800 +++ new/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp 2019-08-16 11:54:01.104868170 +0800 @@ -375,6 +375,8 @@ size_t size, size_t translation_factor); + void trace_heap(GCWhen::Type when, GCTracer* tracer); + double verify(bool guard, const char* msg); void verify_before_gc(); void verify_after_gc(); @@ -1621,6 +1623,8 @@ bool is_obj_dead_cond(const oop obj, const VerifyOption vo) const; + G1HeapSummary create_g1_heap_summary(); + // Printing virtual void print_on(outputStream* st) const; --- old/src/share/vm/gc_implementation/g1/heapRegion.cpp 2019-08-16 11:54:01.484881412 +0800 +++ new/src/share/vm/gc_implementation/g1/heapRegion.cpp 2019-08-16 11:54:01.419879147 +0800 @@ -37,6 +37,7 @@ #include "memory/space.inline.hpp" #include "oops/oop.inline.hpp" #include "runtime/orderAccess.inline.hpp" +#include "gc_implementation/g1/heapRegionTracer.hpp" PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC @@ -211,6 +212,31 @@ _gc_efficiency = (double) reclaimable_bytes() / region_elapsed_time_ms; } +void HeapRegion::set_free() { + report_region_type_change(G1HeapRegionTraceType::Free); + _type.set_free(); +} + +void HeapRegion::set_eden() { + report_region_type_change(G1HeapRegionTraceType::Eden); + _type.set_eden(); +} + +void HeapRegion::set_eden_pre_gc() { + report_region_type_change(G1HeapRegionTraceType::Eden); + _type.set_eden_pre_gc(); +} + +void HeapRegion::set_survivor() { + report_region_type_change(G1HeapRegionTraceType::Survivor); + _type.set_survivor(); +} + +void HeapRegion::set_old() { + report_region_type_change(G1HeapRegionTraceType::Old); + _type.set_old(); +} + void HeapRegion::set_startsHumongous(HeapWord* new_top, HeapWord* new_end) { assert(!isHumongous(), "sanity / pre-condition"); assert(end() == _orig_end, @@ -218,6 +244,7 @@ assert(top() == bottom(), "should be empty"); assert(bottom() <= new_top && new_top <= new_end, "pre-condition"); + report_region_type_change(G1HeapRegionTraceType::StartsHumongous); _type.set_starts_humongous(); _humongous_start_region = this; @@ -232,6 +259,7 @@ assert(top() == bottom(), "should be empty"); assert(first_hr->startsHumongous(), "pre-condition"); + report_region_type_change(G1HeapRegionTraceType::ContinuesHumongous); _type.set_continues_humongous(); _humongous_start_region = first_hr; } @@ -303,6 +331,14 @@ record_timestamp(); } +void HeapRegion::report_region_type_change(G1HeapRegionTraceType::Type to) { + HeapRegionTracer::send_region_type_change(_hrm_index, + get_trace_type(), + to, + (uintptr_t)bottom(), + used()); +} + CompactibleSpace* HeapRegion::next_compaction_space() const { return G1CollectedHeap::heap()->next_compaction_region(this); } --- old/src/share/vm/gc_implementation/g1/heapRegion.hpp 2019-08-16 11:54:01.789892040 +0800 +++ new/src/share/vm/gc_implementation/g1/heapRegion.hpp 2019-08-16 11:54:01.726889845 +0800 @@ -35,6 +35,7 @@ #include "memory/space.inline.hpp" #include "memory/watermark.hpp" #include "utilities/macros.hpp" +#include "gc_implementation/g1/g1HeapRegionTraceType.hpp" // A HeapRegion is the smallest piece of a G1CollectedHeap that // can be collected independently. @@ -211,6 +212,8 @@ G1BlockOffsetArrayContigSpace* offsets() { return &_offsets; } + void report_region_type_change(G1HeapRegionTraceType::Type to); + protected: // The index of this region in the heap region sequence. uint _hrm_index; @@ -405,6 +408,7 @@ const char* get_type_str() const { return _type.get_str(); } const char* get_short_type_str() const { return _type.get_short_str(); } + G1HeapRegionTraceType::Type get_trace_type() { return _type.get_trace_type(); } bool is_free() const { return _type.is_free(); } @@ -667,13 +671,13 @@ } } - void set_free() { _type.set_free(); } + void set_free(); - void set_eden() { _type.set_eden(); } - void set_eden_pre_gc() { _type.set_eden_pre_gc(); } - void set_survivor() { _type.set_survivor(); } + void set_eden(); + void set_eden_pre_gc(); + void set_survivor(); - void set_old() { _type.set_old(); } + void set_old(); // Determine if an object has been allocated since the last // mark performed by the collector. This returns true iff the object --- old/src/share/vm/gc_implementation/g1/heapRegionType.cpp 2019-08-16 11:54:02.094902669 +0800 +++ new/src/share/vm/gc_implementation/g1/heapRegionType.cpp 2019-08-16 11:54:02.030900439 +0800 @@ -67,3 +67,18 @@ // keep some compilers happy return NULL; } + +G1HeapRegionTraceType::Type HeapRegionType::get_trace_type() { + hrt_assert_is_valid(_tag); + switch (_tag) { + case FreeTag: return G1HeapRegionTraceType::Free; + case EdenTag: return G1HeapRegionTraceType::Eden; + case SurvTag: return G1HeapRegionTraceType::Survivor; + case HumStartsTag: return G1HeapRegionTraceType::StartsHumongous; + case HumContTag: return G1HeapRegionTraceType::ContinuesHumongous; + case OldTag: return G1HeapRegionTraceType::Old; + default: + ShouldNotReachHere(); + return G1HeapRegionTraceType::Free; // keep some compilers happy + } +} --- old/src/share/vm/gc_implementation/g1/heapRegionType.hpp 2019-08-16 11:54:02.395913158 +0800 +++ new/src/share/vm/gc_implementation/g1/heapRegionType.hpp 2019-08-16 11:54:02.331910928 +0800 @@ -26,6 +26,7 @@ #define SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONTYPE_HPP #include "memory/allocation.hpp" +#include "gc_implementation/g1/g1HeapRegionTraceType.hpp" #define hrt_assert_is_valid(tag) \ assert(is_valid((tag)), err_msg("invalid HR type: %u", (uint) (tag))) @@ -127,6 +128,7 @@ const char* get_str() const; const char* get_short_str() const; + G1HeapRegionTraceType::Type get_trace_type(); HeapRegionType() : _tag(FreeTag) { hrt_assert_is_valid(_tag); } }; --- old/src/share/vm/gc_implementation/shared/gcHeapSummary.hpp 2019-08-16 11:54:02.692923508 +0800 +++ new/src/share/vm/gc_implementation/shared/gcHeapSummary.hpp 2019-08-16 11:54:02.629921312 +0800 @@ -78,11 +78,13 @@ class GCHeapSummary; class PSHeapSummary; +class G1HeapSummary; class GCHeapSummaryVisitor { public: virtual void visit(const GCHeapSummary* heap_summary) const = 0; virtual void visit(const PSHeapSummary* heap_summary) const {} + virtual void visit(const G1HeapSummary* heap_summary) const {} }; class GCHeapSummary : public StackObj { @@ -122,6 +124,24 @@ virtual void accept(GCHeapSummaryVisitor* visitor) const { visitor->visit(this); + } +}; + +class G1HeapSummary : public GCHeapSummary { + size_t _edenUsed; + size_t _edenCapacity; + size_t _survivorUsed; + uint _numberOfRegions; + public: + G1HeapSummary(VirtualSpaceSummary& heap_space, size_t heap_used, size_t edenUsed, size_t edenCapacity, size_t survivorUsed, uint numberOfRegions) : + GCHeapSummary(heap_space, heap_used), _edenUsed(edenUsed), _edenCapacity(edenCapacity), _survivorUsed(survivorUsed), _numberOfRegions(numberOfRegions) { } + const size_t edenUsed() const { return _edenUsed; } + const size_t edenCapacity() const { return _edenCapacity; } + const size_t survivorUsed() const { return _survivorUsed; } + const uint numberOfRegions() const { return _numberOfRegions; } + + virtual void accept(GCHeapSummaryVisitor* visitor) const { + visitor->visit(this); } }; --- old/src/share/vm/gc_implementation/shared/gcTraceSend.cpp 2019-08-16 11:54:02.988933823 +0800 +++ new/src/share/vm/gc_implementation/shared/gcTraceSend.cpp 2019-08-16 11:54:02.922931523 +0800 @@ -349,20 +349,20 @@ } } -// void visit(const G1HeapSummary* g1_heap_summary) const { -// visit((GCHeapSummary*)g1_heap_summary); -// -// EventG1HeapSummary e; -// if (e.should_commit()) { -// e.set_gcId(_shared_gc_info.gc_id().id()); -// e.set_when((u1)_when); -// e.set_edenUsedSize(g1_heap_summary->edenUsed()); -// e.set_edenTotalSize(g1_heap_summary->edenCapacity()); -// e.set_survivorUsedSize(g1_heap_summary->survivorUsed()); -// e.set_numberOfRegions(g1_heap_summary->numberOfRegions()); -// e.commit(); -// } -// } + void visit(const G1HeapSummary* g1_heap_summary) const { + visit((GCHeapSummary*)g1_heap_summary); + + EventG1HeapSummary e; + if (e.should_commit()) { + e.set_gcId(_gc_id.id()); + e.set_when((u1)_when); + e.set_edenUsedSize(g1_heap_summary->edenUsed()); + e.set_edenTotalSize(g1_heap_summary->edenCapacity()); + e.set_survivorUsedSize(g1_heap_summary->survivorUsed()); + e.set_numberOfRegions(g1_heap_summary->numberOfRegions()); + e.commit(); + } + } void visit(const PSHeapSummary* ps_heap_summary) const { visit((GCHeapSummary*)ps_heap_summary); --- old/src/share/vm/jfr/periodic/sampling/jfrThreadSampler.cpp 2019-08-16 11:54:03.293944452 +0800 +++ new/src/share/vm/jfr/periodic/sampling/jfrThreadSampler.cpp 2019-08-16 11:54:03.227942152 +0800 @@ -321,7 +321,8 @@ volatile bool _disenrolled; static Monitor* _transition_block_lock; -// JavaThread* next_thread(ThreadsList* t_list, JavaThread* first_sampled, JavaThread* current); + int find_index_of_JavaThread(JavaThread** t_list, uint length, JavaThread *target); + JavaThread* next_thread(JavaThread** t_list, uint length, JavaThread* first_sampled, JavaThread* current); void task_stacktrace(JfrSampleType type, JavaThread** last_thread); JfrThreadSampler(size_t interval_java, size_t interval_native, u4 max_frames); ~JfrThreadSampler(); @@ -344,7 +345,7 @@ Monitor* JfrThreadSampler::_transition_block_lock = new Monitor(Mutex::leaf, "Trace block", true); static void clear_transition_block(JavaThread* jt) { -// jt->clear_trace_flag(); + jt->clear_trace_flag(); JfrThreadLocal* const tl = jt->jfr_thread_local(); if (tl->is_trace_block()) { MutexLockerEx ml(JfrThreadSampler::transition_block(), Mutex::_no_safepoint_check_flag); @@ -359,7 +360,7 @@ } bool ret = false; -// thread->set_trace_flag(); + thread->set_trace_flag(); if (!UseMembar) { os::serialize_thread_states(); } @@ -398,37 +399,61 @@ JfrThreadLocal* const tl = thread->jfr_thread_local(); tl->set_trace_block(); { -// MutexLockerEx ml(transition_block(), Mutex::_no_safepoint_check_flag); -// while (thread->is_trace_suspend()) { -// transition_block()->wait(true); -// } -// tl->clear_trace_block(); + MutexLockerEx ml(transition_block(), Mutex::_no_safepoint_check_flag); + while (thread->is_trace_suspend()) { + transition_block()->wait(true); + } + tl->clear_trace_block(); } } -//JavaThread* JfrThreadSampler::next_thread(ThreadsList* t_list, JavaThread* first_sampled, JavaThread* current) { -// assert(t_list != NULL, "invariant"); -// assert(Threads_lock->owned_by_self(), "Holding the thread table lock."); -// assert(_cur_index >= -1 && (uint)_cur_index + 1 <= t_list->length(), "invariant"); -// assert((current == NULL && -1 == _cur_index) || (t_list->find_index_of_JavaThread(current) == _cur_index), "invariant"); -// if ((uint)_cur_index + 1 == t_list->length()) { -// // wrap -// _cur_index = 0; -// } else { -// _cur_index++; -// } -// assert(_cur_index >= 0 && (uint)_cur_index < t_list->length(), "invariant"); -// JavaThread* const next = t_list->thread_at(_cur_index); -// return next != first_sampled ? next : NULL; -//} +int JfrThreadSampler::find_index_of_JavaThread(JavaThread** t_list, uint length, JavaThread *target) { + assert(Threads_lock->owned_by_self(), "Holding the thread table lock."); + if (target == NULL) { + return -1; + } + for (uint i = 0; i < length; i++) { + if (target == t_list[i]) { + return (int)i; + } + } + return -1; +} + +JavaThread* JfrThreadSampler::next_thread(JavaThread** t_list, uint length, JavaThread* first_sampled, JavaThread* current) { + assert(Threads_lock->owned_by_self(), "Holding the thread table lock."); + if (current == NULL) { + _cur_index = 0; + return t_list[_cur_index]; + } + + if (_cur_index == -1 || t_list[_cur_index] != current) { + // 'current' is not at '_cur_index' so find it: + _cur_index = find_index_of_JavaThread(t_list, length, current); + assert(_cur_index != -1, "current JavaThread should be findable."); + } + _cur_index++; + + JavaThread* next = NULL; + // wrap + if ((uint)_cur_index >= length) { + _cur_index = 0; + } + next = t_list[_cur_index]; + + // sample wrap + if (next == first_sampled) { + return NULL; + } + return next; +} void JfrThreadSampler::start_thread() { - // XXX TODO implement sampling -// if (os::create_thread(this, os::os_thread)) { -// os::start_thread(this); -// } else { -// if (true) tty->print_cr("Failed to create thread for thread sampling"); -// } + if (os::create_thread(this, os::os_thread)) { + os::start_thread(this); + } else { + tty->print_cr("Failed to create thread for thread sampling"); + } } void JfrThreadSampler::enroll() { @@ -510,28 +535,33 @@ elapsedTimer sample_time; sample_time.start(); { -// MonitorLockerEx tlock(Threads_lock, Mutex::_allow_vm_block_flag); -// ThreadsListHandle tlh; -// // Resolve a sample session relative start position index into the thread list array. -// // In cases where the last sampled thread is NULL or not-NULL but stale, find_index() returns -1. -// _cur_index = tlh.list()->find_index_of_JavaThread(*last_thread); -// JavaThread* current = _cur_index != -1 ? *last_thread : NULL; -// -// while (num_sample_attempts < sample_limit) { -// current = next_thread(tlh.list(), start, current); -// if (current == NULL) { -// break; -// } -// if (start == NULL) { -// start = current; // remember the thread where we started to attempt sampling -// } -// if (current->is_Compiler_thread()) { -// continue; -// } -// sample_task.do_sample_thread(current, _frames, _max_frames, type); -// num_sample_attempts++; -// } -// *last_thread = current; // remember the thread we last attempted to sample + MonitorLockerEx tlock(Threads_lock, Mutex::_allow_vm_block_flag); + int max_threads = Threads::number_of_threads(); + assert(max_threads >= 0, "Threads list is empty"); + uint index = 0; + JavaThread** threads_list = NEW_C_HEAP_ARRAY(JavaThread *, max_threads, mtInternal); + for (JavaThread* tp = Threads::first(); tp != NULL; tp = tp->next()) { + threads_list[index++] = tp; + } + JavaThread* current = Threads::includes(*last_thread) ? *last_thread : NULL; + JavaThread* start = NULL; + + while (num_sample_attempts < sample_limit) { + current = next_thread(threads_list, index, start, current); + if (current == NULL) { + break; + } + if (start == NULL) { + start = current; // remember the thread where we started to attempt sampling + } + if (current->is_Compiler_thread()) { + continue; + } + sample_task.do_sample_thread(current, _frames, _max_frames, type); + num_sample_attempts++; + } + *last_thread = current; // remember the thread we last attempted to sample + FREE_C_HEAP_ARRAY(JavaThread *, threads_list, mtInternal); } sample_time.stop(); if (LogJFR && Verbose) tty->print_cr("JFR thread sampling done in %3.7f secs with %d java %d native samples", --- old/src/share/vm/jfr/recorder/checkpoint/types/jfrType.cpp 2019-08-16 11:54:03.600955150 +0800 +++ new/src/share/vm/jfr/recorder/checkpoint/types/jfrType.cpp 2019-08-16 11:54:03.536952920 +0800 @@ -188,13 +188,12 @@ } void G1HeapRegionTypeConstant::serialize(JfrCheckpointWriter& writer) { - // XXX TODO? -// static const u4 nof_entries = G1HeapRegionTraceType::G1HeapRegionTypeEndSentinel; -// writer.write_count(nof_entries); -// for (u4 i = 0; i < nof_entries; ++i) { -// writer.write_key(i); -// writer.write(G1HeapRegionTraceType::to_string((G1HeapRegionTraceType::Type)i)); -// } + static const u4 nof_entries = G1HeapRegionTraceType::G1HeapRegionTypeEndSentinel; + writer.write_count(nof_entries); + for (u4 i = 0; i < nof_entries; ++i) { + writer.write_key(i); + writer.write(G1HeapRegionTraceType::to_string((G1HeapRegionTraceType::Type)i)); + } } void GCThresholdUpdaterConstant::serialize(JfrCheckpointWriter& writer) { @@ -279,13 +278,10 @@ } void CodeBlobTypeConstant::serialize(JfrCheckpointWriter& writer) { - // XXX no code blob types. need to send any stub value? -// static const u4 nof_entries = CodeBlobType::NumTypes; -// writer.write_count(nof_entries); -// for (u4 i = 0; i < nof_entries; ++i) { -// writer.write_key(i); -// writer.write(CodeCache::get_code_heap_name(i)); -// } + static const u4 nof_entries = CodeBlobType::NumTypes; + writer.write_count(nof_entries); + writer.write_key((u4)CodeBlobType::All); + writer.write("CodeCache"); }; void VMOperationTypeConstant::serialize(JfrCheckpointWriter& writer) { --- old/src/share/vm/jfr/support/jfrThreadExtension.hpp 2019-08-16 11:54:03.896965465 +0800 +++ new/src/share/vm/jfr/support/jfrThreadExtension.hpp 2019-08-16 11:54:03.834963304 +0800 @@ -46,7 +46,11 @@ #define THREAD_LOCAL_WRITER_OFFSET_JFR \ JfrThreadLocal::java_event_writer_offset() + THREAD_LOCAL_OFFSET_JFR -// XXX consider implementing thread suspend tracing -#define SUSPEND_THREAD_CONDITIONAL(thread) if (false/*(thread)->is_trace_suspend()*/) JfrThreadSampling::on_javathread_suspend(thread) +#define DEFINE_TRACE_SUSPEND_FLAG_METHODS \ + void set_trace_flag() { set_suspend_flag(_trace_flag); } \ + void clear_trace_flag() { clear_suspend_flag(_trace_flag); } \ + bool is_trace_suspend() { return (_suspend_flags & _trace_flag) != 0; } + +#define SUSPEND_THREAD_CONDITIONAL(thread) if ((thread)->is_trace_suspend()) JfrThreadSampling::on_javathread_suspend(thread) #endif // SHARE_VM_JFR_SUPPORT_JFRTHREADEXTENSION_HPP --- old/src/share/vm/prims/whitebox.cpp 2019-08-16 11:54:04.198975989 +0800 +++ new/src/share/vm/prims/whitebox.cpp 2019-08-16 11:54:04.132973689 +0800 @@ -40,6 +40,7 @@ #include "runtime/interfaceSupport.hpp" #include "runtime/os.hpp" #include "utilities/array.hpp" +#include "utilities/align.hpp" #include "utilities/debug.hpp" #include "utilities/macros.hpp" #include "utilities/exceptions.hpp" @@ -653,13 +654,13 @@ WB_END template -static bool GetVMFlag(JavaThread* thread, JNIEnv* env, jstring name, T* value, bool (*TAt)(const char*, T*)) { +static bool GetVMFlag(JavaThread* thread, JNIEnv* env, jstring name, T* value, bool (*TAt)(const char*, T*, bool, bool)) { if (name == NULL) { return false; } ThreadToNativeFromVM ttnfv(thread); // can't be in VM when we call JNI const char* flag_name = env->GetStringUTFChars(name, NULL); - bool result = (*TAt)(flag_name, value); + bool result = (*TAt)(flag_name, value, true, true); env->ReleaseStringUTFChars(name, flag_name); return result; } @@ -851,6 +852,47 @@ return features_string; WB_END +int WhiteBox::get_blob_type(const CodeBlob* code) { + guarantee(WhiteBoxAPI, "internal testing API :: WhiteBox has to be enabled"); + return CodeBlobType::All;; +} + +struct CodeBlobStub { + CodeBlobStub(const CodeBlob* blob) : + name(os::strdup(blob->name())), + size(blob->size()), + blob_type(WhiteBox::get_blob_type(blob)), + address((jlong) blob) { } + ~CodeBlobStub() { os::free((void*) name); } + const char* const name; + const jint size; + const jint blob_type; + const jlong address; +}; + +static jobjectArray codeBlob2objectArray(JavaThread* thread, JNIEnv* env, CodeBlobStub* cb) { + jclass clazz = env->FindClass(vmSymbols::java_lang_Object()->as_C_string()); + CHECK_JNI_EXCEPTION_(env, NULL); + jobjectArray result = env->NewObjectArray(4, clazz, NULL); + + jstring name = env->NewStringUTF(cb->name); + CHECK_JNI_EXCEPTION_(env, NULL); + env->SetObjectArrayElement(result, 0, name); + + jobject obj = integerBox(thread, env, cb->size); + CHECK_JNI_EXCEPTION_(env, NULL); + env->SetObjectArrayElement(result, 1, obj); + + obj = integerBox(thread, env, cb->blob_type); + CHECK_JNI_EXCEPTION_(env, NULL); + env->SetObjectArrayElement(result, 2, obj); + + obj = longBox(thread, env, cb->address); + CHECK_JNI_EXCEPTION_(env, NULL); + env->SetObjectArrayElement(result, 3, obj); + + return result; +} WB_ENTRY(jobjectArray, WB_GetNMethod(JNIEnv* env, jobject o, jobject method, jboolean is_osr)) ResourceMark rm(THREAD); @@ -888,6 +930,47 @@ return result; WB_END +CodeBlob* WhiteBox::allocate_code_blob(int size, int blob_type) { + guarantee(WhiteBoxAPI, "internal testing API :: WhiteBox has to be enabled"); + BufferBlob* blob; + int full_size = CodeBlob::align_code_offset(sizeof(BufferBlob)); + if (full_size < size) { + full_size += align_up(size - full_size, oopSize); + } + { + MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + blob = (BufferBlob*) CodeCache::allocate(full_size); + ::new (blob) BufferBlob("WB::DummyBlob", full_size); + } + // Track memory usage statistic after releasing CodeCache_lock + MemoryService::track_code_cache_memory_usage(); + return blob; +} + +WB_ENTRY(jlong, WB_AllocateCodeBlob(JNIEnv* env, jobject o, jint size, jint blob_type)) + if (size < 0) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), + err_msg("WB_AllocateCodeBlob: size is negative: " INT32_FORMAT, size)); + } + return (jlong) WhiteBox::allocate_code_blob(size, blob_type); +WB_END + +WB_ENTRY(void, WB_FreeCodeBlob(JNIEnv* env, jobject o, jlong addr)) + if (addr == 0) { + return; + } + BufferBlob::free((BufferBlob*) addr); +WB_END + +WB_ENTRY(jobjectArray, WB_GetCodeBlob(JNIEnv* env, jobject o, jlong addr)) + if (addr == 0) { + THROW_MSG_NULL(vmSymbols::java_lang_NullPointerException(), + "WB_GetCodeBlob: addr is null"); + } + ThreadToNativeFromVM ttn(thread); + CodeBlobStub stub((CodeBlob*) addr); + return codeBlob2objectArray(thread, env, &stub); +WB_END int WhiteBox::array_bytes_to_length(size_t bytes) { return Array::bytes_to_length(bytes); @@ -1167,6 +1250,9 @@ {CC"fullGC", CC"()V", (void*)&WB_FullGC }, {CC"youngGC", CC"()V", (void*)&WB_YoungGC }, {CC"readReservedMemory", CC"()V", (void*)&WB_ReadReservedMemory }, + {CC"allocateCodeBlob", CC"(II)J", (void*)&WB_AllocateCodeBlob }, + {CC"freeCodeBlob", CC"(J)V", (void*)&WB_FreeCodeBlob }, + {CC"getCodeBlob", CC"(J)[Ljava/lang/Object;",(void*)&WB_GetCodeBlob }, {CC"allocateMetaspace", CC"(Ljava/lang/ClassLoader;J)J", (void*)&WB_AllocateMetaspace }, {CC"freeMetaspace", --- old/src/share/vm/prims/whitebox.hpp 2019-08-16 11:54:04.509986827 +0800 +++ new/src/share/vm/prims/whitebox.hpp 2019-08-16 11:54:04.444984561 +0800 @@ -64,7 +64,8 @@ Symbol* signature_symbol); static const char* lookup_jstring(const char* field_name, oop object); static bool lookup_bool(const char* field_name, oop object); - + static int get_blob_type(const CodeBlob* code); + static CodeBlob* allocate_code_blob(int size, int blob_type); static int array_bytes_to_length(size_t bytes); static void register_methods(JNIEnv* env, jclass wbclass, JavaThread* thread, JNINativeMethod* method_array, int method_count); --- old/src/share/vm/runtime/biasedLocking.cpp 2019-08-16 11:54:04.820997664 +0800 +++ new/src/share/vm/runtime/biasedLocking.cpp 2019-08-16 11:54:04.754995364 +0800 @@ -31,6 +31,8 @@ #include "runtime/vframe.hpp" #include "runtime/vmThread.hpp" #include "runtime/vm_operations.hpp" +#include "jfr/support/jfrThreadId.hpp" +#include "jfr/jfrEvents.hpp" static bool _biased_locking_enabled = false; BiasedLockingCounters BiasedLocking::_counters; @@ -142,8 +144,9 @@ return info; } - -static BiasedLocking::Condition revoke_bias(oop obj, bool allow_rebias, bool is_bulk, JavaThread* requesting_thread) { +// After the call, *biased_locker will be set to obj->mark()->biased_locker() if biased_locker != NULL, +// AND it is a living thread. Otherwise it will not be updated, (i.e. the caller is responsible for initialization). +static BiasedLocking::Condition revoke_bias(oop obj, bool allow_rebias, bool is_bulk, JavaThread* requesting_thread, JavaThread** biased_locker) { markOop mark = obj->mark(); if (!mark->has_bias_pattern()) { if (TraceBiasedLocking) { @@ -253,6 +256,11 @@ } } + // If requested, return information on which thread held the bias + if (biased_locker != NULL) { + *biased_locker = biased_thread; + } + return BiasedLocking::BIAS_REVOKED; } @@ -373,7 +381,7 @@ // At this point we're done. All we have to do is potentially // adjust the header of the given object to revoke its bias. - revoke_bias(o, attempt_rebias_of_object && klass->prototype_header()->has_bias_pattern(), true, requesting_thread); + revoke_bias(o, attempt_rebias_of_object && klass->prototype_header()->has_bias_pattern(), true, requesting_thread, NULL); } else { if (TraceBiasedLocking) { ResourceMark rm; @@ -395,14 +403,14 @@ oop owner = mon_info->owner(); markOop mark = owner->mark(); if ((owner->klass() == k_o) && mark->has_bias_pattern()) { - revoke_bias(owner, false, true, requesting_thread); + revoke_bias(owner, false, true, requesting_thread, NULL); } } } // Must force the bias of the passed object to be forcibly revoked // as well to ensure guarantees to callers - revoke_bias(o, false, true, requesting_thread); + revoke_bias(o, false, true, requesting_thread, NULL); } if (TraceBiasedLocking) { @@ -445,19 +453,22 @@ GrowableArray* _objs; JavaThread* _requesting_thread; BiasedLocking::Condition _status_code; + traceid _biased_locker_id; public: VM_RevokeBias(Handle* obj, JavaThread* requesting_thread) : _obj(obj) , _objs(NULL) , _requesting_thread(requesting_thread) - , _status_code(BiasedLocking::NOT_BIASED) {} + , _status_code(BiasedLocking::NOT_BIASED) + , _biased_locker_id(0) {} VM_RevokeBias(GrowableArray* objs, JavaThread* requesting_thread) : _obj(NULL) , _objs(objs) , _requesting_thread(requesting_thread) - , _status_code(BiasedLocking::NOT_BIASED) {} + , _status_code(BiasedLocking::NOT_BIASED) + , _biased_locker_id(0) {} virtual VMOp_Type type() const { return VMOp_RevokeBias; } @@ -486,7 +497,11 @@ if (TraceBiasedLocking) { tty->print_cr("Revoking bias with potentially per-thread safepoint:"); } - _status_code = revoke_bias((*_obj)(), false, false, _requesting_thread); + JavaThread* biased_locker = NULL; + _status_code = revoke_bias((*_obj)(), false, false, _requesting_thread, &biased_locker); + if (biased_locker != NULL) { + _biased_locker_id = JFR_THREAD_ID(biased_locker); + } clean_up_cached_monitor_info(); return; } else { @@ -500,6 +515,10 @@ BiasedLocking::Condition status_code() const { return _status_code; } + + traceid biased_locker() const { + return _biased_locker_id; + } }; @@ -609,23 +628,44 @@ if (TraceBiasedLocking) { tty->print_cr("Revoking bias by walking my own stack:"); } - BiasedLocking::Condition cond = revoke_bias(obj(), false, false, (JavaThread*) THREAD); + EventBiasedLockSelfRevocation event; + BiasedLocking::Condition cond = revoke_bias(obj(), false, false, (JavaThread*) THREAD, NULL); ((JavaThread*) THREAD)->set_cached_monitor_info(NULL); assert(cond == BIAS_REVOKED, "why not?"); + if (event.should_commit()) { + event.set_lockClass(k); + event.commit(); + } return cond; } else { + EventBiasedLockRevocation event; VM_RevokeBias revoke(&obj, (JavaThread*) THREAD); VMThread::execute(&revoke); + if (event.should_commit() && (revoke.status_code() != NOT_BIASED)) { + event.set_lockClass(k); + // Subtract 1 to match the id of events committed inside the safepoint + event.set_safepointId(SafepointSynchronize::safepoint_counter() - 1); + event.set_previousOwner(revoke.biased_locker()); + event.commit(); + } return revoke.status_code(); } } assert((heuristics == HR_BULK_REVOKE) || (heuristics == HR_BULK_REBIAS), "?"); + EventBiasedLockClassRevocation event; VM_BulkRevokeBias bulk_revoke(&obj, (JavaThread*) THREAD, (heuristics == HR_BULK_REBIAS), attempt_rebias); VMThread::execute(&bulk_revoke); + if (event.should_commit()) { + event.set_revokedClass(obj->klass()); + event.set_disableBiasing((heuristics != HR_BULK_REBIAS)); + // Subtract 1 to match the id of events committed inside the safepoint + event.set_safepointId(SafepointSynchronize::safepoint_counter() - 1); + event.commit(); + } return bulk_revoke.status_code(); } @@ -645,7 +685,7 @@ oop obj = h_obj(); HeuristicsResult heuristics = update_heuristics(obj, false); if (heuristics == HR_SINGLE_REVOKE) { - revoke_bias(obj, false, false, NULL); + revoke_bias(obj, false, false, NULL, NULL); } else if ((heuristics == HR_BULK_REBIAS) || (heuristics == HR_BULK_REVOKE)) { bulk_revoke_or_rebias_at_safepoint(obj, (heuristics == HR_BULK_REBIAS), false, NULL); @@ -661,7 +701,7 @@ oop obj = (objs->at(i))(); HeuristicsResult heuristics = update_heuristics(obj, false); if (heuristics == HR_SINGLE_REVOKE) { - revoke_bias(obj, false, false, NULL); + revoke_bias(obj, false, false, NULL, NULL); } else if ((heuristics == HR_BULK_REBIAS) || (heuristics == HR_BULK_REVOKE)) { bulk_revoke_or_rebias_at_safepoint(obj, (heuristics == HR_BULK_REBIAS), false, NULL); --- old/src/share/vm/runtime/globals.cpp 2019-08-16 11:54:05.131008467 +0800 +++ new/src/share/vm/runtime/globals.cpp 2019-08-16 11:54:05.065006167 +0800 @@ -616,8 +616,8 @@ e.commit(); } -bool CommandLineFlags::boolAt(const char* name, size_t len, bool* value) { - Flag* result = Flag::find_flag(name, len); +bool CommandLineFlags::boolAt(const char* name, size_t len, bool* value, bool allow_locked, bool return_flag) { + Flag* result = Flag::find_flag(name, len, allow_locked, return_flag); if (result == NULL) return false; if (!result->is_bool()) return false; *value = result->get_bool(); @@ -644,8 +644,8 @@ faddr->set_origin(origin); } -bool CommandLineFlags::intxAt(const char* name, size_t len, intx* value) { - Flag* result = Flag::find_flag(name, len); +bool CommandLineFlags::intxAt(const char* name, size_t len, intx* value, bool allow_locked, bool return_flag) { + Flag* result = Flag::find_flag(name, len, allow_locked, return_flag); if (result == NULL) return false; if (!result->is_intx()) return false; *value = result->get_intx(); @@ -672,8 +672,8 @@ faddr->set_origin(origin); } -bool CommandLineFlags::uintxAt(const char* name, size_t len, uintx* value) { - Flag* result = Flag::find_flag(name, len); +bool CommandLineFlags::uintxAt(const char* name, size_t len, uintx* value, bool allow_locked, bool return_flag) { + Flag* result = Flag::find_flag(name, len, allow_locked, return_flag); if (result == NULL) return false; if (!result->is_uintx()) return false; *value = result->get_uintx(); @@ -700,8 +700,8 @@ faddr->set_origin(origin); } -bool CommandLineFlags::uint64_tAt(const char* name, size_t len, uint64_t* value) { - Flag* result = Flag::find_flag(name, len); +bool CommandLineFlags::uint64_tAt(const char* name, size_t len, uint64_t* value, bool allow_locked, bool return_flag) { + Flag* result = Flag::find_flag(name, len, allow_locked, return_flag); if (result == NULL) return false; if (!result->is_uint64_t()) return false; *value = result->get_uint64_t(); @@ -728,8 +728,8 @@ faddr->set_origin(origin); } -bool CommandLineFlags::doubleAt(const char* name, size_t len, double* value) { - Flag* result = Flag::find_flag(name, len); +bool CommandLineFlags::doubleAt(const char* name, size_t len, double* value, bool allow_locked, bool return_flag) { + Flag* result = Flag::find_flag(name, len, allow_locked, return_flag); if (result == NULL) return false; if (!result->is_double()) return false; *value = result->get_double(); @@ -756,8 +756,8 @@ faddr->set_origin(origin); } -bool CommandLineFlags::ccstrAt(const char* name, size_t len, ccstr* value) { - Flag* result = Flag::find_flag(name, len); +bool CommandLineFlags::ccstrAt(const char* name, size_t len, ccstr* value, bool allow_locked, bool return_flag) { + Flag* result = Flag::find_flag(name, len, allow_locked, return_flag); if (result == NULL) return false; if (!result->is_ccstr()) return false; *value = result->get_ccstr(); --- old/src/share/vm/runtime/globals.hpp 2019-08-16 11:54:05.451019618 +0800 +++ new/src/share/vm/runtime/globals.hpp 2019-08-16 11:54:05.378017074 +0800 @@ -369,33 +369,33 @@ class CommandLineFlags { public: - static bool boolAt(const char* name, size_t len, bool* value); - static bool boolAt(const char* name, bool* value) { return boolAt(name, strlen(name), value); } + static bool boolAt(const char* name, size_t len, bool* value, bool allow_locked = false, bool return_flag = false); + static bool boolAt(const char* name, bool* value, bool allow_locked = false, bool return_flag = false) { return boolAt(name, strlen(name), value, allow_locked, return_flag); } static bool boolAtPut(const char* name, size_t len, bool* value, Flag::Flags origin); static bool boolAtPut(const char* name, bool* value, Flag::Flags origin) { return boolAtPut(name, strlen(name), value, origin); } - static bool intxAt(const char* name, size_t len, intx* value); - static bool intxAt(const char* name, intx* value) { return intxAt(name, strlen(name), value); } + static bool intxAt(const char* name, size_t len, intx* value, bool allow_locked = false, bool return_flag = false); + static bool intxAt(const char* name, intx* value, bool allow_locked = false, bool return_flag = false) { return intxAt(name, strlen(name), value, allow_locked, return_flag); } static bool intxAtPut(const char* name, size_t len, intx* value, Flag::Flags origin); static bool intxAtPut(const char* name, intx* value, Flag::Flags origin) { return intxAtPut(name, strlen(name), value, origin); } - static bool uintxAt(const char* name, size_t len, uintx* value); - static bool uintxAt(const char* name, uintx* value) { return uintxAt(name, strlen(name), value); } + static bool uintxAt(const char* name, size_t len, uintx* value, bool allow_locked = false, bool return_flag = false); + static bool uintxAt(const char* name, uintx* value, bool allow_locked = false, bool return_flag = false) { return uintxAt(name, strlen(name), value, allow_locked, return_flag); } static bool uintxAtPut(const char* name, size_t len, uintx* value, Flag::Flags origin); static bool uintxAtPut(const char* name, uintx* value, Flag::Flags origin) { return uintxAtPut(name, strlen(name), value, origin); } - static bool uint64_tAt(const char* name, size_t len, uint64_t* value); - static bool uint64_tAt(const char* name, uint64_t* value) { return uint64_tAt(name, strlen(name), value); } + static bool uint64_tAt(const char* name, size_t len, uint64_t* value, bool allow_locked = false, bool return_flag = false); + static bool uint64_tAt(const char* name, uint64_t* value, bool allow_locked = false, bool return_flag = false) { return uint64_tAt(name, strlen(name), value, allow_locked, return_flag); } static bool uint64_tAtPut(const char* name, size_t len, uint64_t* value, Flag::Flags origin); static bool uint64_tAtPut(const char* name, uint64_t* value, Flag::Flags origin) { return uint64_tAtPut(name, strlen(name), value, origin); } - static bool doubleAt(const char* name, size_t len, double* value); - static bool doubleAt(const char* name, double* value) { return doubleAt(name, strlen(name), value); } + static bool doubleAt(const char* name, size_t len, double* value, bool allow_locked = false, bool return_flag = false); + static bool doubleAt(const char* name, double* value, bool allow_locked = false, bool return_flag = false) { return doubleAt(name, strlen(name), value, allow_locked, return_flag); } static bool doubleAtPut(const char* name, size_t len, double* value, Flag::Flags origin); static bool doubleAtPut(const char* name, double* value, Flag::Flags origin) { return doubleAtPut(name, strlen(name), value, origin); } - static bool ccstrAt(const char* name, size_t len, ccstr* value); - static bool ccstrAt(const char* name, ccstr* value) { return ccstrAt(name, strlen(name), value); } + static bool ccstrAt(const char* name, size_t len, ccstr* value, bool allow_locked = false, bool return_flag = false); + static bool ccstrAt(const char* name, ccstr* value, bool allow_locked = false, bool return_flag = false) { return ccstrAt(name, strlen(name), value, allow_locked, return_flag); } // Contract: Flag will make private copy of the incoming value. // Outgoing value is always malloc-ed, and caller MUST call free. static bool ccstrAtPut(const char* name, size_t len, ccstr* value, Flag::Flags origin); --- old/src/share/vm/runtime/thread.hpp 2019-08-16 11:54:05.792031501 +0800 +++ new/src/share/vm/runtime/thread.hpp 2019-08-16 11:54:05.726029202 +0800 @@ -196,7 +196,9 @@ _deopt_suspend = 0x10000000U, // thread needs to self suspend for deopt _has_async_exception = 0x00000001U, // there is a pending async exception - _critical_native_unlock = 0x00000002U // Must call back to unlock JNI critical lock + _critical_native_unlock = 0x00000002U, // Must call back to unlock JNI critical lock + + JFR_ONLY(_trace_flag = 0x00000004U) // call jfr tracing }; // various suspension related flags - atomically updated @@ -443,6 +445,7 @@ inline jlong cooked_allocated_bytes(); JFR_ONLY(DEFINE_THREAD_LOCAL_ACCESSOR_JFR;) + JFR_ONLY(DEFINE_TRACE_SUSPEND_FLAG_METHODS) const ThreadExt& ext() const { return _ext; } ThreadExt& ext() { return _ext; } --- /dev/null 2019-03-13 11:16:31.764306451 +0800 +++ new/src/share/vm/gc_implementation/g1/g1HeapRegionTraceType.hpp 2019-08-16 11:54:06.037040039 +0800 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2016, 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 + * 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. + * + */ + +#ifndef SHARE_GC_G1_G1HEAPREGIONTRACETYPE_HPP +#define SHARE_GC_G1_G1HEAPREGIONTRACETYPE_HPP + +#include "memory/allocation.hpp" +#include "utilities/debug.hpp" + +class G1HeapRegionTraceType : AllStatic { + public: + enum Type { + Free, + Eden, + Survivor, + StartsHumongous, + ContinuesHumongous, + Old, + G1HeapRegionTypeEndSentinel + }; + + static const char* to_string(G1HeapRegionTraceType::Type type) { + switch (type) { + case Free: return "Free"; + case Eden: return "Eden"; + case Survivor: return "Survivor"; + case StartsHumongous: return "Starts Humongous"; + case ContinuesHumongous: return "Continues Humongous"; + case Old: return "Old"; + default: ShouldNotReachHere(); return NULL; + } + } +}; + +#endif // SHARE_GC_G1_G1HEAPREGIONTRACETYPE_HPP --- /dev/null 2019-03-13 11:16:31.764306451 +0800 +++ new/src/share/vm/gc_implementation/g1/heapRegionTracer.cpp 2019-08-16 11:54:06.316049762 +0800 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2013, 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. + * + */ + +#include "precompiled.hpp" +#include "gc_implementation/g1/heapRegionTracer.hpp" +#include "jfr/jfrEvents.hpp" + +void HeapRegionTracer::send_region_type_change(uint index, + G1HeapRegionTraceType::Type from, + G1HeapRegionTraceType::Type to, + uintptr_t start, + size_t used) { + EventG1HeapRegionTypeChange e; + if (e.should_commit()) { + e.set_index(index); + e.set_from(from); + e.set_to(to); + e.set_start(start); + e.set_used(used); + e.commit(); + } +} --- /dev/null 2019-03-13 11:16:31.764306451 +0800 +++ new/src/share/vm/gc_implementation/g1/heapRegionTracer.hpp 2019-08-16 11:54:06.588059240 +0800 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016, 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 + * 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. + * + */ + +#ifndef SHARE_GC_G1_HEAPREGIONTRACER_HPP +#define SHARE_GC_G1_HEAPREGIONTRACER_HPP + +#include "gc_implementation/g1/g1HeapRegionTraceType.hpp" +#include "memory/allocation.hpp" + +class HeapRegionTracer : AllStatic { + public: + static void send_region_type_change(uint index, + G1HeapRegionTraceType::Type from, + G1HeapRegionTraceType::Type to, + uintptr_t start, + size_t used); +}; + +#endif // SHARE_GC_G1_HEAPREGIONTRACER_HPP