--- old/make/linux/makefiles/trace.make 2019-01-28 17:43:03.000000000 +0800 +++ new/make/linux/makefiles/trace.make 2019-01-28 17:43:03.000000000 +0800 @@ -1,5 +1,5 @@ # -# Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2003, 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 @@ -50,8 +50,10 @@ TraceGeneratedNames = \ traceEventClasses.hpp \ - traceEventIds.hpp \ - traceTypes.hpp + traceEventIds.hpp \ + traceTypes.hpp \ + traceEventControl.hpp \ + tracePeriodic.hpp ifeq ($(HAS_ALT_SRC), true) TraceGeneratedNames += \ @@ -69,6 +71,8 @@ XSLT = $(REMOTE) $(RUN.JAVA) -classpath $(JvmtiOutDir) jvmtiGen XML_DEPS = $(TraceSrcDir)/trace.xml $(TraceSrcDir)/tracetypes.xml \ + $(TraceSrcDir)/tracerelationdecls.xml \ + $(TraceSrcDir)/traceevents.xml \ $(TraceSrcDir)/trace.dtd $(TraceSrcDir)/xinclude.mod ifeq ($(HAS_ALT_SRC), true) XML_DEPS += $(TraceAltSrcDir)/traceevents.xml @@ -96,6 +100,12 @@ $(TraceOutDir)/traceEventClasses.hpp: $(TraceSrcDir)/trace.xml $(TraceSrcDir)/traceEventClasses.xsl $(XML_DEPS) $(GENERATE_CODE) +$(TraceOutDir)/traceEventControl.hpp: $(TraceSrcDir)/trace.xml $(TraceSrcDir)/traceEventControl.xsl $(XML_DEPS) + $(GENERATE_CODE) + +$(TraceOutDir)/tracePeriodic.hpp: $(TraceSrcDir)/trace.xml $(TraceSrcDir)/tracePeriodic.xsl $(XML_DEPS) + $(GENERATE_CODE) + else $(TraceOutDir)/traceEventClasses.hpp: $(TraceSrcDir)/trace.xml $(TraceAltSrcDir)/traceEventClasses.xsl $(XML_DEPS) --- old/src/os/linux/vm/os_linux.cpp 2019-01-28 17:43:04.000000000 +0800 +++ new/src/os/linux/vm/os_linux.cpp 2019-01-28 17:43:04.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -2136,9 +2136,44 @@ jio_snprintf(fname, sizeof(fname), "/proc/%d/maps", pid); - if (!_print_ascii_file(fname, st)) { - st->print("Can not get library information for pid = %d\n", pid); - } + if (!_print_ascii_file(fname, st)) { + st->print("Can not get library information for pid = %d\n", pid); + } +} + +int os::get_loaded_modules_info(os::LoadedModulesCallbackFunc callback, void *param) { + FILE *procmapsFile = NULL; + + // Open the procfs maps file for the current process + if ((procmapsFile = fopen("/proc/self/maps", "r")) != NULL) { + // Allocate PATH_MAX for file name plus a reasonable size for other fields. + char line[PATH_MAX + 100]; + + // Read line by line from 'file' + while (fgets(line, sizeof(line), procmapsFile) != NULL) { + u8 base, top, offset, inode; + char permissions[5]; + char device[6]; + char name[PATH_MAX + 1]; + + // Parse fields from line + sscanf(line, UINT64_FORMAT_X "-" UINT64_FORMAT_X " %4s " UINT64_FORMAT_X " %5s " INT64_FORMAT " %s", + &base, &top, permissions, &offset, device, &inode, name); + + // Filter by device id '00:00' so that we only get file system mapped files. + if (strcmp(device, "00:00") != 0) { + + // Call callback with the fields of interest + if(callback(name, (address)base, (address)top, param)) { + // Oops abort, callback aborted + fclose(procmapsFile); + return 1; + } + } + } + fclose(procmapsFile); + } + return 0; } void os::print_os_info_brief(outputStream* st) { @@ -4025,6 +4060,10 @@ return ::read(fd, buf, nBytes); } +size_t os::read_at(int fd, void *buf, unsigned int nBytes, jlong offset) { + return ::pread(fd, buf, nBytes, offset); +} + // TODO-FIXME: reconcile Solaris' os::sleep with the linux variation. // Solaris uses poll(), linux uses park(). // Poll() is likely a better choice, assuming that Thread.interrupt() --- old/src/os/posix/vm/os_posix.cpp 2019-01-28 17:43:06.000000000 +0800 +++ new/src/os/posix/vm/os_posix.cpp 2019-01-28 17:43:05.000000000 +0800 @@ -1,5 +1,5 @@ /* -* Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. +* Copyright (c) 1999, 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 @@ -829,6 +829,68 @@ } } +Thread* os::ThreadCrashProtection::_protected_thread = NULL; +os::ThreadCrashProtection* os::ThreadCrashProtection::_crash_protection = NULL; +volatile intptr_t os::ThreadCrashProtection::_crash_mux = 0; + +os::ThreadCrashProtection::ThreadCrashProtection() { +} + +/* + * See the caveats for this class in os_posix.hpp + * Protects the callback call so that SIGSEGV / SIGBUS jumps back into this + * method and returns false. If none of the signals are raised, returns true. + * The callback is supposed to provide the method that should be protected. + */ +bool os::ThreadCrashProtection::call(os::CrashProtectionCallback& cb) { + sigset_t saved_sig_mask; + + Thread::muxAcquire(&_crash_mux, "CrashProtection"); + + _protected_thread = ThreadLocalStorage::thread(); + assert(_protected_thread != NULL, "Cannot crash protect a NULL thread"); + + // we cannot rely on sigsetjmp/siglongjmp to save/restore the signal mask + // since on at least some systems (OS X) siglongjmp will restore the mask + // for the process, not the thread + pthread_sigmask(0, NULL, &saved_sig_mask); + if (sigsetjmp(_jmpbuf, 0) == 0) { + // make sure we can see in the signal handler that we have crash protection + // installed + _crash_protection = this; + cb.call(); + // and clear the crash protection + _crash_protection = NULL; + _protected_thread = NULL; + Thread::muxRelease(&_crash_mux); + return true; + } + // this happens when we siglongjmp() back + pthread_sigmask(SIG_SETMASK, &saved_sig_mask, NULL); + _crash_protection = NULL; + _protected_thread = NULL; + Thread::muxRelease(&_crash_mux); + return false; +} + +void os::ThreadCrashProtection::restore() { + assert(_crash_protection != NULL, "must have crash protection"); + siglongjmp(_jmpbuf, 1); +} + +void os::ThreadCrashProtection::check_crash_protection(int sig, + Thread* thread) { + + if (thread != NULL && + thread == _protected_thread && + _crash_protection != NULL) { + + if (sig == SIGSEGV || sig == SIGBUS) { + _crash_protection->restore(); + } + } +} + os::WatcherThreadCrashProtection::WatcherThreadCrashProtection() { assert(Thread::current()->is_Watcher_thread(), "Must be WatcherThread"); } --- old/src/os/posix/vm/os_posix.hpp 2019-01-28 17:43:07.000000000 +0800 +++ new/src/os/posix/vm/os_posix.hpp 2019-01-28 17:43:06.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -62,6 +62,33 @@ }; /* + * Crash protection utility used by JFR. Wrap the callback + * with a sigsetjmp and in case of a SIGSEGV/SIGBUS we siglongjmp + * back. + * To be able to use this - don't take locks, don't rely on destructors, + * don't make OS library calls, don't allocate memory, don't print, + * don't call code that could leave the heap / memory in an inconsistent state, + * or anything else where we are not in control if we suddenly jump out. + */ +class ThreadCrashProtection : public StackObj { +public: + static bool is_crash_protected(Thread* thr) { + return _crash_protection != NULL && _protected_thread == thr; + } + + ThreadCrashProtection(); + bool call(os::CrashProtectionCallback& cb); + + static void check_crash_protection(int signal, Thread* thread); +private: + static Thread* _protected_thread; + static ThreadCrashProtection* _crash_protection; + static volatile intptr_t _crash_mux; + void restore(); + sigjmp_buf _jmpbuf; +}; + +/* * Crash protection for the watcher thread. Wrap the callback * with a sigsetjmp and in case of a SIGSEGV/SIGBUS we siglongjmp * back. --- old/src/os_cpu/linux_x86/vm/atomic_linux_x86.inline.hpp 2019-01-28 17:43:08.000000000 +0800 +++ new/src/os_cpu/linux_x86/vm/atomic_linux_x86.inline.hpp 2019-01-28 17:43:08.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -102,6 +102,10 @@ #ifdef AMD64 inline void Atomic::store (jlong store_value, jlong* dest) { *dest = store_value; } inline void Atomic::store (jlong store_value, volatile jlong* dest) { *dest = store_value; } +inline void Atomic::store (julong store_value, julong* dest) { + assert(EnableJFR, "sanity check"); + *dest = store_value; +} inline intptr_t Atomic::add_ptr(intptr_t add_value, volatile intptr_t* dest) { intptr_t addend = add_value; --- old/src/os_cpu/linux_x86/vm/orderAccess_linux_x86.inline.hpp 2019-01-28 17:43:09.000000000 +0800 +++ new/src/os_cpu/linux_x86/vm/orderAccess_linux_x86.inline.hpp 2019-01-28 17:43:09.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -73,8 +73,21 @@ inline julong OrderAccess::load_acquire(volatile julong* p) { return Atomic::load((volatile jlong*)p); } inline jfloat OrderAccess::load_acquire(volatile jfloat* p) { return *p; } inline jdouble OrderAccess::load_acquire(volatile jdouble* p) { return jdouble_cast(Atomic::load((volatile jlong*)p)); } +inline bool OrderAccess::load_acquire(const volatile bool* p) { + assert(EnableJFR, "sanity check"); + return *p; +} +inline julong OrderAccess::load_acquire(const volatile julong* p) { + assert(EnableJFR, "sanity check"); + return Atomic::load((volatile jlong*)p); +} inline intptr_t OrderAccess::load_ptr_acquire(volatile intptr_t* p) { return *p; } +inline uintptr_t OrderAccess::load_ptr_acquire(const volatile uintptr_t* p) { + assert(EnableJFR, "sanity check"); + return *p; +} + inline void* OrderAccess::load_ptr_acquire(volatile void* p) { return *(void* volatile *)p; } inline void* OrderAccess::load_ptr_acquire(const volatile void* p) { return *(void* const volatile *)p; } --- old/src/os_cpu/linux_x86/vm/os_linux_x86.cpp 2019-01-28 17:43:10.000000000 +0800 +++ new/src/os_cpu/linux_x86/vm/os_linux_x86.cpp 2019-01-28 17:43:10.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -224,6 +224,8 @@ // (no destructors can be run) os::WatcherThreadCrashProtection::check_crash_protection(sig, t); + os::ThreadCrashProtection::check_crash_protection(sig, t); + SignalHandlerMark shm(t); // Note: it's not uncommon that JNI code uses signal/sigset to install --- old/src/share/vm/c1/c1_GraphBuilder.cpp 2019-01-28 17:43:11.000000000 +0800 +++ new/src/share/vm/c1/c1_GraphBuilder.cpp 2019-01-28 17:43:11.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -3461,10 +3461,11 @@ break; #ifdef TRACE_HAVE_INTRINSICS - case vmIntrinsics::_classID: - case vmIntrinsics::_threadID: - preserves_state = true; - cantrap = true; + case vmIntrinsics::_getClassId: + cantrap = false; + break; + + case vmIntrinsics::_getEventWriter: break; case vmIntrinsics::_counterTime: @@ -4411,6 +4412,18 @@ log->inline_fail("reason unknown"); } } +#if INCLUDE_TRACE + EventCompilerInlining event; + if (event.should_commit()) { + event.set_compileId(compilation()->env()->task()->compile_id()); + event.set_message(msg); + event.set_succeeded(success); + event.set_bci(bci()); + event.set_caller(method()->get_Method()); + event.set_callee(callee->to_trace_struct()); + event.commit(); + } +#endif // INCLUDE_TRACE if (!PrintInlining && !compilation()->method()->has_option("PrintInlining")) { return; --- old/src/share/vm/c1/c1_LIRGenerator.cpp 2019-01-28 17:43:12.000000000 +0800 +++ new/src/share/vm/c1/c1_LIRGenerator.cpp 2019-01-28 17:43:12.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -40,6 +40,9 @@ #if INCLUDE_ALL_GCS #include "gc_implementation/g1/heapRegion.hpp" #endif // INCLUDE_ALL_GCS +#ifdef TRACE_HAVE_INTRINSICS +#include "trace/traceMacros.hpp" +#endif #ifdef ASSERT #define __ gen()->lir(__FILE__, __LINE__)-> @@ -3065,39 +3068,47 @@ } #ifdef TRACE_HAVE_INTRINSICS -void LIRGenerator::do_ThreadIDIntrinsic(Intrinsic* x) { - LIR_Opr thread = getThreadPointer(); - LIR_Opr osthread = new_pointer_register(); - __ move(new LIR_Address(thread, in_bytes(JavaThread::osthread_offset()), osthread->type()), osthread); - size_t thread_id_size = OSThread::thread_id_size(); - if (thread_id_size == (size_t) BytesPerLong) { - LIR_Opr id = new_register(T_LONG); - __ move(new LIR_Address(osthread, in_bytes(OSThread::thread_id_offset()), T_LONG), id); - __ convert(Bytecodes::_l2i, id, rlock_result(x)); - } else if (thread_id_size == (size_t) BytesPerInt) { - __ move(new LIR_Address(osthread, in_bytes(OSThread::thread_id_offset()), T_INT), rlock_result(x)); - } else { - ShouldNotReachHere(); - } +void LIRGenerator::do_ClassIDIntrinsic(Intrinsic* x) { + CodeEmitInfo* info = state_for(x); + CodeEmitInfo* info2 = new CodeEmitInfo(info); // Clone for the second null check + + assert(info != NULL, "must have info"); + LIRItem arg(x->argument_at(0), this); + + arg.load_item(); + LIR_Opr klass = new_register(T_METADATA); + __ move(new LIR_Address(arg.result(), java_lang_Class::klass_offset_in_bytes(), T_ADDRESS), klass, info); + LIR_Opr id = new_register(T_LONG); + ByteSize offset = TRACE_KLASS_TRACE_ID_OFFSET; + LIR_Address* trace_id_addr = new LIR_Address(klass, in_bytes(offset), T_LONG); + + __ move(trace_id_addr, id); + __ logical_or(id, LIR_OprFact::longConst(0x01l), id); + __ store(id, trace_id_addr); + +#ifdef TRACE_ID_META_BITS + __ logical_and(id, LIR_OprFact::longConst(~TRACE_ID_META_BITS), id); +#endif +#ifdef TRACE_ID_CLASS_SHIFT + __ unsigned_shift_right(id, TRACE_ID_CLASS_SHIFT, id); +#endif + + __ move(id, rlock_result(x)); } -void LIRGenerator::do_ClassIDIntrinsic(Intrinsic* x) { - CodeEmitInfo* info = state_for(x); - CodeEmitInfo* info2 = new CodeEmitInfo(info); // Clone for the second null check - BasicType klass_pointer_type = NOT_LP64(T_INT) LP64_ONLY(T_LONG); - assert(info != NULL, "must have info"); - LIRItem arg(x->argument_at(1), this); - arg.load_item(); - LIR_Opr klass = new_pointer_register(); - __ move(new LIR_Address(arg.result(), java_lang_Class::klass_offset_in_bytes(), klass_pointer_type), klass, info); - LIR_Opr id = new_register(T_LONG); - ByteSize offset = TRACE_ID_OFFSET; - LIR_Address* trace_id_addr = new LIR_Address(klass, in_bytes(offset), T_LONG); - __ move(trace_id_addr, id); - __ logical_or(id, LIR_OprFact::longConst(0x01l), id); - __ store(id, trace_id_addr); - __ logical_and(id, LIR_OprFact::longConst(~0x3l), id); - __ move(id, rlock_result(x)); +void LIRGenerator::do_getEventWriter(Intrinsic* x) { + LabelObj* L_end = new LabelObj(); + + LIR_Address* jobj_addr = new LIR_Address(getThreadPointer(), + in_bytes(TRACE_THREAD_DATA_WRITER_OFFSET), + T_OBJECT); + LIR_Opr result = rlock_result(x); + __ move_wide(jobj_addr, result); + __ cmp(lir_cond_equal, result, LIR_OprFact::oopConst(NULL)); + __ branch(lir_cond_equal, T_OBJECT, L_end->label()); + __ move_wide(new LIR_Address(result, T_OBJECT), result); + + __ branch_destination(L_end->label()); } #endif @@ -3112,8 +3123,16 @@ } #ifdef TRACE_HAVE_INTRINSICS - case vmIntrinsics::_threadID: do_ThreadIDIntrinsic(x); break; - case vmIntrinsics::_classID: do_ClassIDIntrinsic(x); break; + case vmIntrinsics::_getClassId: + if (EnableJFR) { + do_ClassIDIntrinsic(x); + } + break; + case vmIntrinsics::_getEventWriter: + if (EnableJFR) { + do_getEventWriter(x); + } + break; case vmIntrinsics::_counterTime: do_RuntimeCall(CAST_FROM_FN_PTR(address, TRACE_TIME_METHOD), 0, x); break; --- old/src/share/vm/c1/c1_LIRGenerator.hpp 2019-01-28 17:43:13.000000000 +0800 +++ new/src/share/vm/c1/c1_LIRGenerator.hpp 2019-01-28 17:43:13.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -437,8 +437,8 @@ void do_RuntimeCall(address routine, int expected_arguments, Intrinsic* x); #ifdef TRACE_HAVE_INTRINSICS - void do_ThreadIDIntrinsic(Intrinsic* x); void do_ClassIDIntrinsic(Intrinsic* x); + void do_getEventWriter(Intrinsic* x); #endif ciKlass* profile_type(ciMethodData* md, int md_first_offset, int md_offset, intptr_t profiled_k, Value arg, LIR_Opr& mdp, bool not_null, ciKlass* signature_at_call_k, --- old/src/share/vm/ci/ciMethod.cpp 2019-01-28 17:43:14.000000000 +0800 +++ new/src/share/vm/ci/ciMethod.cpp 2019-01-28 17:43:14.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -1467,3 +1467,13 @@ st->print(" loaded=false"); } } + +#if INCLUDE_TRACE +TraceStructCalleeMethod ciMethod::to_trace_struct() const { + TraceStructCalleeMethod result; + result.set_type(holder()->name()->as_utf8()); + result.set_name(name()->as_utf8()); + result.set_descriptor(signature()->as_symbol()->as_utf8()); + return result; +} +#endif --- old/src/share/vm/ci/ciMethod.hpp 2019-01-28 17:43:15.000000000 +0800 +++ new/src/share/vm/ci/ciMethod.hpp 2019-01-28 17:43:15.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -32,6 +32,7 @@ #include "compiler/methodLiveness.hpp" #include "prims/methodHandles.hpp" #include "utilities/bitMap.hpp" +#include "trace/tracing.hpp" class ciMethodBlocks; class MethodLiveness; @@ -93,12 +94,6 @@ ciMethod(methodHandle h_m, ciInstanceKlass* holder); ciMethod(ciInstanceKlass* holder, ciSymbol* name, ciSymbol* signature, ciInstanceKlass* accessor); - Method* get_Method() const { - Method* m = (Method*)_metadata; - assert(m != NULL, "illegal use of unloaded method"); - return m; - } - oop loader() const { return _holder->loader(); } const char* type_string() { return "ciMethod"; } @@ -156,6 +151,11 @@ } } + Method* get_Method() const { + Method* m = (Method*)_metadata; + assert(m != NULL, "illegal use of unloaded method"); + return m; + } // Method code and related information. address code() { if (_code == NULL) load_code(); return _code; } @@ -347,6 +347,10 @@ // Print the name of this method in various incarnations. void print_name(outputStream* st = tty); void print_short_name(outputStream* st = tty); + +#if INCLUDE_TRACE + TraceStructCalleeMethod to_trace_struct() const; +#endif }; #endif // SHARE_VM_CI_CIMETHOD_HPP --- old/src/share/vm/classfile/classFileParser.cpp 2019-01-28 17:43:16.000000000 +0800 +++ new/src/share/vm/classfile/classFileParser.cpp 2019-01-28 17:43:16.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -61,6 +61,7 @@ #include "runtime/timer.hpp" #include "services/classLoadingService.hpp" #include "services/threadService.hpp" +#include "trace/traceMacros.hpp" #include "utilities/array.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/ostream.hpp" @@ -3885,6 +3886,7 @@ // This class and superclass u2 this_class_index = cfs->get_u2_fast(); + _this_class_index = this_class_index; //used by jfr check_property( valid_cp_range(this_class_index, cp_size) && cp->tag_at(this_class_index).is_unresolved_klass(), @@ -4215,6 +4217,7 @@ ClassLoadingService::notify_class_loaded(InstanceKlass::cast(this_klass()), false /* not shared class */); + TRACE_INIT_ID(InstanceKlass::cast(this_klass())); if (TraceClassLoading) { ResourceMark rm; @@ -5300,3 +5303,19 @@ } return NULL; } + +const ClassFileStream* ClassFileParser::clone_stream() const { + assert(_stream != NULL, "invariant"); + return _stream->clone(); +} + +void ClassFileParser::set_klass_to_deallocate(InstanceKlass* klass) { + +#ifdef ASSERT + if (klass != NULL) { + assert(NULL == _klass, "leaking?"); + } +#endif + + _klass = klass; +} --- old/src/share/vm/classfile/classFileParser.hpp 2019-01-28 17:43:18.000000000 +0800 +++ new/src/share/vm/classfile/classFileParser.hpp 2019-01-28 17:43:17.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -47,6 +47,7 @@ bool _relax_verify; u2 _major_version; u2 _minor_version; + u2 _this_class_index; Symbol* _class_name; ClassLoaderData* _loader_data; KlassHandle _host_klass; @@ -90,6 +91,7 @@ void create_combined_annotations(TRAPS); void init_parsed_class_attributes(ClassLoaderData* loader_data) { + _this_class_index = 0; _loader_data = loader_data; _synthetic_flag = false; _sourcefile_index = 0; @@ -486,6 +488,10 @@ bool verify, TRAPS); + u2 this_class_index() const { return _this_class_index; } + const ClassFileStream* clone_stream() const; + void set_klass_to_deallocate(InstanceKlass* klass); + // Verifier checks static void check_super_class_access(instanceKlassHandle this_klass, TRAPS); static void check_super_interface_access(instanceKlassHandle this_klass, TRAPS); --- old/src/share/vm/classfile/classFileStream.cpp 2019-01-28 17:43:19.000000000 +0800 +++ new/src/share/vm/classfile/classFileStream.cpp 2019-01-28 17:43:18.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -38,6 +38,33 @@ _need_verify = false; } +const u1* ClassFileStream::clone_buffer() const { + u1* const new_buffer_start = NEW_RESOURCE_ARRAY(u1, length()); + memcpy(new_buffer_start, _buffer_start, length()); + return new_buffer_start; +} + +const char* const ClassFileStream::clone_source() const { + const char* const src = source(); + char* source_copy = NULL; + if (src != NULL) { + size_t source_len = strlen(src); + source_copy = NEW_RESOURCE_ARRAY(char, source_len + 1); + strncpy(source_copy, src, source_len + 1); + } + return source_copy; +} + +// Caller responsible for ResourceMark +// clone stream with a rewound position +const ClassFileStream* ClassFileStream::clone() const { + const u1* const new_buffer_start = clone_buffer(); + return new ClassFileStream(const_cast(new_buffer_start), + length(), + clone_source()/*, + need_verify()*/); +} + u1 ClassFileStream::get_u1(TRAPS) { if (_need_verify) { guarantee_more(1, CHECK_0); --- old/src/share/vm/classfile/classFileStream.hpp 2019-01-28 17:43:20.000000000 +0800 +++ new/src/share/vm/classfile/classFileStream.hpp 2019-01-28 17:43:19.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -57,15 +57,24 @@ bool _need_verify; // True if verification is on for the class file void truncated_file_error(TRAPS); + protected: + const u1* clone_buffer() const; + const char* const clone_source() const; public: // Constructor ClassFileStream(u1* buffer, int length, const char* source); + virtual const ClassFileStream* clone() const; + // Buffer access u1* buffer() const { return _buffer_start; } int length() const { return _buffer_end - _buffer_start; } u1* current() const { return _current; } void set_current(u1* pos) { _current = pos; } + // for relative positioning + juint current_offset() const { + return (juint)(_current - _buffer_start); + } const char* source() const { return _source; } void set_verify(bool flag) { _need_verify = flag; } --- old/src/share/vm/classfile/classLoader.cpp 2019-01-28 17:43:21.000000000 +0800 +++ new/src/share/vm/classfile/classLoader.cpp 2019-01-28 17:43:21.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -1127,6 +1127,8 @@ parsed_name, context.should_verify(classpath_index), THREAD); + + TRACE_KLASS_CREATION(result, parser, THREAD); if (HAS_PENDING_EXCEPTION) { ResourceMark rm; if (DumpSharedSpaces) { --- old/src/share/vm/classfile/classLoaderData.cpp 2019-01-28 17:43:22.000000000 +0800 +++ new/src/share/vm/classfile/classLoaderData.cpp 2019-01-28 17:43:22.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -82,6 +82,7 @@ _next(NULL), _dependencies(dependencies), _metaspace_lock(new Mutex(Monitor::leaf+1, "Metaspace allocation lock", true)) { // empty + TRACE_INIT_ID(this); } void ClassLoaderData::init_dependencies(TRAPS) { @@ -646,6 +647,20 @@ } } +void ClassLoaderDataGraph::cld_unloading_do(CLDClosure* cl) { + // this method is only used by jfr now, if you need to use this method in another case, + // this check should be removed. + assert(EnableJFR && FlightRecorder, "just check"); + + assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint!"); + // Only walk the head until any clds not purged from prior unloading + // (CMS doesn't purge right away). + for (ClassLoaderData* cld = _unloading; cld != _saved_unloading; cld = cld->next()) { + assert(cld->is_unloading(), "invariant"); + cl->do_cld(cld); + } +} + void ClassLoaderDataGraph::roots_cld_do(CLDClosure* strong, CLDClosure* weak) { for (ClassLoaderData* cld = _head; cld != NULL; cld = cld->_next) { CLDClosure* closure = cld->keep_alive() ? strong : weak; @@ -980,9 +995,7 @@ EventClassUnload event(UNTIMED); event.set_endtime(_class_unload_time); event.set_unloadedClass(k); - oop defining_class_loader = k->class_loader(); - event.set_definingClassLoader(defining_class_loader != NULL ? - defining_class_loader->klass() : (Klass*)NULL); + event.set_definingClassLoader(k->class_loader_data()); event.commit(); } --- old/src/share/vm/classfile/classLoaderData.hpp 2019-01-28 17:43:23.000000000 +0800 +++ new/src/share/vm/classfile/classLoaderData.hpp 2019-01-28 17:43:23.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,6 +35,7 @@ #if INCLUDE_TRACE #include "utilities/ticks.hpp" #endif +#include "jfr/utilities/jfrLog.hpp" // // A class loader represents a linkset. Conceptually, a linkset identifies @@ -82,6 +83,7 @@ static void always_strong_oops_do(OopClosure* blk, KlassClosure* klass_closure, bool must_claim); // cld do static void cld_do(CLDClosure* cl); + static void cld_unloading_do(CLDClosure* cl); static void roots_cld_do(CLDClosure* strong, CLDClosure* weak); static void keep_alive_cld_do(CLDClosure* cl); static void always_strong_cld_do(CLDClosure* cl); @@ -128,6 +130,8 @@ class ClassLoaderData : public CHeapObj { friend class VMStructs; + friend class CLDClaimContext; + friend class CLDClaimStateClosure; private: class Dependencies VALUE_OBJ_CLASS_SPEC { objArrayOop _list_head; @@ -213,6 +217,8 @@ static Metaspace* _ro_metaspace; static Metaspace* _rw_metaspace; + TRACE_DEFINE_TRACE_ID_FIELD; + void set_next(ClassLoaderData* next) { _next = next; } ClassLoaderData* next() const { return _next; } @@ -325,6 +331,8 @@ Metaspace* ro_metaspace(); Metaspace* rw_metaspace(); void initialize_shared_metaspaces(); + + TRACE_DEFINE_TRACE_ID_METHODS; }; // An iterator that distributes Klasses to parallel worker threads. --- old/src/share/vm/classfile/javaClasses.cpp 2019-01-28 17:43:24.000000000 +0800 +++ new/src/share/vm/classfile/javaClasses.cpp 2019-01-28 17:43:24.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -1047,7 +1047,7 @@ // Read thread status value from threadStatus field in java.lang.Thread java class. java_lang_Thread::ThreadStatus java_lang_Thread::get_thread_status(oop java_thread) { - assert(Thread::current()->is_Watcher_thread() || Thread::current()->is_VM_thread() || + assert((EnableJFR && Threads_lock->owned_by_self()) || Thread::current()->is_Watcher_thread() || Thread::current()->is_VM_thread() || JavaThread::current()->thread_state() == _thread_in_vm, "Java Thread is not running in vm"); // The threadStatus is only present starting in 1.5 --- old/src/share/vm/classfile/systemDictionary.cpp 2019-01-28 17:43:25.000000000 +0800 +++ new/src/share/vm/classfile/systemDictionary.cpp 2019-01-28 17:43:25.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -994,15 +994,16 @@ // // Note: "name" is updated. - instanceKlassHandle k = ClassFileParser(st).parseClassFile(class_name, - loader_data, - protection_domain, - host_klass, - cp_patches, - parsed_name, - true, - THREAD); - + ClassFileParser parser(st); + instanceKlassHandle k = parser.parseClassFile(class_name, + loader_data, + protection_domain, + host_klass, + cp_patches, + parsed_name, + true, + THREAD); + TRACE_KLASS_CREATION(k, parser, THREAD); if (host_klass.not_null() && k.not_null()) { assert(EnableInvokeDynamic, ""); @@ -1076,12 +1077,14 @@ // // Note: "name" is updated. - instanceKlassHandle k = ClassFileParser(st).parseClassFile(class_name, - loader_data, - protection_domain, - parsed_name, - verify, - THREAD); + ClassFileParser parser(st); + instanceKlassHandle k = parser.parseClassFile(class_name, + loader_data, + protection_domain, + parsed_name, + verify, + THREAD); + TRACE_KLASS_CREATION(k, parser, THREAD); const char* pkg = "java/"; size_t pkglen = strlen(pkg); @@ -1376,6 +1379,19 @@ } } +static void class_define_event(InstanceKlass* k, + const ClassLoaderData* def_cld) { +#if INCLUDE_TRACE + EventClassDefine event; + if (event.should_commit()) { + ResourceMark m; + event.set_definedClass(k); + event.set_definingClassLoader(def_cld); + event.commit(); + } +#endif // INCLUDE_TRACE +} + void SystemDictionary::define_instance_class(instanceKlassHandle k, TRAPS) { ClassLoaderData* loader_data = k->class_loader_data(); @@ -1446,6 +1462,7 @@ } + class_define_event(k(), loader_data); } // Support parallel classloading @@ -2683,16 +2700,11 @@ instanceKlassHandle k, Handle initiating_loader) { #if INCLUDE_TRACE - EventClassLoad event(UNTIMED); + EventClassLoad event; if (event.should_commit()) { - event.set_starttime(start_time); event.set_loadedClass(k()); - oop defining_class_loader = k->class_loader(); - event.set_definingClassLoader(defining_class_loader != NULL ? - defining_class_loader->klass() : (Klass*)NULL); - oop class_loader = initiating_loader.is_null() ? (oop)NULL : initiating_loader(); - event.set_initiatingClassLoader(class_loader != NULL ? - class_loader->klass() : (Klass*)NULL); + event.set_definingClassLoader(k->class_loader_data()); + event.set_initiatingClassLoader(ClassLoaderData::class_loader_data_or_null(initiating_loader())); event.commit(); } #endif // INCLUDE_TRACE --- old/src/share/vm/classfile/systemDictionary.hpp 2019-01-28 17:43:26.000000000 +0800 +++ new/src/share/vm/classfile/systemDictionary.hpp 2019-01-28 17:43:26.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -77,7 +77,6 @@ template class HashtableBucket; class ResolutionErrorTable; class SymbolPropertyTable; -class Ticks; // Certain classes are preloaded, such as java.lang.Object and java.lang.String. // They are all "well-known", in the sense that no class loader is allowed --- old/src/share/vm/code/codeBlob.cpp 2019-01-28 17:43:27.000000000 +0800 +++ new/src/share/vm/code/codeBlob.cpp 2019-01-28 17:43:27.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -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-01-28 17:43:28.000000000 +0800 +++ new/src/share/vm/code/codeBlob.hpp 2019-01-28 17:43:28.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -30,6 +30,15 @@ #include "runtime/frame.hpp" #include "runtime/handles.hpp" +// CodeBlob Types, used for jfr +// 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-01-28 17:43:29.000000000 +0800 +++ new/src/share/vm/code/codeCache.cpp 2019-01-28 17:43:29.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -188,6 +188,14 @@ if (cb != NULL) break; if (!_heap->expand_by(CodeCacheExpansionSize)) { // Expansion failed + if (EnableJFR) { + 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) { @@ -757,6 +765,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/compiler/compileBroker.cpp 2019-01-28 17:43:30.000000000 +0800 +++ new/src/share/vm/compiler/compileBroker.cpp 2019-01-28 17:43:30.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -2033,7 +2033,7 @@ assert(task->compile_id() != CICrashAt, "just as planned"); if (event.should_commit()) { event.set_method(target->get_Method()); - event.set_compileID(compile_id); + event.set_compileId(compile_id); event.set_compileLevel(task->comp_level()); event.set_succeded(task->is_success()); event.set_isOsr(is_osr); --- old/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp 2019-01-28 17:43:32.000000000 +0800 +++ new/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp 2019-01-28 17:43:31.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -75,7 +75,6 @@ class G1OldTracer; class EvacuationFailedInfo; class nmethod; -class Ticks; typedef OverflowTaskQueue RefToScanQueue; typedef GenericTaskQueueSet RefToScanQueueSet; @@ -1082,6 +1081,7 @@ ConcurrentGCTimer* gc_timer_cm() const { return _gc_timer_cm; } G1OldTracer* gc_tracer_cm() const { return _gc_tracer_cm; } + G1NewTracer* gc_tracer_stw() const { return _gc_tracer_stw; } virtual size_t capacity() const; virtual size_t used() const; --- old/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp 2019-01-28 17:43:33.000000000 +0800 +++ new/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp 2019-01-28 17:43:33.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -282,6 +282,9 @@ double max_gc_time = (double) MaxGCPauseMillis / 1000.0; double time_slice = (double) GCPauseIntervalMillis / 1000.0; _mmu_tracker = new G1MMUTrackerQueue(time_slice, max_gc_time); + if (EnableJFR) { + _ihop_control = create_ihop_control(); + } uintx confidence_perc = G1ConfidencePercent; // Put an artificial ceiling on this so that it's not set to a silly value. @@ -320,6 +323,13 @@ _collectionSetChooser = new CollectionSetChooser(); } +G1CollectorPolicy::~G1CollectorPolicy() { + if (EnableJFR) { + assert(_ihop_control != NULL, "sanity check"); + delete _ihop_control; + } +} + void G1CollectorPolicy::initialize_alignments() { _space_alignment = HeapRegion::GrainBytes; size_t card_table_alignment = GenRemSet::max_alignment_constraint(GenRemSet::CardTable); @@ -507,6 +517,10 @@ _reserve_regions = (uint) ceil(reserve_regions_d); _young_gen_sizer->heap_size_changed(new_number_of_regions); + + if (EnableJFR) { + _ihop_control->update_target_occupancy(new_number_of_regions * HeapRegion::GrainBytes); + } } uint G1CollectorPolicy::calculate_young_list_desired_min_length( @@ -1189,6 +1203,15 @@ phase_times()->sum_thread_work_items(G1GCPhaseTimes::UpdateRS), update_rs_time_goal_ms); _collectionSetChooser->verify(); + + if (EnableJFR) { + _ihop_control->send_trace_event(_g1->gc_tracer_stw()); + } +} + +G1IHOPControl* G1CollectorPolicy::create_ihop_control() { + assert(EnableJFR, "sanity check"); + return new G1StaticIHOPControl(InitiatingHeapOccupancyPercent); } #define EXT_SIZE_FORMAT "%.1f%s" --- old/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp 2019-01-28 17:43:34.000000000 +0800 +++ new/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp 2019-01-28 17:43:34.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -28,6 +28,7 @@ #include "gc_implementation/g1/collectionSetChooser.hpp" #include "gc_implementation/g1/g1Allocator.hpp" #include "gc_implementation/g1/g1MMUTracker.hpp" +#include "gc_implementation/g1/g1IHOPControl.hpp" #include "memory/collectorPolicy.hpp" // A G1CollectorPolicy makes policy decisions that determine the @@ -161,6 +162,8 @@ class G1CollectorPolicy: public CollectorPolicy { private: + static G1IHOPControl* create_ihop_control(); + // either equal to the number of parallel threads, if ParallelGCThreads // has been set, or 1 otherwise int _parallel_gc_threads; @@ -173,6 +176,7 @@ }; G1MMUTracker* _mmu_tracker; + G1IHOPControl* _ihop_control; void initialize_alignments(); void initialize_flags(); @@ -636,6 +640,7 @@ public: G1CollectorPolicy(); + virtual ~G1CollectorPolicy(); virtual G1CollectorPolicy* as_g1_policy() { return this; } --- old/src/share/vm/gc_implementation/g1/g1MMUTracker.cpp 2019-01-28 17:43:35.000000000 +0800 +++ new/src/share/vm/gc_implementation/g1/g1MMUTracker.cpp 2019-01-28 17:43:35.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -105,6 +105,11 @@ ++_no_entries; } _array[_head_index] = G1MMUTrackerQueueElem(start, end); + + if (EnableJFR) { + double slice_time = calculate_gc_time(end); + G1MMUTracer::report_mmu(_time_slice, slice_time, _max_gc_time, gc_thread); + } } // basically the _internal call does not remove expired entries --- old/src/share/vm/gc_implementation/g1/g1ParScanThreadState.cpp 2019-01-28 17:43:36.000000000 +0800 +++ new/src/share/vm/gc_implementation/g1/g1ParScanThreadState.cpp 2019-01-28 17:43:36.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -198,6 +198,21 @@ return dest(state); } +void G1ParScanThreadState::report_promotion_event(InCSetState const dest_state, + oop const old, size_t word_sz, uint age, + HeapWord * const obj_ptr, AllocationContext_t context) const { + assert(EnableJFR, "sanity check"); + ParGCAllocBuffer* alloc_buf = _g1_par_allocator->alloc_buffer(dest_state, context); + if (alloc_buf->contains(obj_ptr)) { + _g1h->_gc_tracer_stw->report_promotion_in_new_plab_event(old->klass(), word_sz, age, + dest_state.value() == InCSetState::Old, + alloc_buf->word_sz()); + } else { + _g1h->_gc_tracer_stw->report_promotion_outside_plab_event(old->klass(), word_sz, age, + dest_state.value() == InCSetState::Old); + } +} + oop G1ParScanThreadState::copy_to_survivor_space(InCSetState const state, oop const old, markOop const old_mark) { @@ -225,6 +240,10 @@ return _g1h->handle_evacuation_failure_par(this, old); } } + if (EnableJFR && _g1h->_gc_tracer_stw->should_report_promotion_events()) { + // The events are checked individually as part of the actual commit + report_promotion_event(dest_state, old, word_sz, age, obj_ptr, context); + } } assert(obj_ptr != NULL, "when we get here, allocation should have succeeded"); --- old/src/share/vm/gc_implementation/g1/g1ParScanThreadState.hpp 2019-01-28 17:43:37.000000000 +0800 +++ new/src/share/vm/gc_implementation/g1/g1ParScanThreadState.hpp 2019-01-28 17:43:37.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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 @@ -213,6 +213,10 @@ AllocationContext_t const context); inline InCSetState next_state(InCSetState const state, markOop const m, uint& age); + inline InCSetState next_state(InCSetState const state, markOop const m, uint& age, AllocationContext_t context); + void report_promotion_event(InCSetState const dest_state, + oop const old, size_t word_sz, uint age, + HeapWord * const obj_ptr, AllocationContext_t context) const; public: oop copy_to_survivor_space(InCSetState const state, oop const obj, markOop const old_mark); --- old/src/share/vm/gc_implementation/g1/heapRegion.cpp 2019-01-28 17:43:38.000000000 +0800 +++ new/src/share/vm/gc_implementation/g1/heapRegion.cpp 2019-01-28 17:43:38.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -31,6 +31,7 @@ #include "gc_implementation/g1/heapRegionBounds.inline.hpp" #include "gc_implementation/g1/heapRegionRemSet.hpp" #include "gc_implementation/g1/heapRegionManager.inline.hpp" +#include "gc_implementation/g1/heapRegionTracer.hpp" #include "gc_implementation/shared/liveRange.hpp" #include "memory/genOopClosures.inline.hpp" #include "memory/iterator.hpp" @@ -217,7 +218,9 @@ "Should be normal before the humongous object allocation"); assert(top() == bottom(), "should be empty"); assert(bottom() <= new_top && new_top <= new_end, "pre-condition"); - + if (EnableJFR) { + report_region_type_change(G1HeapRegionTraceType::StartsHumongous); + } _type.set_starts_humongous(); _humongous_start_region = this; @@ -231,7 +234,9 @@ "Should be normal before the humongous object allocation"); assert(top() == bottom(), "should be empty"); assert(first_hr->startsHumongous(), "pre-condition"); - + if (EnableJFR) { + report_region_type_change(G1HeapRegionTraceType::ContinuesHumongous); + } _type.set_continues_humongous(); _humongous_start_region = first_hr; } @@ -303,6 +308,16 @@ record_timestamp(); } +void HeapRegion::report_region_type_change(G1HeapRegionTraceType::Type to) { + assert(EnableJFR, "sanity check"); + 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-01-28 17:43:39.000000000 +0800 +++ new/src/share/vm/gc_implementation/g1/heapRegion.hpp 2019-01-28 17:43:39.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -30,6 +30,7 @@ #include "gc_implementation/g1/g1_specialized_oop_closures.hpp" #include "gc_implementation/g1/heapRegionType.hpp" #include "gc_implementation/g1/survRateGroup.hpp" +#include "gc_implementation/g1/g1HeapRegionTraceType.hpp" #include "gc_implementation/shared/ageTable.hpp" #include "gc_implementation/shared/spaceDecorator.hpp" #include "memory/space.inline.hpp" @@ -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,40 @@ } } - void set_free() { _type.set_free(); } + void set_free() { + if (EnableJFR) { + report_region_type_change(G1HeapRegionTraceType::Free); + } + _type.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() { + if (EnableJFR) { + report_region_type_change(G1HeapRegionTraceType::Eden); + } + _type.set_eden(); + } - void set_old() { _type.set_old(); } + void set_old() { + if (EnableJFR) { + report_region_type_change(G1HeapRegionTraceType::Old); + } + _type.set_old(); + } + + void set_eden_pre_gc() { + if (EnableJFR) { + report_region_type_change(G1HeapRegionTraceType::Eden); + } + _type.set_eden_pre_gc(); + } + + void set_survivor() { + if (EnableJFR) { + report_region_type_change(G1HeapRegionTraceType::Survivor); + } + _type.set_survivor(); + } // 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-01-28 17:43:40.000000000 +0800 +++ new/src/share/vm/gc_implementation/g1/heapRegionType.cpp 2019-01-28 17:43:40.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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 @@ -23,6 +23,7 @@ */ #include "precompiled.hpp" +#include "gc_implementation/g1/g1HeapRegionTraceType.hpp" #include "gc_implementation/g1/heapRegionType.hpp" bool HeapRegionType::is_valid(Tag tag) { @@ -67,3 +68,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-01-28 17:43:41.000000000 +0800 +++ new/src/share/vm/gc_implementation/g1/heapRegionType.hpp 2019-01-28 17:43:41.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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 @@ -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/parallelScavenge/psPromotionManager.hpp 2019-01-28 17:43:42.000000000 +0800 +++ new/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.hpp 2019-01-28 17:43:42.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -148,6 +148,10 @@ claimed_stack_depth()->push(p); } + inline void promotion_trace_event(oop new_obj, oop old_obj, size_t obj_size, + uint age, bool tenured, + const PSPromotionLAB* lab); + protected: static OopStarTaskQueueSet* stack_array_depth() { return _stack_array_depth; } public: --- old/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.inline.hpp 2019-01-28 17:43:43.000000000 +0800 +++ new/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.inline.hpp 2019-01-28 17:43:43.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -64,6 +64,34 @@ claim_or_forward_internal_depth(p); } +inline void PSPromotionManager::promotion_trace_event(oop new_obj, oop old_obj, + size_t obj_size, + uint age, bool tenured, + const PSPromotionLAB* lab) { + assert(EnableJFR, "sanity check"); + // Skip if memory allocation failed + if (new_obj != NULL) { + const ParallelScavengeTracer* gc_tracer = PSScavenge::gc_tracer(); + + if (lab != NULL) { + // Promotion of object through newly allocated PLAB + if (gc_tracer->should_report_promotion_in_new_plab_event()) { + size_t obj_bytes = obj_size * HeapWordSize; + size_t lab_size = lab->capacity(); + gc_tracer->report_promotion_in_new_plab_event(old_obj->klass(), obj_bytes, + age, tenured, lab_size); + } + } else { + // Promotion of object directly to heap + if (gc_tracer->should_report_promotion_outside_plab_event()) { + size_t obj_bytes = obj_size * HeapWordSize; + gc_tracer->report_promotion_outside_plab_event(old_obj->klass(), obj_bytes, + age, tenured); + } + } + } +} + // // This method is pretty bulky. It would be nice to split it up // into smaller submethods, but we need to be careful not to hurt @@ -98,6 +126,9 @@ if (new_obj_size > (YoungPLABSize / 2)) { // Allocate this object directly new_obj = (oop)young_space()->cas_allocate(new_obj_size); + if (EnableJFR) { + promotion_trace_event(new_obj, o, new_obj_size, age, false, NULL); + } } else { // Flush and fill _young_lab.flush(); @@ -107,6 +138,9 @@ _young_lab.initialize(MemRegion(lab_base, YoungPLABSize)); // Try the young lab allocation again. new_obj = (oop) _young_lab.allocate(new_obj_size); + if (EnableJFR) { + promotion_trace_event(new_obj, o, new_obj_size, age, false, &_young_lab); + } } else { _young_gen_is_full = true; } @@ -127,11 +161,20 @@ new_obj_is_tenured = true; if (new_obj == NULL) { + uint age = 0; + if (EnableJFR) { + // Find the objects age, MT safe. + age = (test_mark->has_displaced_mark_helper() /* o->has_displaced_mark() */) ? + test_mark->displaced_mark_helper()->age() : test_mark->age(); + } if (!_old_gen_is_full) { // Do we allocate directly, or flush and refill? if (new_obj_size > (OldPLABSize / 2)) { // Allocate this object directly new_obj = (oop)old_gen()->cas_allocate(new_obj_size); + if (EnableJFR) { + promotion_trace_event(new_obj, o, new_obj_size, age, true, NULL); + } } else { // Flush and fill _old_lab.flush(); @@ -148,6 +191,9 @@ _old_lab.initialize(MemRegion(lab_base, OldPLABSize)); // Try the old lab allocation again. new_obj = (oop) _old_lab.allocate(new_obj_size); + if (EnableJFR) { + promotion_trace_event(new_obj, o, new_obj_size, age, true, &_old_lab); + } } } } --- old/src/share/vm/gc_implementation/parallelScavenge/psScavenge.hpp 2019-01-28 17:43:44.000000000 +0800 +++ new/src/share/vm/gc_implementation/parallelScavenge/psScavenge.hpp 2019-01-28 17:43:44.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -93,6 +93,8 @@ // Private accessors static CardTableExtension* const card_table() { assert(_card_table != NULL, "Sanity"); return _card_table; } + static const ParallelScavengeTracer* gc_tracer() { return &_gc_tracer; } + public: // Accessors static uint tenuring_threshold() { return _tenuring_threshold; } --- old/src/share/vm/gc_implementation/shared/ageTable.cpp 2019-01-28 17:43:45.000000000 +0800 +++ new/src/share/vm/gc_implementation/shared/ageTable.cpp 2019-01-28 17:43:45.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -25,6 +25,7 @@ #include "precompiled.hpp" #include "gc_implementation/shared/ageTable.hpp" #include "gc_implementation/shared/gcPolicyCounters.hpp" +#include "gc_implementation/shared/ageTableTracer.hpp" #include "memory/collectorPolicy.hpp" #include "memory/resourceArea.hpp" #include "memory/sharedHeap.hpp" @@ -92,7 +93,7 @@ } uint result = age < MaxTenuringThreshold ? age : MaxTenuringThreshold; - if (PrintTenuringDistribution || UsePerfData) { + if (PrintTenuringDistribution || UsePerfData || (EnableJFR && AgeTableTracer::is_tenuring_distribution_event_enabled())) { if (PrintTenuringDistribution) { gclog_or_tty->cr(); @@ -110,6 +111,11 @@ age, sizes[age]*oopSize, total*oopSize); } } + + if (EnableJFR) { + AgeTableTracer::send_tenuring_distribution_event(age, sizes[age] * oopSize); + } + if (UsePerfData) { _perf_sizes[age]->set_value(sizes[age]*oopSize); } --- old/src/share/vm/gc_implementation/shared/gcHeapSummary.hpp 2019-01-28 17:43:46.000000000 +0800 +++ new/src/share/vm/gc_implementation/shared/gcHeapSummary.hpp 2019-01-28 17:43:46.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -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 { @@ -125,6 +127,24 @@ } }; +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); + } +}; + class MetaspaceSummary : public StackObj { size_t _capacity_until_GC; MetaspaceSizes _meta_space; --- old/src/share/vm/gc_implementation/shared/gcTrace.cpp 2019-01-28 17:43:47.000000000 +0800 +++ new/src/share/vm/gc_implementation/shared/gcTrace.cpp 2019-01-28 17:43:47.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -172,6 +172,30 @@ _tenuring_threshold = tenuring_threshold; } +bool YoungGCTracer::should_report_promotion_events() const { + return should_report_promotion_in_new_plab_event() || + should_report_promotion_outside_plab_event(); +} + +bool YoungGCTracer::should_report_promotion_in_new_plab_event() const { + return should_send_promotion_in_new_plab_event(); +} + +bool YoungGCTracer::should_report_promotion_outside_plab_event() const { + return should_send_promotion_outside_plab_event(); +} + +void YoungGCTracer::report_promotion_in_new_plab_event(Klass* klass, size_t obj_size, + uint age, bool tenured, + size_t plab_size) const { + send_promotion_in_new_plab_event(klass, obj_size, age, tenured, plab_size); +} + +void YoungGCTracer::report_promotion_outside_plab_event(Klass* klass, size_t obj_size, + uint age, bool tenured) const { + send_promotion_outside_plab_event(klass, obj_size, age, tenured); +} + void OldGCTracer::report_gc_end_impl(const Ticks& timestamp, TimePartitions* time_partitions) { assert_set_gc_id(); @@ -199,6 +223,13 @@ } #if INCLUDE_ALL_GCS +void G1MMUTracer::report_mmu(double time_slice_sec, double gc_time_sec, double max_time_sec, bool gc_thread) { + send_g1_mmu_event(time_slice_sec * MILLIUNITS, + gc_time_sec * MILLIUNITS, + max_time_sec * MILLIUNITS, + gc_thread); +} + void G1NewTracer::report_yc_type(G1YCType type) { assert_set_gc_id(); @@ -224,4 +255,19 @@ send_evacuation_failed_event(ef_info); ef_info.reset(); } + +void G1NewTracer::report_basic_ihop_statistics(size_t threshold, + size_t target_ccupancy, + size_t current_occupancy, + size_t last_allocation_size, + double last_allocation_duration, + double last_marking_length) { + send_basic_ihop_statistics(threshold, + target_ccupancy, + current_occupancy, + last_allocation_size, + last_allocation_duration, + last_marking_length); +} + #endif --- old/src/share/vm/gc_implementation/shared/gcTrace.hpp 2019-01-28 17:43:49.000000000 +0800 +++ new/src/share/vm/gc_implementation/shared/gcTrace.hpp 2019-01-28 17:43:48.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -157,9 +157,39 @@ void report_promotion_failed(const PromotionFailedInfo& pf_info); void report_tenuring_threshold(const uint tenuring_threshold); + /* + * Methods for reporting Promotion in new or outside PLAB Events. + * + * The object age is always required as it is not certain that the mark word + * of the oop can be trusted at this stage. + * + * obj_size is the size of the promoted object in bytes. + * + * tenured should be true if the object has been promoted to the old + * space during this GC, if the object is copied to survivor space + * from young space or survivor space (aging) tenured should be false. + * + * plab_size is the size of the newly allocated PLAB in bytes. + */ + bool should_report_promotion_events() const; + bool should_report_promotion_in_new_plab_event() const; + bool should_report_promotion_outside_plab_event() const; + void report_promotion_in_new_plab_event(Klass* klass, size_t obj_size, + uint age, bool tenured, + size_t plab_size) const; + void report_promotion_outside_plab_event(Klass* klass, size_t obj_size, + uint age, bool tenured) const; + private: void send_young_gc_event() const; void send_promotion_failed_event(const PromotionFailedInfo& pf_info) const; + bool should_send_promotion_in_new_plab_event() const; + bool should_send_promotion_outside_plab_event() const; + void send_promotion_in_new_plab_event(Klass* klass, size_t obj_size, + uint age, bool tenured, + size_t plab_size) const; + void send_promotion_outside_plab_event(Klass* klass, size_t obj_size, + uint age, bool tenured) const; }; class OldGCTracer : public GCTracer { @@ -210,6 +240,13 @@ }; #if INCLUDE_ALL_GCS +class G1MMUTracer : public AllStatic { + static void send_g1_mmu_event(double time_slice_ms, double gc_time_ms, double max_time_ms, bool gc_thread); + + public: + static void report_mmu(double time_slice_sec, double gc_time_sec, double max_time_sec, bool gc_thread); +}; + class G1NewTracer : public YoungGCTracer { G1YoungGCInfo _g1_young_gc_info; @@ -221,10 +258,25 @@ void report_evacuation_info(EvacuationInfo* info); void report_evacuation_failed(EvacuationFailedInfo& ef_info); + void report_basic_ihop_statistics(size_t threshold, + size_t target_occupancy, + size_t current_occupancy, + size_t last_allocation_size, + double last_allocation_duration, + double last_marking_length); + private: void send_g1_young_gc_event(); void send_evacuation_info_event(EvacuationInfo* info); void send_evacuation_failed_event(const EvacuationFailedInfo& ef_info) const; + + void send_basic_ihop_statistics(size_t threshold, + size_t target_occupancy, + size_t current_occupancy, + size_t last_allocation_size, + double last_allocation_duration, + double last_marking_length); + }; #endif --- old/src/share/vm/gc_implementation/shared/gcTraceSend.cpp 2019-01-28 17:43:50.000000000 +0800 +++ new/src/share/vm/gc_implementation/shared/gcTraceSend.cpp 2019-01-28 17:43:49.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,7 @@ #if INCLUDE_ALL_GCS #include "gc_implementation/g1/evacuationInfo.hpp" #include "gc_implementation/g1/g1YCTypes.hpp" +#include "tracefiles/traceEventClasses.hpp" #endif // All GC dependencies against the trace framework is contained within this file. @@ -41,7 +42,7 @@ typedef uintptr_t TraceAddress; void GCTracer::send_garbage_collection_event() const { - EventGCGarbageCollection event(UNTIMED); + EventGarbageCollection event(UNTIMED); if (event.should_commit()) { event.set_gcId(_shared_gc_info.gc_id().id()); event.set_name(_shared_gc_info.name()); @@ -89,7 +90,7 @@ } void ParallelOldTracer::send_parallel_old_event() const { - EventGCParallelOld e(UNTIMED); + EventParallelOldGarbageCollection e(UNTIMED); if (e.should_commit()) { e.set_gcId(_shared_gc_info.gc_id().id()); e.set_densePrefix((TraceAddress)_parallel_old_gc_info.dense_prefix()); @@ -100,7 +101,7 @@ } void YoungGCTracer::send_young_gc_event() const { - EventGCYoungGarbageCollection e(UNTIMED); + EventYoungGarbageCollection e(UNTIMED); if (e.should_commit()) { e.set_gcId(_shared_gc_info.gc_id().id()); e.set_tenuringThreshold(_tenuring_threshold); @@ -110,8 +111,48 @@ } } +bool YoungGCTracer::should_send_promotion_in_new_plab_event() const { + return EventPromoteObjectInNewPLAB::is_enabled(); +} + +bool YoungGCTracer::should_send_promotion_outside_plab_event() const { + return EventPromoteObjectOutsidePLAB::is_enabled(); +} + +void YoungGCTracer::send_promotion_in_new_plab_event(Klass* klass, size_t obj_size, + uint age, bool tenured, + size_t plab_size) const { + + EventPromoteObjectInNewPLAB event; + if (event.should_commit()) { + event.set_gcId(_shared_gc_info.gc_id().id()); + event.set_objectClass(klass); + event.set_objectSize(obj_size); + event.set_tenured(tenured); + event.set_tenuringAge(age); + event.set_plabSize(plab_size); + event.commit(); + } +} + +void YoungGCTracer::send_promotion_outside_plab_event(Klass* klass, size_t obj_size, + uint age, bool tenured) const { + + EventPromoteObjectOutsidePLAB event; + if (event.should_commit()) { + event.set_gcId(_shared_gc_info.gc_id().id()); + event.set_gcId(GCId::peek().id() - 1); + event.set_objectClass(klass); + event.set_objectSize(obj_size); + event.set_tenured(tenured); + event.set_tenuringAge(age); + event.commit(); + } +} + + void OldGCTracer::send_old_gc_event() const { - EventGCOldGarbageCollection e(UNTIMED); + EventOldGarbageCollection e(UNTIMED); if (e.should_commit()) { e.set_gcId(_shared_gc_info.gc_id().id()); e.set_starttime(_shared_gc_info.start_timestamp()); @@ -133,7 +174,7 @@ EventPromotionFailed e; if (e.should_commit()) { e.set_gcId(_shared_gc_info.gc_id().id()); - e.set_data(to_trace_struct(pf_info)); + e.set_promotionFailed(to_trace_struct(pf_info)); e.set_thread(pf_info.thread()->thread_id()); e.commit(); } @@ -150,7 +191,7 @@ #if INCLUDE_ALL_GCS void G1NewTracer::send_g1_young_gc_event() { - EventGCG1GarbageCollection e(UNTIMED); + EventG1GarbageCollection e(UNTIMED); if (e.should_commit()) { e.set_gcId(_shared_gc_info.gc_id().id()); e.set_type(_g1_young_gc_info.type()); @@ -160,16 +201,31 @@ } } +void G1MMUTracer::send_g1_mmu_event(double time_slice_ms, double gc_time_ms, double max_time_ms, bool gc_thread) { + EventG1MMU e; + if (e.should_commit()) { + if (gc_thread) { + e.set_gcId(G1CollectedHeap::heap()->gc_tracer_cm()->gc_id().id()); + } else { + e.set_gcId(G1CollectedHeap::heap()->gc_tracer_stw()->gc_id().id()); + } + e.set_timeSlice(time_slice_ms); + e.set_gcTime(gc_time_ms); + e.set_pauseTarget(max_time_ms); + e.commit(); + } +} + void G1NewTracer::send_evacuation_info_event(EvacuationInfo* info) { - EventEvacuationInfo e; + EventEvacuationInformation e; if (e.should_commit()) { e.set_gcId(_shared_gc_info.gc_id().id()); e.set_cSetRegions(info->collectionset_regions()); e.set_cSetUsedBefore(info->collectionset_used_before()); e.set_cSetUsedAfter(info->collectionset_used_after()); e.set_allocationRegions(info->allocation_regions()); - e.set_allocRegionsUsedBefore(info->alloc_regions_used_before()); - e.set_allocRegionsUsedAfter(info->alloc_regions_used_before() + info->bytes_copied()); + e.set_allocationRegionsUsedBefore(info->alloc_regions_used_before()); + e.set_allocationRegionsUsedAfter(info->alloc_regions_used_before() + info->bytes_copied()); e.set_bytesCopied(info->bytes_copied()); e.set_regionsFreed(info->regions_freed()); e.commit(); @@ -180,10 +236,31 @@ EventEvacuationFailed e; if (e.should_commit()) { e.set_gcId(_shared_gc_info.gc_id().id()); - e.set_data(to_trace_struct(ef_info)); + e.set_evacuationFailed(to_trace_struct(ef_info)); e.commit(); } } + +void G1NewTracer::send_basic_ihop_statistics(size_t threshold, + size_t target_occupancy, + size_t current_occupancy, + size_t last_allocation_size, + double last_allocation_duration, + double last_marking_length) { + EventG1BasicIHOP evt; + if (evt.should_commit()) { + evt.set_gcId(_shared_gc_info.gc_id().id()); + evt.set_threshold(threshold); + evt.set_targetOccupancy(target_occupancy); + evt.set_thresholdPercentage(target_occupancy > 0 ? ((double)threshold / target_occupancy) : 0.0); + evt.set_currentOccupancy(current_occupancy); + evt.set_recentMutatorAllocationSize(last_allocation_size); + evt.set_recentMutatorDuration(last_allocation_duration * MILLIUNITS); + evt.set_recentAllocationRate(last_allocation_duration != 0.0 ? last_allocation_size / last_allocation_duration : 0.0); + evt.set_lastMarkingDuration(last_marking_length * MILLIUNITS); + evt.commit(); + } +} #endif static TraceStructVirtualSpace to_trace_struct(const VirtualSpaceSummary& summary) { @@ -224,6 +301,20 @@ } } + 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/gc_implementation/shared/gcTraceTime.cpp 2019-01-28 17:43:51.000000000 +0800 +++ new/src/share/vm/gc_implementation/shared/gcTraceTime.cpp 2019-01-28 17:43:50.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -72,7 +72,8 @@ if (_doit) { const Tickspan duration = stop_counter - _start_counter; - double duration_in_seconds = TicksToTimeHelper::seconds(duration); + double duration_in_seconds = TimeHelper::counter_to_seconds(duration.value()); + if (_print_cr) { gclog_or_tty->print_cr(", %3.7f secs]", duration_in_seconds); } else { --- old/src/share/vm/gc_implementation/shared/objectCountEventSender.cpp 2019-01-28 17:43:52.000000000 +0800 +++ new/src/share/vm/gc_implementation/shared/objectCountEventSender.cpp 2019-01-28 17:43:51.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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 @@ -32,28 +32,44 @@ #include "utilities/macros.hpp" #include "utilities/ticks.hpp" #if INCLUDE_SERVICES - -void ObjectCountEventSender::send(const KlassInfoEntry* entry, GCId gc_id, const Ticks& timestamp) { -#if INCLUDE_TRACE - assert(Tracing::is_event_enabled(EventObjectCountAfterGC::eventId), - "Only call this method if the event is enabled"); - - EventObjectCountAfterGC event(UNTIMED); - event.set_gcId(gc_id.id()); - event.set_class(entry->klass()); - event.set_count(entry->count()); - event.set_totalSize(entry->words() * BytesPerWord); - event.set_endtime(timestamp); - event.commit(); -#endif // INCLUDE_TRACE -} - bool ObjectCountEventSender::should_send_event() { #if INCLUDE_TRACE - return Tracing::is_event_enabled(EventObjectCountAfterGC::eventId); + return _should_send_requestable_event || + Tracing::is_event_enabled(EventObjectCountAfterGC::eventId); #else return false; #endif // INCLUDE_TRACE } +bool ObjectCountEventSender::_should_send_requestable_event = false; + +void ObjectCountEventSender::enable_requestable_event() { + _should_send_requestable_event = true; +} + +void ObjectCountEventSender::disable_requestable_event() { + _should_send_requestable_event = false; +} + +template +void ObjectCountEventSender::send_event_if_enabled(Klass* klass, jlong count, julong size, GCId gc_id, const Ticks& timestamp) { + T event(UNTIMED); + if (event.should_commit()) { + event.set_gcId(gc_id.id()); + event.set_objectClass(klass); + event.set_count(count); + event.set_totalSize(size); + event.set_endtime(timestamp); + event.commit(); + } +} + +void ObjectCountEventSender::send(const KlassInfoEntry* entry, GCId gc_id, const Ticks& timestamp) { + Klass* klass = entry->klass(); + jlong count = entry->count(); + julong total_size = entry->words() * BytesPerWord; + + send_event_if_enabled(klass, count, total_size, gc_id, timestamp); + send_event_if_enabled(klass, count, total_size, gc_id, timestamp); +} #endif // INCLUDE_SERVICES --- old/src/share/vm/gc_implementation/shared/objectCountEventSender.hpp 2019-01-28 17:43:53.000000000 +0800 +++ new/src/share/vm/gc_implementation/shared/objectCountEventSender.hpp 2019-01-28 17:43:53.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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 @@ -32,10 +32,25 @@ #if INCLUDE_SERVICES class KlassInfoEntry; -class Ticks; class ObjectCountEventSender : public AllStatic { + static bool _should_send_requestable_event; + + template + static void send_event_if_enabled(Klass* klass, jlong count, julong size, GCId gc_id, const Ticks& timestamp); + + public: + static void enable_requestable_event(); + static void disable_requestable_event(); + + public: + // The following two functions have the exact same signature as + // hotspot/src/share/vm/gc_implementation/shared/objectCountEventSender.hpp + // + // This file will replace the open file if a closed build is performed. + // These function signatures can therefore not be changed if the open + // signatures aren't changed as well. static void send(const KlassInfoEntry* entry, GCId gc_id, const Ticks& timestamp); static bool should_send_event(); }; --- old/src/share/vm/gc_interface/allocTracer.cpp 2019-01-28 17:43:54.000000000 +0800 +++ new/src/share/vm/gc_interface/allocTracer.cpp 2019-01-28 17:43:54.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -29,19 +29,21 @@ #include "runtime/handles.hpp" #include "utilities/globalDefinitions.hpp" -void AllocTracer::send_allocation_outside_tlab_event(KlassHandle klass, size_t alloc_size) { - EventAllocObjectOutsideTLAB event; +void AllocTracer::send_allocation_outside_tlab_event(KlassHandle klass, HeapWord* obj, size_t alloc_size, Thread* thread) { + TRACE_ALLOCATION(obj, alloc_size, thread); + EventObjectAllocationOutsideTLAB event; if (event.should_commit()) { - event.set_class(klass()); + event.set_objectClass(klass()); event.set_allocationSize(alloc_size); event.commit(); } } -void AllocTracer::send_allocation_in_new_tlab_event(KlassHandle klass, size_t tlab_size, size_t alloc_size) { - EventAllocObjectInNewTLAB event; +void AllocTracer::send_allocation_in_new_tlab_event(KlassHandle klass, HeapWord* obj, size_t tlab_size, size_t alloc_size, Thread* thread) { + TRACE_ALLOCATION(obj, alloc_size, thread); + EventObjectAllocationInNewTLAB event; if (event.should_commit()) { - event.set_class(klass()); + event.set_objectClass(klass()); event.set_allocationSize(alloc_size); event.set_tlabSize(tlab_size); event.commit(); --- old/src/share/vm/gc_interface/allocTracer.hpp 2019-01-28 17:43:55.000000000 +0800 +++ new/src/share/vm/gc_interface/allocTracer.hpp 2019-01-28 17:43:55.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -30,8 +30,8 @@ class AllocTracer : AllStatic { public: - static void send_allocation_outside_tlab_event(KlassHandle klass, size_t alloc_size); - static void send_allocation_in_new_tlab_event(KlassHandle klass, size_t tlab_size, size_t alloc_size); + static void send_allocation_outside_tlab_event(KlassHandle klass, HeapWord* obj, size_t alloc_size, Thread* thread); + static void send_allocation_in_new_tlab_event(KlassHandle klass, HeapWord* obj, size_t tlab_size, size_t alloc_size, Thread* thread); static void send_allocation_requiring_gc_event(size_t size, const GCId& gcId); }; --- old/src/share/vm/gc_interface/collectedHeap.cpp 2019-01-28 17:43:56.000000000 +0800 +++ new/src/share/vm/gc_interface/collectedHeap.cpp 2019-01-28 17:43:56.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -88,10 +88,12 @@ MetaspaceAux::committed_bytes(), MetaspaceAux::used_bytes(), MetaspaceAux::reserved_bytes()); + const MetaspaceSizes data_space( MetaspaceAux::committed_bytes(Metaspace::NonClassType), MetaspaceAux::used_bytes(Metaspace::NonClassType), MetaspaceAux::reserved_bytes(Metaspace::NonClassType)); + const MetaspaceSizes class_space( MetaspaceAux::committed_bytes(Metaspace::ClassType), MetaspaceAux::used_bytes(Metaspace::ClassType), @@ -286,7 +288,7 @@ return NULL; } - AllocTracer::send_allocation_in_new_tlab_event(klass, new_tlab_size * HeapWordSize, size * HeapWordSize); + AllocTracer::send_allocation_in_new_tlab_event(klass, obj, new_tlab_size * HeapWordSize, size * HeapWordSize, thread); if (ZeroTLAB) { // ..and clear it. --- old/src/share/vm/gc_interface/collectedHeap.inline.hpp 2019-01-28 17:43:57.000000000 +0800 +++ new/src/share/vm/gc_interface/collectedHeap.inline.hpp 2019-01-28 17:43:57.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -140,7 +140,7 @@ "Unexpected exception, will result in uninitialized storage"); THREAD->incr_allocated_bytes(size * HeapWordSize); - AllocTracer::send_allocation_outside_tlab_event(klass, size * HeapWordSize); + AllocTracer::send_allocation_outside_tlab_event(klass, result, size * HeapWordSize, THREAD); return result; } --- old/src/share/vm/memory/metaspace.cpp 2019-01-28 17:43:58.000000000 +0800 +++ new/src/share/vm/memory/metaspace.cpp 2019-01-28 17:43:58.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -1135,7 +1135,6 @@ #endif } - // This function looks at the mmap regions in the metaspace without locking. // The chunks are added with store ordering and not deleted except for at // unloading time during a safepoint. --- old/src/share/vm/memory/metaspaceTracer.cpp 2019-01-28 17:43:59.000000000 +0800 +++ new/src/share/vm/memory/metaspaceTracer.cpp 2019-01-28 17:43:59.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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 @@ -62,15 +62,10 @@ Metaspace::MetadataType mdtype) const { E event; if (event.should_commit()) { + event.set_classLoader(cld); if (cld->is_anonymous()) { - event.set_classLoader(NULL); event.set_anonymousClassLoader(true); } else { - if (cld->is_the_null_class_loader_data()) { - event.set_classLoader((Klass*) NULL); - } else { - event.set_classLoader(cld->class_loader()->klass()); - } event.set_anonymousClassLoader(false); } --- old/src/share/vm/oops/arrayKlass.cpp 2019-01-28 17:44:00.000000000 +0800 +++ new/src/share/vm/oops/arrayKlass.cpp 2019-01-28 17:44:00.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -94,6 +94,7 @@ int vtable_size = Universe::base_vtable_size(); set_vtable_length(vtable_size); set_is_cloneable(); // All arrays are considered to be cloneable (See JLS 20.1.5) + TRACE_INIT_ID(this); } --- old/src/share/vm/oops/instanceKlass.hpp 2019-01-28 17:44:01.000000000 +0800 +++ new/src/share/vm/oops/instanceKlass.hpp 2019-01-28 17:44:01.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -833,7 +833,7 @@ // support for stub routines static ByteSize init_state_offset() { return in_ByteSize(offset_of(InstanceKlass, _init_state)); } - TRACE_DEFINE_OFFSET; + TRACE_DEFINE_KLASS_TRACE_ID_OFFSET; static ByteSize init_thread_offset() { return in_ByteSize(offset_of(InstanceKlass, _init_thread)); } // subclass/subinterface checks --- old/src/share/vm/oops/klass.cpp 2019-01-28 17:44:02.000000000 +0800 +++ new/src/share/vm/oops/klass.cpp 2019-01-28 17:44:02.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -197,7 +197,6 @@ set_subklass(NULL); set_next_sibling(NULL); set_next_link(NULL); - TRACE_INIT_ID(this); set_prototype_header(markOopDesc::prototype()); set_biased_lock_revocation_count(0); @@ -524,6 +523,7 @@ } void Klass::remove_unshareable_info() { + TRACE_REMOVE_ID(this); assert (DumpSharedSpaces, "only called for DumpSharedSpaces"); set_subklass(NULL); @@ -537,7 +537,7 @@ } void Klass::restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS) { - TRACE_INIT_ID(this); + TRACE_RESTORE_ID(this); // If an exception happened during CDS restore, some of these fields may already be // set. We leave the class on the CLD list, even if incomplete so that we don't // modify the CLD list outside a safepoint. --- old/src/share/vm/oops/klass.hpp 2019-01-28 17:44:03.000000000 +0800 +++ new/src/share/vm/oops/klass.hpp 2019-01-28 17:44:03.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -40,6 +40,7 @@ #include "gc_implementation/g1/g1OopClosures.hpp" #include "gc_implementation/parNew/parOopClosures.hpp" #endif // INCLUDE_ALL_GCS +#include "jfr/utilities/jfrLog.hpp" // // A Klass provides: @@ -170,7 +171,7 @@ markOop _prototype_header; // Used when biased locking is both enabled and disabled for this type jint _biased_lock_revocation_count; - TRACE_DEFINE_KLASS_TRACE_ID; + TRACE_DEFINE_TRACE_ID_FIELD; // Remembered sets support for the oops in the klasses. jbyte _modified_oops; // Card Table Equivalent (YC/CMS support) @@ -612,7 +613,7 @@ jlong last_biased_lock_bulk_revocation_time() { return _last_biased_lock_bulk_revocation_time; } void set_last_biased_lock_bulk_revocation_time(jlong cur_time) { _last_biased_lock_bulk_revocation_time = cur_time; } - TRACE_DEFINE_KLASS_METHODS; + TRACE_DEFINE_TRACE_ID_METHODS; // garbage collection support virtual void oops_do(OopClosure* cl); --- old/src/share/vm/oops/method.hpp 2019-01-28 17:44:04.000000000 +0800 +++ new/src/share/vm/oops/method.hpp 2019-01-28 17:44:04.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -116,6 +116,8 @@ _has_injected_profile : 1, : 2; + TRACE_DEFINE_FLAG; + #ifndef PRODUCT int _compiled_invocation_count; // Number of nmethod invocations so far (for perf. debugging) #endif @@ -805,6 +807,8 @@ bool has_injected_profile() { return _has_injected_profile; } void set_has_injected_profile(bool x) { _has_injected_profile = x; } + TRACE_DEFINE_FLAG_ACCESSOR; + ConstMethod::MethodType method_type() const { return _constMethod->method_type(); } --- old/src/share/vm/oops/objArrayKlass.hpp 2019-01-28 17:44:05.000000000 +0800 +++ new/src/share/vm/oops/objArrayKlass.hpp 2019-01-28 17:44:05.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -96,6 +96,11 @@ return (ObjArrayKlass*) k; } + static const ObjArrayKlass* cast(const Klass* k) { + assert(k->oop_is_objArray(), "cast to ObjArrayKlass"); + return static_cast(k); + } + // Sizing static int header_size() { return sizeof(ObjArrayKlass)/HeapWordSize; } int size() const { return ArrayKlass::static_size(header_size()); } --- old/src/share/vm/oops/typeArrayKlass.hpp 2019-01-28 17:44:07.000000000 +0800 +++ new/src/share/vm/oops/typeArrayKlass.hpp 2019-01-28 17:44:06.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -93,8 +93,12 @@ public: // Casting from Klass* static TypeArrayKlass* cast(Klass* k) { - assert(k->oop_is_typeArray(), "cast to TypeArrayKlass"); - return (TypeArrayKlass*) k; + return const_cast(cast(const_cast(k))); + } + + static const TypeArrayKlass* cast(const Klass* k) { + //assert(k->is_typeArray_klass(), "cast to TypeArrayKlass"); + return static_cast(k); } // Naming --- old/src/share/vm/opto/bytecodeInfo.cpp 2019-01-28 17:44:08.000000000 +0800 +++ new/src/share/vm/opto/bytecodeInfo.cpp 2019-01-28 17:44:07.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -500,6 +500,18 @@ //tty->print(" bcs: %d+%d invoked: %d", top->count_inline_bcs(), callee_method->code_size(), callee_method->interpreter_invocation_count()); } } +#if INCLUDE_TRACE + EventCompilerInlining event; + if (event.should_commit()) { + event.set_compileId(C->compile_id()); + event.set_message(inline_msg); + event.set_succeeded(success); + event.set_bci(caller_bci); + event.set_caller(_method->get_Method()); + event.set_callee(callee_method->to_trace_struct()); + event.commit(); + } +#endif // INCLUDE_TRACE } //------------------------------ok_to_inline----------------------------------- --- old/src/share/vm/opto/compile.cpp 2019-01-28 17:44:09.000000000 +0800 +++ new/src/share/vm/opto/compile.cpp 2019-01-28 17:44:09.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -3588,10 +3588,10 @@ _failure_reason = reason; } - EventCompilerFailure event; + EventCompilationFailure event; if (event.should_commit()) { - event.set_compileID(Compile::compile_id()); - event.set_failure(reason); + event.set_compileId(Compile::compile_id()); + event.set_failureMessage(reason); event.commit(); } --- old/src/share/vm/opto/compile.hpp 2019-01-28 17:44:10.000000000 +0800 +++ new/src/share/vm/opto/compile.hpp 2019-01-28 17:44:10.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -637,7 +637,7 @@ if (event.should_commit()) { event.set_starttime(C->_latest_stage_start_counter); event.set_phase((u1) cpt); - event.set_compileID(C->_compile_id); + event.set_compileId(C->_compile_id); event.set_phaseLevel(level); event.commit(); } @@ -654,7 +654,7 @@ if (event.should_commit()) { event.set_starttime(C->_latest_stage_start_counter); event.set_phase((u1) PHASE_END); - event.set_compileID(C->_compile_id); + event.set_compileId(C->_compile_id); event.set_phaseLevel(level); event.commit(); } --- old/src/share/vm/opto/library_call.cpp 2019-01-28 17:44:11.000000000 +0800 +++ new/src/share/vm/opto/library_call.cpp 2019-01-28 17:44:11.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -238,7 +238,7 @@ bool inline_native_currentThread(); #ifdef TRACE_HAVE_INTRINSICS bool inline_native_classID(); - bool inline_native_threadID(); + bool inline_native_getEventWriter(); #endif bool inline_native_time_funcs(address method, const char* funcName); bool inline_native_isInterrupted(); @@ -880,9 +880,9 @@ case vmIntrinsics::_isInterrupted: return inline_native_isInterrupted(); #ifdef TRACE_HAVE_INTRINSICS - case vmIntrinsics::_classID: return inline_native_classID(); - case vmIntrinsics::_threadID: return inline_native_threadID(); case vmIntrinsics::_counterTime: return inline_native_time_funcs(CAST_FROM_FN_PTR(address, TRACE_TIME_METHOD), "counterTime"); + case vmIntrinsics::_getClassId: return inline_native_classID(); + case vmIntrinsics::_getEventWriter: return inline_native_getEventWriter(); #endif case vmIntrinsics::_currentTimeMillis: return inline_native_time_funcs(CAST_FROM_FN_PTR(address, os::javaTimeMillis), "currentTimeMillis"); case vmIntrinsics::_nanoTime: return inline_native_time_funcs(CAST_FROM_FN_PTR(address, os::javaTimeNanos), "nanoTime"); @@ -3272,42 +3272,75 @@ * return myklass->trace_id & ~0x3 */ bool LibraryCallKit::inline_native_classID() { - null_check_receiver(); // null-check, then ignore - Node* cls = null_check(argument(1), T_OBJECT); - Node* kls = load_klass_from_mirror(cls, false, NULL, 0); - kls = null_check(kls, T_OBJECT); - ByteSize offset = TRACE_ID_OFFSET; - Node* insp = basic_plus_adr(kls, in_bytes(offset)); - Node* tvalue = make_load(NULL, insp, TypeLong::LONG, T_LONG, MemNode::unordered); - Node* bits = longcon(~0x03l); // ignore bit 0 & 1 - Node* andl = _gvn.transform(new (C) AndLNode(tvalue, bits)); - Node* clsused = longcon(0x01l); // set the class bit - Node* orl = _gvn.transform(new (C) OrLNode(tvalue, clsused)); - - const TypePtr *adr_type = _gvn.type(insp)->isa_ptr(); - store_to_memory(control(), insp, orl, T_LONG, adr_type, MemNode::unordered); - set_result(andl); - return true; + if (EnableJFR) { + Node* cls = null_check(argument(0), T_OBJECT); + Node* kls = load_klass_from_mirror(cls, false, NULL, 0); + kls = null_check(kls, T_OBJECT); + + ByteSize offset = TRACE_KLASS_TRACE_ID_OFFSET; + Node* insp = basic_plus_adr(kls, in_bytes(offset)); + Node* tvalue = make_load(NULL, insp, TypeLong::LONG, T_LONG, MemNode::unordered); + + Node* clsused = longcon(0x01l); // set the class bit + Node* orl = _gvn.transform(new (C) OrLNode(tvalue, clsused)); + const TypePtr *adr_type = _gvn.type(insp)->isa_ptr(); + store_to_memory(control(), insp, orl, T_LONG, adr_type, MemNode::unordered); + +#ifdef TRACE_ID_META_BITS + Node* mbits = longcon(~TRACE_ID_META_BITS); + tvalue = _gvn.transform(new (C) AndLNode(tvalue, mbits)); +#endif +#ifdef TRACE_ID_SHIFT + Node* cbits = intcon(TRACE_ID_SHIFT); + tvalue = _gvn.transform(new (C) URShiftLNode(tvalue, cbits)); +#endif + + set_result(tvalue); + return true; + } else { + return false; + } } -bool LibraryCallKit::inline_native_threadID() { - Node* tls_ptr = NULL; - Node* cur_thr = generate_current_thread(tls_ptr); - Node* p = basic_plus_adr(top()/*!oop*/, tls_ptr, in_bytes(JavaThread::osthread_offset())); - Node* osthread = make_load(NULL, p, TypeRawPtr::NOTNULL, T_ADDRESS, MemNode::unordered); - p = basic_plus_adr(top()/*!oop*/, osthread, in_bytes(OSThread::thread_id_offset())); +bool LibraryCallKit::inline_native_getEventWriter() { + if (EnableJFR) { + Node* tls_ptr = _gvn.transform(new (C) ThreadLocalNode()); - Node* threadid = NULL; - size_t thread_id_size = OSThread::thread_id_size(); - if (thread_id_size == (size_t) BytesPerLong) { - threadid = ConvL2I(make_load(control(), p, TypeLong::LONG, T_LONG, MemNode::unordered)); - } else if (thread_id_size == (size_t) BytesPerInt) { - threadid = make_load(control(), p, TypeInt::INT, T_INT, MemNode::unordered); + Node* jobj_ptr = basic_plus_adr(top(), tls_ptr, + in_bytes(TRACE_THREAD_DATA_WRITER_OFFSET) + ); + + Node* jobj = make_load(control(), jobj_ptr, TypeRawPtr::BOTTOM, T_ADDRESS, MemNode::unordered); + + Node* jobj_cmp_null = _gvn.transform( new (C) CmpPNode(jobj, null()) ); + Node* test_jobj_eq_null = _gvn.transform( new (C) BoolNode(jobj_cmp_null, BoolTest::eq) ); + + IfNode* iff_jobj_null = + create_and_map_if(control(), test_jobj_eq_null, PROB_MIN, COUNT_UNKNOWN); + + enum { _normal_path = 1, + _null_path = 2, + PATH_LIMIT }; + + RegionNode* result_rgn = new (C) RegionNode(PATH_LIMIT); + PhiNode* result_val = new (C) PhiNode(result_rgn, TypePtr::BOTTOM); + + Node* jobj_is_null = _gvn.transform(new (C) IfTrueNode(iff_jobj_null)); + result_rgn->init_req(_null_path, jobj_is_null); + result_val->init_req(_null_path, null()); + + Node* jobj_is_not_null = _gvn.transform(new (C) IfFalseNode(iff_jobj_null)); + result_rgn->init_req(_normal_path, jobj_is_not_null); + + Node* res = make_load(jobj_is_not_null, jobj, TypeInstPtr::NOTNULL, T_OBJECT, MemNode::unordered); + result_val->init_req(_normal_path, res); + + set_result(result_rgn, result_val); + + return true; } else { - ShouldNotReachHere(); + return false; } - set_result(threadid); - return true; } #endif --- old/src/share/vm/prims/jni.cpp 2019-01-28 17:44:12.000000000 +0800 +++ new/src/share/vm/prims/jni.cpp 2019-01-28 17:44:12.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012 Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -5239,7 +5239,7 @@ EventThreadStart event; if (event.should_commit()) { - event.set_javalangthread(java_lang_Thread::thread_id(thread->threadObj())); + event.set_thread(THREAD_TRACE_ID(thread)); event.commit(); } @@ -5454,7 +5454,7 @@ EventThreadStart event; if (event.should_commit()) { - event.set_javalangthread(java_lang_Thread::thread_id(thread->threadObj())); + event.set_thread(THREAD_TRACE_ID(thread)); event.commit(); } --- old/src/share/vm/prims/nativeLookup.cpp 2019-01-28 17:44:14.000000000 +0800 +++ new/src/share/vm/prims/nativeLookup.cpp 2019-01-28 17:44:13.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -136,6 +136,9 @@ { CC"Java_java_lang_invoke_MethodHandleNatives_registerNatives", NULL, FN_PTR(JVM_RegisterMethodHandleMethods) }, { CC"Java_sun_misc_Perf_registerNatives", NULL, FN_PTR(JVM_RegisterPerfMethods) }, { CC"Java_sun_hotspot_WhiteBox_registerNatives", NULL, FN_PTR(JVM_RegisterWhiteBoxMethods) }, +#if INCLUDE_TRACE + { CC"Java_jdk_jfr_internal_JVM_registerNatives", NULL, TRACE_REGISTER_NATIVES }, +#endif }; static address lookup_special_native(char* jni_name) { --- old/src/share/vm/prims/unsafe.cpp 2019-01-28 17:44:15.000000000 +0800 +++ new/src/share/vm/prims/unsafe.cpp 2019-01-28 17:44:15.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -1255,7 +1255,7 @@ #endif /* USDT2 */ if (event.should_commit()) { oop obj = thread->current_park_blocker(); - event.set_klass((obj != NULL) ? obj->klass() : NULL); + event.set_parkedClass((obj != NULL) ? obj->klass() : NULL); event.set_timeout(time); event.set_address((obj != NULL) ? (TYPE_ADDRESS) cast_from_oop(obj) : 0); event.commit(); --- old/src/share/vm/prims/whitebox.cpp 2019-01-28 17:44:16.000000000 +0800 +++ new/src/share/vm/prims/whitebox.cpp 2019-01-28 17:44:16.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -60,6 +60,7 @@ #include "compiler/compileBroker.hpp" #include "jvmtifiles/jvmtiEnv.hpp" #include "runtime/compilationPolicy.hpp" +#include "jfr/utilities/align.hpp" PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC @@ -569,6 +570,14 @@ return (mh->queued_for_compilation() || nm != NULL); WB_END +WB_ENTRY(jboolean, WB_EnqueueInitializerForCompilation(JNIEnv* env, jobject o, jclass klass, jint comp_level)) + InstanceKlass* ik = InstanceKlass::cast(java_lang_Class::as_Klass(JNIHandles::resolve(klass))); + methodHandle mh(THREAD, ik->class_initializer()); + nmethod* nm = CompileBroker::compile_method(mh, InvocationEntryBci, comp_level, mh, mh->invocation_count(), "WhiteBox", THREAD); + MutexLockerEx mu(Compile_lock); + return (mh->queued_for_compilation() || nm != NULL); +WB_END + class VM_WhiteBoxOperation : public VM_Operation { public: VM_WhiteBoxOperation() { } @@ -643,6 +652,18 @@ } template +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, true, true); + env->ReleaseStringUTFChars(name, flag_name); + return result; +} + +template static bool SetVMFlag(JavaThread* thread, JNIEnv* env, jstring name, T* value, bool (*TAtPut)(const char*, T*, Flag::Flags)) { if (name == NULL) { return false; @@ -862,6 +883,37 @@ 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 int WhiteBox::array_bytes_to_length(size_t bytes) { return Array::bytes_to_length(bytes); @@ -933,6 +985,11 @@ VMThread::execute(&force_safepoint_op); WB_END +WB_ENTRY(jlong, WB_GetHeapAlignment(JNIEnv* env, jobject o)) + size_t alignment = Universe::heap()->collector_policy()->heap_alignment(); + return (jlong)alignment; +WB_END + //Some convenience methods to deal with objects from java int WhiteBox::offset_for_field(const char* field_name, oop object, Symbol* signature_symbol) { @@ -1111,6 +1168,8 @@ CC"(I)I", (void*)&WB_GetCompileQueueSize}, {CC"testSetForceInlineMethod", CC"(Ljava/lang/reflect/Executable;Z)Z", (void*)&WB_TestSetForceInlineMethod}, + {CC"enqueueInitializerForCompilation", + CC"(Ljava/lang/Class;I)Z", (void*)&WB_EnqueueInitializerForCompilation}, {CC"enqueueMethodForCompilation", CC"(Ljava/lang/reflect/Executable;II)Z", (void*)&WB_EnqueueMethodForCompilation}, {CC"clearMethodState", @@ -1153,6 +1212,8 @@ (void*)&WB_CheckLibSpecifiesNoexecstack}, {CC"isContainerized", CC"()Z", (void*)&WB_IsContainerized }, {CC"printOsInfo", CC"()V", (void*)&WB_PrintOsInfo }, + {CC"getHeapAlignment", CC"()J", (void*)&WB_GetHeapAlignment}, + {CC"allocateCodeBlob", CC"(II)J", (void*)&WB_AllocateCodeBlob }, }; #undef CC --- old/src/share/vm/prims/whitebox.hpp 2019-01-28 17:44:17.000000000 +0800 +++ new/src/share/vm/prims/whitebox.hpp 2019-01-28 17:44:17.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -65,6 +65,7 @@ static const char* lookup_jstring(const char* field_name, oop object); static bool lookup_bool(const char* field_name, oop object); + 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/arguments.cpp 2019-01-28 17:44:18.000000000 +0800 +++ new/src/share/vm/runtime/arguments.cpp 2019-01-28 17:44:18.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -2636,6 +2636,12 @@ // Check the minimum number of compiler threads status &=verify_min_value(CICompilerCount, min_number_of_compiler_threads, "CICompilerCount"); + if ((FlightRecorder || StartFlightRecording != NULL) && !EnableJFR) { + jio_fprintf(defaultStream::error_stream(), + "The VM option -XX:+FlightRecorder or -XX:StartFlightRecording=... must be combined with -XX:+EnableJFR.\n"); + status = false; + } + return status; } --- old/src/share/vm/runtime/arguments.hpp 2019-01-28 17:44:19.000000000 +0800 +++ new/src/share/vm/runtime/arguments.hpp 2019-01-28 17:44:19.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -50,12 +50,15 @@ char* _key; char* _value; SystemProperty* _next; + bool _internal; + bool _writeable; bool writeable() { return _writeable; } public: // Accessors const char* key() const { return _key; } + bool internal() const { return _internal; } char* value() const { return _value; } SystemProperty* next() const { return _next; } void set_next(SystemProperty* next) { _next = next; } @@ -97,7 +100,7 @@ } // Constructor - SystemProperty(const char* key, const char* value, bool writeable) { + SystemProperty(const char* key, const char* value, bool writeable, bool internal = false) { if (key == NULL) { _key = NULL; } else { @@ -112,6 +115,7 @@ } _next = NULL; _writeable = writeable; + _internal = internal; } }; --- old/src/share/vm/runtime/atomic.cpp 2019-01-28 17:44:20.000000000 +0800 +++ new/src/share/vm/runtime/atomic.cpp 2019-01-28 17:44:20.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -74,6 +74,16 @@ (jint)compare_value); } +julong Atomic::cmpxchg(julong exchange_value, + volatile julong* dest, julong compare_value) { + return (julong)Atomic::cmpxchg((jlong)exchange_value, (volatile jlong*)dest, + (jlong)compare_value); +} + +julong Atomic::load(volatile julong* src) { + return (julong)load((volatile jlong*)src); +} + jlong Atomic::add(jlong add_value, volatile jlong* dest) { jlong old = load(dest); jlong new_value = old + add_value; @@ -84,6 +94,16 @@ return old; } +julong Atomic::add(julong add_value, volatile julong* dest) { + julong old = load(dest); + julong new_value = old + add_value; + while (old != cmpxchg(new_value, dest, old)) { + old = load(dest); + new_value = old + add_value; + } + return old; +} + void Atomic::inc(volatile short* dest) { // Most platforms do not support atomic increment on a 2-byte value. However, // if the value occupies the most significant 16 bits of an aligned 32-bit --- old/src/share/vm/runtime/atomic.hpp 2019-01-28 17:44:21.000000000 +0800 +++ new/src/share/vm/runtime/atomic.hpp 2019-01-28 17:44:21.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -41,6 +41,7 @@ inline static void store (jint store_value, jint* dest); // See comment above about using jlong atomics on 32-bit platforms inline static void store (jlong store_value, jlong* dest); + inline static void store (julong store_value, julong* dest); inline static void store_ptr(intptr_t store_value, intptr_t* dest); inline static void store_ptr(void* store_value, void* dest); @@ -54,6 +55,7 @@ // See comment above about using jlong atomics on 32-bit platforms inline static jlong load(volatile jlong* src); + static julong load(volatile julong* src); // Atomically add to a location, return updated value inline static jint add (jint add_value, volatile jint* dest); @@ -61,6 +63,7 @@ inline static void* add_ptr(intptr_t add_value, volatile void* dest); // See comment above about using jlong atomics on 32-bit platforms static jlong add (jlong add_value, volatile jlong* dest); + static julong add (julong add_value, volatile julong* dest); // Atomically increment location inline static void inc (volatile jint* dest); @@ -88,6 +91,7 @@ inline static jint cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value); // See comment above about using jlong atomics on 32-bit platforms inline static jlong cmpxchg (jlong exchange_value, volatile jlong* dest, jlong compare_value); + static julong cmpxchg (julong exchange_value, volatile julong* dest, julong compare_value); static unsigned int cmpxchg(unsigned int exchange_value, volatile unsigned int* dest, --- old/src/share/vm/runtime/biasedLocking.cpp 2019-01-28 17:44:22.000000000 +0800 +++ new/src/share/vm/runtime/biasedLocking.cpp 2019-01-28 17:44:22.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -31,6 +31,7 @@ #include "runtime/vframe.hpp" #include "runtime/vmThread.hpp" #include "runtime/vm_operations.hpp" +#include "trace/tracing.hpp" static bool _biased_locking_enabled = false; BiasedLockingCounters BiasedLocking::_counters; @@ -143,7 +144,7 @@ } -static BiasedLocking::Condition revoke_bias(oop obj, bool allow_rebias, bool is_bulk, JavaThread* requesting_thread) { +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 +254,11 @@ } } + // If requested, return information on which thread held the bias + if (EnableJFR && biased_locker != NULL) { + *biased_locker = biased_thread; + } + return BiasedLocking::BIAS_REVOKED; } @@ -373,7 +379,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 +401,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 +451,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 +495,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 = THREAD_TRACE_ID(biased_locker); + } clean_up_cached_monitor_info(); return; } else { @@ -500,6 +513,10 @@ BiasedLocking::Condition status_code() const { return _status_code; } + + traceid biased_locker() const { + return _biased_locker_id; + } }; @@ -609,23 +626,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 +683,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 +699,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-01-28 17:44:24.000000000 +0800 +++ new/src/share/vm/runtime/globals.cpp 2019-01-28 17:44:23.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -92,6 +92,32 @@ *((bool*) _addr) = value; } +bool Flag::is_int() const { + return strcmp(_type, "int") == 0; +} + +int Flag::get_int() const { + return *((int*) _addr); +} + +void Flag::set_int(int value) { + check_writable(); + *((int*) _addr) = value; +} + +bool Flag::is_uint() const { + return strcmp(_type, "uint") == 0; +} + +uint Flag::get_uint() const { + return *((uint*) _addr); +} + +void Flag::set_uint(uint value) { + check_writable(); + *((uint*) _addr) = value; +} + bool Flag::is_intx() const { return strcmp(_type, "intx") == 0; } @@ -131,6 +157,19 @@ *((uint64_t*) _addr) = value; } +bool Flag::is_size_t() const { + return strcmp(_type, "size_t") == 0; +} + +size_t Flag::get_size_t() const { + return *((size_t*) _addr); +} + +void Flag::set_size_t(size_t value) { + check_writable(); + *((size_t*) _addr) = value; +} + bool Flag::is_double() const { return strcmp(_type, "double") == 0; } @@ -610,8 +649,8 @@ { E e; e.set_name(name); - e.set_old_value(old_value); - e.set_new_value(new_value); + e.set_oldValue(old_value); + e.set_newValue(new_value); e.set_origin(origin); e.commit(); } @@ -672,8 +711,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(); --- old/src/share/vm/runtime/globals.hpp 2019-01-28 17:44:25.000000000 +0800 +++ new/src/share/vm/runtime/globals.hpp 2019-01-28 17:44:24.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -264,6 +264,14 @@ bool get_bool() const; void set_bool(bool value); + bool is_int() const; + int get_int() const; + void set_int(int value); + + bool is_uint() const; + uint get_uint() const; + void set_uint(uint value); + bool is_intx() const; intx get_intx() const; void set_intx(intx value); @@ -276,6 +284,10 @@ uint64_t get_uint64_t() const; void set_uint64_t(uint64_t value); + bool is_size_t() const; + size_t get_size_t() const; + void set_size_t(size_t value); + bool is_double() const; double get_double() const; void set_double(double value); @@ -375,8 +387,8 @@ 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); } @@ -406,6 +418,12 @@ static void verify() PRODUCT_RETURN; }; +#if INCLUDE_TRACE +#define TRACE_ONLY(code) code +#else +#define TRACE_ONLY(code) +#endif + // use this for flags that are true by default in the debug version but // false in the optimized version, and vice versa #ifdef ASSERT @@ -3990,7 +4008,24 @@ \ product_pd(bool, PreserveFramePointer, \ "Use the FP register for holding the frame pointer " \ - "and not as a general purpose register.") + "and not as a general purpose register.") \ + \ + TRACE_ONLY(product(bool, FlightRecorder, false, \ + "Enable Flight Recorder")) \ + \ + TRACE_ONLY(product(ccstr, FlightRecorderOptions, NULL, \ + "Flight Recorder options")) \ + \ + TRACE_ONLY(product(ccstr, StartFlightRecording, NULL, \ + "Start flight recording with options")) \ + \ + experimental(bool, UseFastUnorderedTimeStamps, false, \ + "Use platform unstable time where supported for timestamps only") \ + \ + product(bool, PrintJFRLog, false, \ + "Print JFR log ") \ + \ + product(bool, EnableJFR, false, "Enable JFR feature") \ /* * Macros for factoring of globals --- old/src/share/vm/runtime/handles.cpp 2019-01-28 17:44:26.000000000 +0800 +++ new/src/share/vm/runtime/handles.cpp 2019-01-28 17:44:26.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -71,8 +71,9 @@ // is not yet valid, so loosen the assertion while (bottom < top) { // This test can be moved up but for now check every oop. - - assert((*bottom)->is_oop(), "handle should point to oop"); + if (!EnableJFR) { + assert((*bottom)->is_oop(), "handle should point to oop"); + } f->do_oop(bottom++); } --- old/src/share/vm/runtime/java.cpp 2019-01-28 17:44:27.000000000 +0800 +++ new/src/share/vm/runtime/java.cpp 2019-01-28 17:44:27.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -520,13 +520,14 @@ JvmtiExport::post_thread_end(thread); } - EventThreadEnd event; if (event.should_commit()) { - event.set_javalangthread(java_lang_Thread::thread_id(thread->threadObj())); + event.set_thread(THREAD_TRACE_ID(thread)); event.commit(); } + TRACE_VM_EXIT(); + // Always call even when there are not JVMTI environments yet, since environments // may be attached late and JVMTI must track phases of VM execution JvmtiExport::post_vm_death(); --- old/src/share/vm/runtime/javaCalls.hpp 2019-01-28 17:44:28.000000000 +0800 +++ new/src/share/vm/runtime/javaCalls.hpp 2019-01-28 17:44:28.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -138,6 +138,9 @@ inline void push_oop(Handle h) { _is_oop[_size] = true; JNITypes::put_obj((oop)h.raw_value(), _value, _size); } + inline void push_jobject(jobject h) { _is_oop[_size] = true; + JNITypes::put_obj((oop)h, _value, _size); } + inline void push_int(int i) { _is_oop[_size] = false; JNITypes::put_int(i, _value, _size); } --- old/src/share/vm/runtime/jniHandles.cpp 2019-01-28 17:44:29.000000000 +0800 +++ new/src/share/vm/runtime/jniHandles.cpp 2019-01-28 17:44:29.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -413,6 +413,7 @@ * is placed here so that we don't need to add it to each of the collectors. */ JvmtiExport::weak_oops_do(is_alive, f); + TRACE_WEAK_OOPS_DO(is_alive, f); } --- old/src/share/vm/runtime/objectMonitor.cpp 2019-01-28 17:44:30.000000000 +0800 +++ new/src/share/vm/runtime/objectMonitor.cpp 2019-01-28 17:44:30.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -465,8 +465,8 @@ } if (event.should_commit()) { - event.set_klass(((oop)this->object())->klass()); - event.set_previousOwner((TYPE_JAVALANGTHREAD)_previous_owner_tid); + event.set_monitorClass(((oop)this->object())->klass()); + event.set_previousOwner((TYPE_THREAD)_previous_owner_tid); event.set_address((TYPE_ADDRESS)(uintptr_t)(this->object_addr())); event.commit(); } @@ -994,7 +994,7 @@ // get the owner's thread id for the MonitorEnter event // if it is enabled and the thread isn't suspended if (not_suspended && Tracing::is_event_enabled(TraceJavaMonitorEnterEvent)) { - _previous_owner_tid = SharedRuntime::get_java_tid(Self); + _previous_owner_tid = THREAD_TRACE_ID(Self); } #endif @@ -1447,11 +1447,12 @@ jlong notifier_tid, jlong timeout, bool timedout) { - event->set_klass(((oop)this->object())->klass()); - event->set_timeout((TYPE_ULONG)timeout); - event->set_address((TYPE_ADDRESS)(uintptr_t)(this->object_addr())); - event->set_notifier((TYPE_OSTHREAD)notifier_tid); - event->set_timedOut((TYPE_BOOLEAN)timedout); + assert(event != NULL, "invariant"); + event->set_monitorClass(((oop)this->object())->klass()); + event->set_timeout(timeout); + event->set_address((TYPE_ADDRESS)this->object_addr()); + event->set_notifier(notifier_tid); + event->set_timedOut(timedout); event->commit(); } @@ -1716,7 +1717,7 @@ } iterator->_notified = 1 ; Thread * Self = THREAD; - iterator->_notifier_tid = Self->osthread()->thread_id(); + iterator->_notifier_tid = THREAD_TRACE_ID(Self); ObjectWaiter * List = _EntryList ; if (List != NULL) { @@ -1842,7 +1843,7 @@ guarantee (iterator->_notified == 0, "invariant") ; iterator->_notified = 1 ; Thread * Self = THREAD; - iterator->_notifier_tid = Self->osthread()->thread_id(); + iterator->_notifier_tid = THREAD_TRACE_ID(Self); if (Policy != 4) { iterator->TState = ObjectWaiter::TS_ENTER ; } --- old/src/share/vm/runtime/orderAccess.hpp 2019-01-28 17:44:32.000000000 +0800 +++ new/src/share/vm/runtime/orderAccess.hpp 2019-01-28 17:44:31.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -261,8 +261,11 @@ static julong load_acquire(volatile julong* p); static jfloat load_acquire(volatile jfloat* p); static jdouble load_acquire(volatile jdouble* p); + static bool load_acquire(const volatile bool* p); + static julong load_acquire(const volatile julong* p); static intptr_t load_ptr_acquire(volatile intptr_t* p); + static uintptr_t load_ptr_acquire(const volatile uintptr_t* p); static void* load_ptr_acquire(volatile void* p); static void* load_ptr_acquire(const volatile void* p); --- old/src/share/vm/runtime/os.hpp 2019-01-28 17:44:33.000000000 +0800 +++ new/src/share/vm/runtime/os.hpp 2019-01-28 17:44:32.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -556,6 +556,7 @@ //File i/o operations static size_t read(int fd, void *buf, unsigned int nBytes); + static size_t read_at(int fd, void *buf, unsigned int nBytes, jlong offset); static size_t restartable_read(int fd, void *buf, unsigned int nBytes); static size_t write(int fd, const void *buf, unsigned int nBytes); @@ -605,6 +606,16 @@ // Unload library static void dll_unload(void *lib); + // Callback for loaded module information + // Input parameters: + // char* module_file_name, + // address module_base_addr, + // address module_top_addr, + // void* param + typedef int (*LoadedModulesCallbackFunc)(const char *, address, address, void *); + + static int get_loaded_modules_info(LoadedModulesCallbackFunc callback, void *param); + // Return the handle of this process static void* get_default_process_handle(); --- old/src/share/vm/runtime/safepoint.cpp 2019-01-28 17:44:34.000000000 +0800 +++ new/src/share/vm/runtime/safepoint.cpp 2019-01-28 17:44:34.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -97,6 +97,7 @@ // Roll all threads forward to a safepoint and suspend them all void SafepointSynchronize::begin() { + EventSafepointBegin begin_event; Thread* myThread = Thread::current(); assert(myThread->is_VM_thread(), "Only VM thread may execute a safepoint"); @@ -189,6 +190,8 @@ // between states, the safepointing code will wait for the thread to // block itself when it attempts transitions to a new state. // + EventSafepointStateSynchronization sync_event; + int initial_running = 0; _state = _synchronizing; OrderAccess::fence(); @@ -243,8 +246,11 @@ } } - if (PrintSafepointStatistics && iterations == 0) { - begin_statistics(nof_threads, still_running); + if (iterations == 0) { + initial_running = still_running; + if (PrintSafepointStatistics) { + begin_statistics(nof_threads, still_running); + } } if (still_running > 0) { @@ -336,6 +342,17 @@ update_statistics_on_spin_end(); } + if (sync_event.should_commit()) { + // Group this event together with the ones committed after the counter is increased + sync_event.set_safepointId(safepoint_counter() + 1); + sync_event.set_initialThreadCount(initial_running); + sync_event.set_runningThreadCount(_waiting_to_block); + sync_event.set_iterations(iterations); + sync_event.commit(); + } + + EventSafepointWaitBlocked wait_blocked_event; + int initial_waiting_to_block = _waiting_to_block; // wait until all threads are stopped while (_waiting_to_block > 0) { if (TraceSafepoint) tty->print_cr("Waiting for %d thread(s) to block", _waiting_to_block); @@ -374,6 +391,12 @@ OrderAccess::fence(); + if (wait_blocked_event.should_commit()) { + wait_blocked_event.set_safepointId(safepoint_counter()); + wait_blocked_event.set_runningThreadCount(initial_waiting_to_block); + wait_blocked_event.commit(); + } + #ifdef ASSERT for (JavaThread *cur = Threads::first(); cur != NULL; cur = cur->next()) { // make sure all the threads were visited @@ -395,17 +418,32 @@ } // Call stuff that needs to be run when a safepoint is just about to be completed + EventSafepointCleanup cleanup_event; do_cleanup_tasks(); + if (cleanup_event.should_commit()) { + cleanup_event.set_safepointId(safepoint_counter()); + cleanup_event.commit(); + } + if (PrintSafepointStatistics) { // Record how much time spend on the above cleanup tasks update_statistics_on_cleanup_end(os::javaTimeNanos()); } + + if (begin_event.should_commit()) { + begin_event.set_safepointId(safepoint_counter()); + begin_event.set_totalThreadCount(nof_threads); + begin_event.set_jniCriticalThreadCount(_current_jni_active_count); + begin_event.commit(); + } } // Wake up all threads, so they are ready to resume execution after the safepoint // operation has been carried out void SafepointSynchronize::end() { + EventSafepointEnd event; + int safepoint_id = safepoint_counter(); // Keep the odd counter as "id" assert(Threads_lock->owned_by_self(), "must hold Threads_lock"); assert((_safepoint_counter & 0x1) == 1, "must be odd"); @@ -494,6 +532,11 @@ // record this time so VMThread can keep track how much time has elasped // since last safepoint. _end_of_last_safepoint = os::javaTimeMillis(); + + if (event.should_commit()) { + event.set_safepointId(safepoint_id); + event.commit(); + } } bool SafepointSynchronize::is_cleanup_needed() { @@ -502,49 +545,79 @@ return false; } - +static void event_safepoint_cleanup_task_commit(EventSafepointCleanupTask& event, const char* name) { + if (event.should_commit()) { + event.set_safepointId(SafepointSynchronize::safepoint_counter()); + event.set_name(name); + event.commit(); + } +} // Various cleaning tasks that should be done periodically at safepoints void SafepointSynchronize::do_cleanup_tasks() { { - TraceTime t1("deflating idle monitors", TraceSafepointCleanupTime); + const char* name = "deflating idle monitors"; + EventSafepointCleanupTask event; + TraceTime t1(name, TraceSafepointCleanupTime); ObjectSynchronizer::deflate_idle_monitors(); + event_safepoint_cleanup_task_commit(event, name); } { - TraceTime t2("updating inline caches", TraceSafepointCleanupTime); + const char* name = "updating inline caches"; + EventSafepointCleanupTask event; + TraceTime t2(name, TraceSafepointCleanupTime); InlineCacheBuffer::update_inline_caches(); + event_safepoint_cleanup_task_commit(event, name); } { - TraceTime t3("compilation policy safepoint handler", TraceSafepointCleanupTime); + const char* name = "compilation policy safepoint handler"; + EventSafepointCleanupTask event; + TraceTime t3(name, TraceSafepointCleanupTime); CompilationPolicy::policy()->do_safepoint_work(); + event_safepoint_cleanup_task_commit(event, name); } { - TraceTime t4("mark nmethods", TraceSafepointCleanupTime); + const char* name = "mark nmethods"; + EventSafepointCleanupTask event; + TraceTime t4(name, TraceSafepointCleanupTime); NMethodSweeper::mark_active_nmethods(); + event_safepoint_cleanup_task_commit(event, name); } if (SymbolTable::needs_rehashing()) { - TraceTime t5("rehashing symbol table", TraceSafepointCleanupTime); + const char* name = "rehashing symbol table"; + EventSafepointCleanupTask event; + TraceTime t5(name, TraceSafepointCleanupTime); SymbolTable::rehash_table(); + event_safepoint_cleanup_task_commit(event, name); } if (StringTable::needs_rehashing()) { - TraceTime t6("rehashing string table", TraceSafepointCleanupTime); + const char* name = "rehashing string table"; + EventSafepointCleanupTask event; + TraceTime t6(name, TraceSafepointCleanupTime); StringTable::rehash_table(); + event_safepoint_cleanup_task_commit(event, name); } // rotate log files? if (UseGCLogFileRotation) { + const char* name = "rotate gc log"; + EventSafepointCleanupTask event; gclog_or_tty->rotate_log(false); + event_safepoint_cleanup_task_commit(event, name); } { // CMS delays purging the CLDG until the beginning of the next safepoint and to // make sure concurrent sweep is done - TraceTime t7("purging class loader data graph", TraceSafepointCleanupTime); + const char* name = "purging class loader data graph"; + EventSafepointCleanupTask event; + TraceTime t7(name, TraceSafepointCleanupTime); ClassLoaderDataGraph::purge_if_needed(); + event_safepoint_cleanup_task_commit(event, name); } } --- old/src/share/vm/runtime/safepoint.hpp 2019-01-28 17:44:35.000000000 +0800 +++ new/src/share/vm/runtime/safepoint.hpp 2019-01-28 17:44:35.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -145,6 +145,7 @@ // Query inline static bool is_at_safepoint() { return _state == _synchronized; } inline static bool is_synchronizing() { return _state == _synchronizing; } + inline static int safepoint_counter() { return _safepoint_counter; } inline static bool do_call_back() { return (_state != _not_synchronized); --- old/src/share/vm/runtime/sweeper.cpp 2019-01-28 17:44:36.000000000 +0800 +++ new/src/share/vm/runtime/sweeper.cpp 2019-01-28 17:44:36.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -396,11 +396,9 @@ if (event.should_commit()) { event.set_starttime(sweep_start_counter); event.set_endtime(sweep_end_counter); - event.set_sweepIndex(_traversals); - event.set_sweepFractionIndex(NmethodSweepFraction - _sweep_fractions_left + 1); + event.set_sweepId(_traversals); event.set_sweptCount(swept_count); event.set_flushedCount(_flushed_count); - event.set_markedCount(_marked_for_reclamation_count); event.set_zombifiedCount(_zombified_count); event.commit(); } --- old/src/share/vm/runtime/synchronizer.cpp 2019-01-28 17:44:37.000000000 +0800 +++ new/src/share/vm/runtime/synchronizer.cpp 2019-01-28 17:44:37.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -1193,6 +1193,19 @@ // Note that we could encounter some performance loss through false-sharing as // multiple locks occupy the same $ line. Padding might be appropriate. +static void post_monitor_inflate_event(EventJavaMonitorInflate& event, + const oop obj, + //const ObjectSynchronizer::InflateCause cause) { + // unsupport cause + const u1 cause) { +#if INCLUDE_TRACE + assert(event.should_commit(), "check outside"); + event.set_monitorClass(obj->klass()); + event.set_address((TYPE_ADDRESS)(uintptr_t)(void*)obj); + event.set_cause((u1)cause); + event.commit(); +#endif +} ObjectMonitor * ATTR ObjectSynchronizer::inflate (Thread * Self, oop object) { // Inflate mutates the heap ... @@ -1200,6 +1213,8 @@ assert (Universe::verify_in_progress() || !SafepointSynchronize::is_at_safepoint(), "invariant") ; + EventJavaMonitorInflate event; + for (;;) { const markOop mark = object->mark() ; assert (!mark->has_bias_pattern(), "invariant") ; @@ -1330,6 +1345,9 @@ object->klass()->external_name()); } } + if (event.should_commit()) { + post_monitor_inflate_event(event, object, (u1)0); + } return m ; } @@ -1380,6 +1398,9 @@ object->klass()->external_name()); } } + if (event.should_commit()) { + post_monitor_inflate_event(event, object, (u1)0); + } return m ; } } --- old/src/share/vm/runtime/thread.cpp 2019-01-28 17:44:38.000000000 +0800 +++ new/src/share/vm/runtime/thread.cpp 2019-01-28 17:44:38.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -1670,7 +1670,7 @@ EventThreadStart event; if (event.should_commit()) { - event.set_javalangthread(java_lang_Thread::thread_id(this->threadObj())); + event.set_thread(THREAD_TRACE_ID(this)); event.commit(); } @@ -1804,7 +1804,7 @@ // from java_lang_Thread object EventThreadEnd event; if (event.should_commit()) { - event.set_javalangthread(java_lang_Thread::thread_id(this->threadObj())); + event.set_thread(THREAD_TRACE_ID(this)); event.commit(); } @@ -2185,6 +2185,11 @@ if (check_asyncs) { check_and_handle_async_exceptions(); } +#if INCLUDE_TRACE + if (is_trace_suspend()) { + TRACE_SUSPEND_THREAD(this); + } +#endif } void JavaThread::send_thread_stop(oop java_throwable) { @@ -2423,6 +2428,11 @@ fatal("missed deoptimization!"); } } +#if INCLUDE_TRACE + if (thread->is_trace_suspend()) { + TRACE_SUSPEND_THREAD(thread); + } +#endif } // Slow path when the native==>VM/Java barriers detect a safepoint is in --- old/src/share/vm/runtime/thread.hpp 2019-01-28 17:44:39.000000000 +0800 +++ new/src/share/vm/runtime/thread.hpp 2019-01-28 17:44:39.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -195,7 +195,8 @@ _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 + _trace_flag = 0x00000004U // call tracing backend }; // various suspension related flags - atomically updated @@ -260,7 +261,7 @@ // Thread-local buffer used by MetadataOnStackMark. MetadataOnStackBuffer* _metadata_on_stack_buffer; - TRACE_DATA _trace_data; // Thread-local data for tracing + mutable TRACE_DATA _trace_data; // Thread-local data for tracing ThreadExt _ext; @@ -383,6 +384,14 @@ clear_suspend_flag(_critical_native_unlock); } + void set_trace_flag() { + set_suspend_flag(_trace_flag); + } + + void clear_trace_flag() { + clear_suspend_flag(_trace_flag); + } + // Support for Unhandled Oop detection #ifdef CHECK_UNHANDLED_OOPS private: @@ -441,7 +450,9 @@ void incr_allocated_bytes(jlong size) { _allocated_bytes += size; } inline jlong cooked_allocated_bytes(); - TRACE_DATA* trace_data() { return &_trace_data; } + TRACE_DEFINE_THREAD_TRACE_DATA_OFFSET; + TRACE_DATA* trace_data() const { return &_trace_data; } + bool is_trace_suspend() { return (_suspend_flags & _trace_flag) != 0; } const ThreadExt& ext() const { return _ext; } ThreadExt& ext() { return _ext; } --- old/src/share/vm/runtime/timer.cpp 2019-01-28 17:44:41.000000000 +0800 +++ new/src/share/vm/runtime/timer.cpp 2019-01-28 17:44:40.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -48,6 +48,12 @@ return counter/freq; } +double TimeHelper::counter_to_milliseconds(jlong counter) { + double count = (double) counter; + double freq = (double) os::elapsed_frequency() / (double) 1000; + return counter/freq; +} + void elapsedTimer::add(elapsedTimer t) { _counter += t._counter; } --- old/src/share/vm/runtime/timer.hpp 2019-01-28 17:44:42.000000000 +0800 +++ new/src/share/vm/runtime/timer.hpp 2019-01-28 17:44:42.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -123,6 +123,7 @@ class TimeHelper { public: static double counter_to_seconds(jlong counter); + static double counter_to_milliseconds(jlong counter); }; #endif // SHARE_VM_RUNTIME_TIMER_HPP --- old/src/share/vm/runtime/vmThread.cpp 2019-01-28 17:44:43.000000000 +0800 +++ new/src/share/vm/runtime/vmThread.cpp 2019-01-28 17:44:43.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -378,13 +378,15 @@ if (event.should_commit()) { bool is_concurrent = op->evaluate_concurrently(); + bool evaluate_at_safepoint = op->evaluate_at_safepoint(); event.set_operation(op->type()); - event.set_safepoint(op->evaluate_at_safepoint()); + event.set_safepoint(evaluate_at_safepoint); event.set_blocking(!is_concurrent); // Only write caller thread information for non-concurrent vm operations. // For concurrent vm operations, the thread id is set to 0 indicating thread is unknown. // This is because the caller thread could have exited already. - event.set_caller(is_concurrent ? 0 : op->calling_thread()->osthread()->thread_id()); + event.set_caller(is_concurrent ? 0 : THREAD_TRACE_ID(op->calling_thread())); + event.set_safepointId(evaluate_at_safepoint ? SafepointSynchronize::safepoint_counter() : 0); event.commit(); } --- old/src/share/vm/services/diagnosticArgument.cpp 2019-01-28 17:44:44.000000000 +0800 +++ new/src/share/vm/services/diagnosticArgument.cpp 2019-01-28 17:44:44.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -280,7 +280,7 @@ size_t len, TRAPS) { if (str == NULL) { THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), - "Integer parsing error nanotime value: syntax error"); + "Parsing error memory size value: syntax error, value is null\n"); } if (*str == '-') { --- old/src/share/vm/trace/noTraceBackend.hpp 2019-01-28 17:44:45.000000000 +0800 +++ new/src/share/vm/trace/noTraceBackend.hpp 2019-01-28 17:44:45.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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 @@ -25,18 +25,19 @@ #define SHARE_VM_TRACE_NOTRACEBACKEND_HPP #include "prims/jni.h" -#include "trace/traceTime.hpp" + +typedef jlong JfrTraceTime; class NoTraceBackend { public: - static TracingTime time() { + static JfrTraceTime time() { return 0; } }; class TraceThreadData { -public: - TraceThreadData() {} + public: + TraceThreadData() {} }; typedef NoTraceBackend Tracing; --- old/src/share/vm/trace/trace.dtd 2019-01-28 17:44:46.000000000 +0800 +++ new/src/share/vm/trace/trace.dtd 2019-01-28 17:44:46.000000000 +0800 @@ -1,6 +1,6 @@ - + @@ -64,23 +64,29 @@ has_stacktrace CDATA "false" is_instant CDATA "false" is_constant CDATA "false" - is_requestable CDATA "false"> + is_requestable CDATA "false" + experimental CDATA "false" + cutoff CDATA "false"> + transition CDATA "NONE" + experimental CDATA "false"> + description CDATA #IMPLIED + experimental CDATA "false"> + description CDATA #IMPLIED + experimental CDATA "false"> + description CDATA #IMPLIED + experimental CDATA "false"> --- old/src/share/vm/trace/trace.xml 2019-01-28 17:44:47.000000000 +0800 +++ new/src/share/vm/trace/trace.xml 2019-01-28 17:44:47.000000000 +0800 @@ -1,6 +1,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + --- old/src/share/vm/trace/traceBackend.hpp 2019-01-28 17:44:48.000000000 +0800 +++ new/src/share/vm/trace/traceBackend.hpp 2019-01-28 17:44:48.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,39 +23,18 @@ */ #ifndef SHARE_VM_TRACE_TRACEBACKEND_HPP #define SHARE_VM_TRACE_TRACEBACKEND_HPP - #include "utilities/macros.hpp" #if INCLUDE_TRACE -#include "runtime/globals.hpp" -#include "runtime/os.hpp" -#include "trace/traceTime.hpp" -#include "tracefiles/traceEventIds.hpp" - -class TraceBackend { -public: - static bool enabled(void) { - return EnableTracing; - } - - static bool is_event_enabled(TraceEventId id) { - return enabled(); - } - - static TracingTime time() { - return os::elapsed_counter(); - } - - static void on_unloading_classes(void) { - } -}; - -class TraceThreadData { -public: - TraceThreadData() {} -}; - -typedef TraceBackend Tracing; - +#include "jfr/jfr.hpp" +#include "jfr/instrumentation/jfrEventClassTransformer.hpp" +#include "jfr/leakprofiler/leakProfiler.hpp" +#include "jfr/leakprofiler/utilities/objectSampleAssistance.hpp" +#include "jfr/recorder/access/jfrbackend.hpp" +#include "jfr/recorder/access/jfrFlush.hpp" +#include "jfr/recorder/access/jfrStackTraceMark.hpp" +#include "jfr/recorder/access/jfrThreadData.hpp" +#include "jfr/recorder/checkpoint/constant/traceid/jfrTraceId.hpp" +typedef JfrBackend Tracing; #else // !INCLUDE_TRACE #include "trace/noTraceBackend.hpp" #endif // INCLUDE_TRACE --- old/src/share/vm/trace/traceDataTypes.hpp 2019-01-28 17:44:49.000000000 +0800 +++ new/src/share/vm/trace/traceDataTypes.hpp 2019-01-28 17:44:49.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,42 +28,13 @@ #include #include "utilities/globalDefinitions.hpp" +#include "utilities/ticks.hpp" -enum { - CONTENT_TYPE_NONE = 0, - CONTENT_TYPE_BYTES = 1, - CONTENT_TYPE_EPOCHMILLIS = 2, - CONTENT_TYPE_MILLIS = 3, - CONTENT_TYPE_NANOS = 4, - CONTENT_TYPE_TICKS = 5, - CONTENT_TYPE_ADDRESS = 6, - - CONTENT_TYPE_OSTHREAD, - CONTENT_TYPE_JAVALANGTHREAD, - CONTENT_TYPE_STACKTRACE, - CONTENT_TYPE_CLASS, - CONTENT_TYPE_PERCENTAGE, - - JVM_CONTENT_TYPES_START = 30, - JVM_CONTENT_TYPES_END = 100 -}; - -enum ReservedEvent { - EVENT_PRODUCERS, - EVENT_CHECKPOINT, - EVENT_BUFFERLOST, - - NUM_RESERVED_EVENTS -}; - -typedef enum ReservedEvent ReservedEvent; - -typedef u8 classid; -typedef u8 stacktraceid; -typedef u8 methodid; -typedef u8 fieldid; +typedef u8 traceid; -class TraceUnicodeString; +class ClassLoaderData; +class Klass; +class Method; +class Symbol; #endif // SHARE_VM_TRACE_TRACEDATATYPES_HPP - --- old/src/share/vm/trace/traceEvent.hpp 2019-01-28 17:44:50.000000000 +0800 +++ new/src/share/vm/trace/traceEvent.hpp 2019-01-28 17:44:50.000000000 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,7 @@ #ifndef SHARE_VM_TRACE_TRACEEVENT_HPP #define SHARE_VM_TRACE_TRACEEVENT_HPP +#include "jfr/utilities/jfrTraceTime.hpp" #include "utilities/macros.hpp" enum EventStartTime { @@ -34,123 +35,93 @@ #if INCLUDE_TRACE #include "trace/traceBackend.hpp" -#include "trace/tracing.hpp" #include "tracefiles/traceEventIds.hpp" -#include "tracefiles/traceTypes.hpp" #include "utilities/ticks.hpp" template -class TraceEvent : public StackObj { +class TraceEvent { private: bool _started; -#ifdef ASSERT - bool _committed; - bool _cancelled; - protected: - bool _ignore_check; -#endif protected: jlong _startTime; jlong _endTime; - - void set_starttime(const TracingTime& time) { + DEBUG_ONLY(bool _committed;) + void set_starttime(const JfrTraceTime& time) { _startTime = time; } - void set_endtime(const TracingTime& time) { + void set_endtime(const JfrTraceTime& time) { _endTime = time; } - public: TraceEvent(EventStartTime timing=TIMED) : _startTime(0), _endTime(0), _started(false) #ifdef ASSERT - , - _committed(false), - _cancelled(false), - _ignore_check(false) + , _committed(false) #endif { if (T::is_enabled()) { _started = true; - if (timing == TIMED && !T::isInstant) { - static_cast(this)->set_starttime(Tracing::time()); + if (TIMED == timing && !T::isInstant) { + static_cast(this)->set_starttime(Tracing::time()); } } } + public: + void set_starttime(const Ticks& time) { + _startTime = time.value(); + } + + void set_endtime(const Ticks& time) { + _endTime = time.value(); + } static bool is_enabled() { - return Tracing::is_event_enabled(T::eventId); + return EnableJFR && Tracing::is_event_enabled(T::eventId); } bool should_commit() { return _started; } - void ignoreCheck() { - DEBUG_ONLY(_ignore_check = true); - } - void commit() { if (!should_commit()) { - cancel(); - return; + return; } - if (_endTime == 0) { + assert(!_committed, "event already committed"); + if (_startTime == 0) { + static_cast(this)->set_starttime(Tracing::time()); + } else if (_endTime == 0) { static_cast(this)->set_endtime(Tracing::time()); } if (static_cast(this)->should_write()) { static_cast(this)->writeEvent(); + DEBUG_ONLY(_committed = true;) } - set_commited(); } - void set_starttime(const Ticks& time) { - _startTime = time.value(); - } - - void set_endtime(const Ticks& time) { - _endTime = time.value(); - } - - TraceEventId id() const { + static TraceEventId id() { return T::eventId; } - bool is_instant() const { + static bool is_instant() { return T::isInstant; } - bool is_requestable() const { + static bool is_requestable() { return T::isRequestable; } - bool has_thread() const { + static bool has_thread() { return T::hasThread; } - bool has_stacktrace() const { + static bool has_stacktrace() { return T::hasStackTrace; } - - void cancel() { - assert(!_committed && !_cancelled, "event was already committed/cancelled"); - DEBUG_ONLY(_cancelled = true); - } - - void set_commited() { - assert(!_committed, "event has already been committed"); - DEBUG_ONLY(_committed = true); - } - - ~TraceEvent() { - if (_started) { - assert(_ignore_check || _committed || _cancelled, "event was not committed/cancelled"); - } - } }; #endif // INCLUDE_TRACE --- old/src/share/vm/trace/traceEventClasses.xsl 2019-01-28 17:44:51.000000000 +0800 +++ new/src/share/vm/trace/traceEventClasses.xsl 2019-01-28 17:44:51.000000000 +0800 @@ -1,6 +1,6 @@ + + ( + + + , + ) : JfrTraceEvent<>(TIMED) { + if (should_commit()) { + set_(); + } } - - - - void writeEvent(void) { - ResourceMark rm; - if (UseLockedTracing) { - ttyLocker lock; - writeEventContent(); - } else { - writeEventContent(); + void commit( + + , + ) { + if (should_commit()) { + set_(); + commit(); + } + } + + static void commit(const Ticks& startTicks, + const Ticks& endTicks, + + + + , + ) { + me(UNTIMED); + + if (me.should_commit()) { + me.set_starttime(startTicks); + me.set_endtime(endTicks); + + me.set_(); + me.commit(); } } -}; +#ifdef ASSERT + void verify() const { + + } +#endif +}; + + - + @@ -160,7 +209,7 @@ - + @@ -168,43 +217,44 @@ -#if INCLUDE_TRACE - -#else - -#endif + + + + + + + + -#if INCLUDE_TRACE - -#else - -#endif + + + + + + + - + + - + - + - - - - - - - + - + + @@ -220,34 +270,71 @@ - + + + + + } + + + + + + + - - - - - - - - - - - - + + - ts.print(", "); - - + + + + + + + + + + + + - ts.print(", "); + + + + + + + + + + + + + + + + + + + + + + + + + + + + --- old/src/share/vm/trace/traceEventIds.xsl 2019-01-28 17:44:52.000000000 +0800 +++ new/src/share/vm/trace/traceEventIds.xsl 2019-01-28 17:44:52.000000000 +0800 @@ -1,6 +1,6 @@ - - - - - - - - - - - - + type="U8" builtin_type="THREAD"> + + + + - + - + - - + - + - + + + + + - + - - + + - - + - + type="U8" jvm_type="THREADSTATE"> + - + type="U8" jvm_type="GCNAME"> + - + type="U8" jvm_type="GCCAUSE"> + - + type="U8" jvm_type="GCWHEN"> + + + + + - + type="U8" jvm_type="G1YCTYPE"> + - - + + - + type="U8" jvm_type="REFERENCETYPE"> + - + type="U8" jvm_type="METADATATYPE"> + - + type="U8" jvm_type="METASPACEOBJTYPE"> + - - + + - + type="U8" jvm_type="VMOPERATIONTYPE"> + - + type="U8" jvm_type="COMPILERPHASETYPE"> + - + type="U8" jvm_type="FLAGVALUEORIGIN"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -231,11 +284,11 @@ type="bool" sizeop="1"/> - + - + - - - - - + + type="const char*" sizeop="sizeof_utf(%)"/> + type="const Symbol*" sizeop="sizeof(u8)"/> + type="const Klass*" sizeop="sizeof(u8)"/> + + - - - - - - - - - - - - - - + + + + - + - + - + - + - + + + + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + --- old/src/share/vm/trace/xsl_util.xsl 2019-01-28 17:44:58.000000000 +0800 +++ new/src/share/vm/trace/xsl_util.xsl 2019-01-28 17:44:57.000000000 +0800 @@ -1,6 +1,6 @@ + + + + + + + + +#ifndef TRACEFILES_JFR_NATIVE_EVENTSETTING_HPP +#define TRACEFILES_JFR_NATIVE_EVENTSETTING_HPP + +#include "utilities/macros.hpp" +#if INCLUDE_TRACE +#include "tracefiles/traceEventIds.hpp" + +/** + * Event setting. We add some padding so we can use our + * event IDs as indexes into this. + */ + +struct jfrNativeEventSetting { + jlong threshold_ticks; + jlong cutoff_ticks; + u1 stacktrace; + u1 enabled; + u1 pad[6]; // Because GCC on linux ia32 at least tries to pack this. +}; + +union JfrNativeSettings { + // Array version. + jfrNativeEventSetting bits[MaxTraceEventId]; + // Then, to make it easy to debug, + // add named struct members also. + struct { + jfrNativeEventSetting pad[NUM_RESERVED_EVENTS]; + + + + + + + + + } ev; +}; + +#endif // INCLUDE_TRACE +#endif // TRACEFILES_JFR_NATIVE_EVENTSETTING_HPP + + + --- /dev/null 2019-01-28 17:48:46.000000000 +0800 +++ new/src/share/vm/trace/tracePeriodic.xsl 2019-01-28 17:48:45.000000000 +0800 @@ -0,0 +1,64 @@ + + + + + + + + + + +#ifndef JFRFILES_JFRPERIODICEVENTSET_HPP +#define JFRFILES_JFRPERIODICEVENTSET_HPP + +#include "utilities/macros.hpp" +#if INCLUDE_TRACE +#include "memory/allocation.hpp" +#include "tracefiles/traceEventIds.hpp" + +class JfrPeriodicEventSet : public AllStatic { + public: + static void requestEvent(TraceEventId id) { + switch(id) { + + case TraceEvent: + request(); + break; + + default: + break; + } + } + + private: + + static void request(void); + +}; + +#endif // INCLUDE_TRACE +#endif // JFRFILES_JFRPERIODICEVENTSET_HPP + + --- /dev/null 2019-01-28 17:48:46.000000000 +0800 +++ new/src/share/vm/trace/traceStream.cpp 2019-01-28 17:48:46.000000000 +0800 @@ -0,0 +1,93 @@ +/* +* Copyright (c) 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. +* +*/ + +#include "precompiled.hpp" +#include "trace/traceStream.hpp" +#if INCLUDE_TRACE +#include "classfile/classLoaderData.hpp" +#include "classfile/javaClasses.hpp" +#include "memory/resourceArea.hpp" +#include "oops/klass.hpp" +#include "oops/method.hpp" +#include "oops/symbol.hpp" + +void TraceStream::print_val(const char* label, const Klass* val) const { + ResourceMark rm; + const char* description = "NULL"; + if (val != NULL) { + const Symbol* name = val->name(); + if (name != NULL) { + description = name->as_C_string(); + } + } + tty->print("%s = %s", label, description); +} + +void TraceStream::print_val(const char* label, const Method* val) const { + ResourceMark rm; + const char* description = "NULL"; + if (val != NULL) { + description = val->name_and_sig_as_C_string(); + } + tty->print("%s = %s", label, description); +} + +void TraceStream::print_val(const char* label, const ClassLoaderData* cld) const { +/* guangyu.zgy not implemented yet + ResourceMark rm; + if (cld == NULL || cld->is_anonymous()) { + tty->print("%s = NULL", label); + return; + } + const char* class_loader_name = "NULL"; + const char* class_loader_type_name = "NULL"; + const oop class_loader_oop = cld->class_loader(); + + if (class_loader_oop != NULL) { + const Klass* k = class_loader_oop->klass(); + assert(k != NULL, "invariant"); + const Symbol* klass_name_sym = k->name(); + if (klass_name_sym != NULL) { + class_loader_type_name = klass_name_sym->as_C_string(); + } + const oop class_loader_name_oop = + java_lang_ClassLoader::name(class_loader_oop); + if (class_loader_name_oop != NULL) { + const char* class_loader_name_from_oop = + java_lang_String::as_utf8_string(class_loader_name_oop); + if (class_loader_name_from_oop != NULL && + class_loader_name_from_oop[0] != '\0') { + class_loader_name = class_loader_name_from_oop; + } + } + } else { + assert(class_loader_oop == NULL, "invariant"); + // anonymous CLDs are excluded, this would be the boot loader + class_loader_name = "boot"; + } + tty->print("%s = name=%s class=%s", label, class_loader_name, class_loader_type_name); +*/ +} + +#endif // INCLUDE_TRACE --- /dev/null 2019-01-28 17:48:47.000000000 +0800 +++ new/src/share/vm/trace/traceVM.hpp 2019-01-28 17:48:47.000000000 +0800 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2011, 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_VM_TRACE_TRACEVM_HPP +#define SHARE_VM_TRACE_TRACEVM_HPP + +#include "utilities/macros.hpp" +#if INCLUDE_TRACE +#include "jvmtifiles/jvmti.h" + +/** The possible java.lang.Thread states a thread can have "in java". */ + +#define MAKE_JAVATHREAD_STATES(state) \ + state(STATE_NEW, (u8)0) /* Only implicitely defined in JVMTI */ \ + state(STATE_RUNNABLE, (u8)(JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_RUNNABLE)) \ + state(STATE_SLEEPING, (u8)(JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT | JVMTI_THREAD_STATE_SLEEPING)) \ + state(STATE_IN_OBJECT_WAIT, (u8)(JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_INDEFINITELY | JVMTI_THREAD_STATE_IN_OBJECT_WAIT)) \ + state(STATE_IN_OBJECT_WAIT_TIMED, (u8)(JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT | JVMTI_THREAD_STATE_IN_OBJECT_WAIT)) \ + state(STATE_PARKED, (u8)(JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_INDEFINITELY | JVMTI_THREAD_STATE_PARKED)) \ + state(STATE_PARKED_TIMED, (u8)(JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT | JVMTI_THREAD_STATE_PARKED)) \ + state(STATE_BLOCKED_ON_MONITOR_ENTER, (u8)(JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER)) \ + state(STATE_TERMINATED, (u8)(JVMTI_THREAD_STATE_TERMINATED)) + +typedef enum { +#define _ts_enum(n, v) THREAD_##n = v, + MAKE_JAVATHREAD_STATES(_ts_enum) + LAST_JavaLangThreadState +} JavaLangThreadState; + +#endif // INCLUDE_TRACE +#endif // SHARE_VM_TRACE_TRACEVM_HPP --- /dev/null 2019-01-28 17:48:48.000000000 +0800 +++ new/src/share/vm/trace/traceevents.xml 2019-01-28 17:48:48.000000000 +0800 @@ -0,0 +1,959 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + --- /dev/null 2019-01-28 17:48:49.000000000 +0800 +++ new/src/share/vm/trace/tracerelationdecls.xml 2019-01-28 17:48:49.000000000 +0800 @@ -0,0 +1,35 @@ + + + + + + + + + + + + + --- /dev/null 2019-01-28 17:48:50.000000000 +0800 +++ new/src/share/vm/utilities/bytes.hpp 2019-01-28 17:48:50.000000000 +0800 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2014, 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_VM_UTILITIES_BYTES_HPP +#define SHARE_VM_UTILITIES_BYTES_HPP + +#include "utilities/macros.hpp" + +class Endian : AllStatic { +public: + enum Order { + LITTLE, + BIG, + JAVA = BIG, + NATIVE = +#ifdef VM_LITTLE_ENDIAN + LITTLE +#else + BIG +#endif + }; + + // Returns true, if the byte ordering used by Java is different from + // the native byte ordering of the underlying machine. + static inline bool is_Java_byte_ordering_different() { + return NATIVE != JAVA; + } +}; + +//#include CPU_HEADER(bytes) + +#endif // SHARE_VM_UTILITIES_BYTES_HPP --- /dev/null 2019-01-28 17:48:51.000000000 +0800 +++ new/src/share/vm/utilities/compilerWarnings.hpp 2019-01-28 17:48:51.000000000 +0800 @@ -0,0 +1,87 @@ +/* + * Copyright (c) 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_VM_UTILITIES_COMPILERWARNINGS_HPP +#define SHARE_VM_UTILITIES_COMPILERWARNINGS_HPP + +// Macros related to control of compiler warnings. + +// We presently only have interesting macros here for gcc and variants, +// so it's not worth going through the COMPILER_HEADER() dispatch, with +// all the non-gcc files being empty. +#ifdef TARGET_COMPILER_gcc + +// Diagnostic pragmas like the ones defined below in PRAGMA_FORMAT_NONLITERAL_IGNORED +// were only introduced in GCC 4.2. Because we have no other possibility to ignore +// these warnings for older versions of GCC, we simply don't decorate our printf-style +// functions with __attribute__(format) in that case. +#if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 2)) || (__GNUC__ > 4) +#ifndef ATTRIBUTE_PRINTF +#define ATTRIBUTE_PRINTF(fmt,vargs) __attribute__((format(printf, fmt, vargs))) +#endif +#ifndef ATTRIBUTE_SCANF +#define ATTRIBUTE_SCANF(fmt,vargs) __attribute__((format(scanf, fmt, vargs))) +#endif +#endif // gcc version check + +#define PRAGMA_FORMAT_NONLITERAL_IGNORED _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"") \ + _Pragma("GCC diagnostic ignored \"-Wformat-security\"") +#define PRAGMA_FORMAT_IGNORED _Pragma("GCC diagnostic ignored \"-Wformat\"") + +#if defined(__clang_major__) && \ + (__clang_major__ >= 4 || \ + (__clang_major__ >= 3 && __clang_minor__ >= 1)) || \ + ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ > 4) +// Tested to work with clang version 3.1 and better. +#define PRAGMA_DIAG_PUSH _Pragma("GCC diagnostic push") +#define PRAGMA_DIAG_POP _Pragma("GCC diagnostic pop") + +#endif // clang/gcc version check + +#endif // TARGET_COMPILER_gcc + +// Defaults when not defined for the TARGET_COMPILER_xxx. + +#ifndef ATTRIBUTE_PRINTF +#define ATTRIBUTE_PRINTF(fmt, vargs) +#endif +#ifndef ATTRIBUTE_SCANF +#define ATTRIBUTE_SCANF(fmt, vargs) +#endif + +#ifndef PRAGMA_FORMAT_NONLITERAL_IGNORED +#define PRAGMA_FORMAT_NONLITERAL_IGNORED +#endif +#ifndef PRAGMA_FORMAT_IGNORED +#define PRAGMA_FORMAT_IGNORED +#endif + +#ifndef PRAGMA_DIAG_PUSH +#define PRAGMA_DIAG_PUSH +#endif +#ifndef PRAGMA_DIAG_POP +#define PRAGMA_DIAG_POP +#endif + +#endif // SHARE_VM_UTILITIES_COMPILERWARNINGS_HPP --- /dev/null 2019-01-28 17:48:52.000000000 +0800 +++ new/test/jfr/event/compiler/TestCodeCacheConfig.java 2019-01-28 17:48:52.000000000 +0800 @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2013, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jfr.event.compiler; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import com.oracle.java.testlibrary.Asserts; +import com.oracle.java.testlibrary.jfr.EventNames; +import com.oracle.java.testlibrary.jfr.Events; +import sun.hotspot.WhiteBox; + +/* + * @test TestCodeCacheConfig + * @library /testlibrary /testlibrary/whitebox + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+EnableJFR + * jfr.event.compiler.TestCodeCacheConfig + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+EnableJFR + * jfr.event.compiler.TestCodeCacheConfig + * @summary check "Code Cache Configuration" jfr event + */ +public class TestCodeCacheConfig { + private final static String EVENT_NAME = EventNames.CodeCacheConfiguration; + + private static final long CodeCacheExpectedSize = WhiteBox.getWhiteBox().getUintxVMFlag("ReservedCodeCacheSize"); + + public static void main(String[] args) throws Exception { + Recording recording = new Recording(); + recording.enable(EVENT_NAME); + recording.start(); + recording.stop(); + + List events = Events.fromRecording(recording); + Events.hasEvents(events); + RecordedEvent event = events.get(0); + long initialSize = (long) event.getValue("initialSize"); + long reservedSize = (long) event.getValue("reservedSize"); + long expansionSize = (long) event.getValue("expansionSize"); + long minBlockLength = (long) event.getValue("minBlockLength"); + long startAddress = (long) event.getValue("startAddress"); + long reservedTopAddress = (long) event.getValue("reservedTopAddress"); + + Asserts.assertGT(initialSize, 1024L, + "initialSize less than 1024 byte, got " + initialSize); + + Asserts.assertEQ(reservedSize, CodeCacheExpectedSize, + String.format("Unexpected reservedSize value. Expected %d but " + "got %d", CodeCacheExpectedSize, reservedSize)); + + Asserts.assertGTE(expansionSize, 1024L, + "expansionSize less than 1024 " + "bytes, got " + expansionSize); + + Asserts.assertGTE(minBlockLength, 1L, + "minBlockLength less than 1 byte, got " + minBlockLength); + + Asserts.assertNE(startAddress, 0L, + "startAddress null"); + + Asserts.assertNE(reservedTopAddress, 0L, + "codeCacheReservedTopAddr null"); + } +} --- /dev/null 2019-01-28 17:48:53.000000000 +0800 +++ new/test/jfr/event/compiler/TestCodeCacheFull.java 2019-01-28 17:48:53.000000000 +0800 @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2013, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jfr.event.compiler; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import com.oracle.java.testlibrary.Asserts; +import com.oracle.java.testlibrary.jfr.EventNames; +import com.oracle.java.testlibrary.jfr.Events; +import sun.hotspot.WhiteBox; +import sun.hotspot.code.BlobType; + +/* + * @test TestCodeCacheFull + * + * @library /testlibrary /testlibrary/whitebox + * @modules jdk.jfr + * jdk.management.jfr + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission + * + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:-UseLargePages -XX:+EnableJFR jfr.event.compiler.TestCodeCacheFull + */ +public class TestCodeCacheFull { + + public static void main(String[] args) throws Exception { + testWithBlobType(BlobType.All, BlobType.All.getSize()); + } + + private static void testWithBlobType(BlobType btype, long availableSize) throws Exception { + Recording r = new Recording(); + r.enable(EventNames.CodeCacheFull); + r.start(); + WhiteBox.getWhiteBox().allocateCodeBlob(availableSize, btype.id); + r.stop(); + + List events = Events.fromRecording(r); + Events.hasEvents(events); + RecordedEvent event = events.get(0); + + String codeBlobType = Events.assertField(event, "codeBlobType").notNull().getValue(); + BlobType blobType = BlobType.All; + Asserts.assertTrue(blobType.allowTypeWhenOverflow(blobType), "Unexpected overflow BlobType " + blobType.id); + Events.assertField(event, "entryCount").atLeast(0); + Events.assertField(event, "methodCount").atLeast(0); + Events.assertEventThread(event); + Events.assertField(event, "fullCount").atLeast(0); + Events.assertField(event, "startAddress").notEqual(0L); + Events.assertField(event, "commitedTopAddress").notEqual(0L); + Events.assertField(event, "reservedTopAddress").notEqual(0L); + } + + private static BlobType blobTypeFromName(String codeBlobTypeName) throws Exception { + for (BlobType t : BlobType.getAvailable()) { + if (t.beanName.equals(codeBlobTypeName)) { + return t; + } + } + throw new Exception("Unexpected event " + codeBlobTypeName); + } + + // Compute the available size for this BlobType by taking into account + // that it may be stored in a different code heap in case it does not fit + // into the current one. + private static long calculateAvailableSize(BlobType btype) { + long availableSize = btype.getSize(); + for (BlobType alternative : BlobType.getAvailable()) { + if (btype.allowTypeWhenOverflow(alternative)) { + availableSize = Math.max(availableSize, alternative.getSize()); + } + } + return availableSize; + } +} --- /dev/null 2019-01-28 17:48:54.000000000 +0800 +++ new/test/jfr/event/compiler/TestCompilerInlining.java 2019-01-28 17:48:54.000000000 +0800 @@ -0,0 +1,404 @@ +/* + * Copyright (c) 2015, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jfr.event.compiler; + +import jdk.internal.org.objectweb.asm.*; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedMethod; +import jdk.jfr.consumer.RecordedObject; +import com.oracle.java.testlibrary.Asserts; +import com.oracle.java.testlibrary.Platform; +import com.oracle.java.testlibrary.jfr.EventNames; +import com.oracle.java.testlibrary.jfr.Events; +import sun.hotspot.WhiteBox; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; +import java.lang.reflect.Method; +import java.util.*; +import java.util.stream.IntStream; + +/* + * @test CompilerInliningTest + * @bug 8073607 + * @summary Verifies that corresponding JFR events are emitted in case of inlining. + * + * @requires vm.opt.Inline == true | vm.opt.Inline == null + * @library /testlibrary /testlibrary/whitebox + * @modules java.base/jdk.internal.org.objectweb.asm + * jdk.jfr + * + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -Xbatch -XX:+EnableJFR jfr.event.compiler.TestCompilerInlining + */ +public class TestCompilerInlining { + private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); + private static final int LEVEL_SIMPLE = 1; + private static final int LEVEL_FULL_OPTIMIZATION = 4; + private static final Executable ENTRY_POINT = getConstructor(TestCase.class); + private static final String TEST_CASE_CLASS_NAME = TestCase.class.getName().replace('.', '/'); + + public static void main(String[] args) throws Exception { + InlineCalls inlineCalls = new InlineCalls(TestCase.class); + inlineCalls.disableInline(getConstructor(Object.class)); + inlineCalls.disableInline(getMethod(TestCase.class, "qux", boolean.class)); + inlineCalls.forceInline(getMethod(TestCase.class, "foo")); + inlineCalls.forceInline(getMethod(TestCase.class, "foo", int.class)); + inlineCalls.forceInline(getMethod(TestCase.class, "bar")); + inlineCalls.forceInline(getMethod(TestCase.class, "baz")); + + Map result = inlineCalls.getExpected(ENTRY_POINT); + for (int level : determineAvailableLevels()) { + testLevel(result, level); + } + } + + private static void testLevel(Map expectedResult, int level) throws IOException { + System.out.println("****** Testing level " + level + " *******"); + Recording r = new Recording(); + r.enable(EventNames.CompilerInlining); + r.start(); + WHITE_BOX.enqueueMethodForCompilation(ENTRY_POINT, level); + WHITE_BOX.deoptimizeMethod(ENTRY_POINT); + r.stop(); + System.out.println("Expected:"); + + List events = Events.fromRecording(r); + Set foundEvents = new HashSet<>(); + int foundRelevantEvent = 0; + for (RecordedEvent event : events) { + RecordedMethod callerObject = event.getValue("caller"); + RecordedObject calleeObject = event.getValue("callee"); + MethodDesc caller = methodToMethodDesc(callerObject); + MethodDesc callee = ciMethodToMethodDesc(calleeObject); + // only TestCase.* -> TestCase.* OR TestCase.* -> Object. are tested/filtered + if (caller.className.equals(TEST_CASE_CLASS_NAME) && (callee.className.equals(TEST_CASE_CLASS_NAME) + || (callee.className.equals("java/lang/Object") && callee.methodName.equals("")))) { + System.out.println(event); + boolean succeeded = (boolean) event.getValue("succeeded"); + int bci = Events.assertField(event, "bci").atLeast(0).getValue(); + Call call = new Call(caller, callee, bci); + foundRelevantEvent++; + Boolean expected = expectedResult.get(call); + Asserts.assertNotNull(expected, "Unexpected inlined call : " + call); + Asserts.assertEquals(expected, succeeded, "Incorrect result for " + call); + Asserts.assertTrue(foundEvents.add(call), "repeated event for " + call); + } + } + Asserts.assertEquals(foundRelevantEvent, expectedResult.size(), String.format("not all events found at lavel %d. " + "found = '%s'. expected = '%s'", level, events, expectedResult.keySet())); + System.out.println(); + System.out.println(); + } + + private static int[] determineAvailableLevels() { + if (WHITE_BOX.getBooleanVMFlag("TieredCompilation")) { + return IntStream.rangeClosed(LEVEL_SIMPLE, WHITE_BOX.getIntxVMFlag("TieredStopAtLevel").intValue()).toArray(); + } + if (Platform.isServer() && !Platform.isEmulatedClient()) { + return new int[] { LEVEL_FULL_OPTIMIZATION }; + } + if (Platform.isClient() || Platform.isEmulatedClient()) { + return new int[] { LEVEL_SIMPLE }; + } + throw new Error("TESTBUG: unknown VM"); + } + + private static MethodDesc methodToMethodDesc(RecordedMethod method) { + String internalClassName = method.getType().getName().replace('.', '/'); + String methodName = method.getValue("name"); + String methodDescriptor = method.getValue("descriptor"); + return new MethodDesc(internalClassName, methodName, methodDescriptor); + } + + private static MethodDesc ciMethodToMethodDesc(RecordedObject ciMethod) { + String internalClassName = ciMethod.getValue("type"); + String methodName = ciMethod.getValue("name"); + String methodDescriptor = ciMethod.getValue("descriptor"); + return new MethodDesc(internalClassName, methodName, methodDescriptor); + } + + private static Method getMethod(Class aClass, String name, Class... params) { + try { + return aClass.getDeclaredMethod(name, params); + } catch (NoSuchMethodException | SecurityException e) { + throw new Error("TESTBUG : cannot get method " + name + Arrays.toString(params), e); + } + } + + private static Constructor getConstructor(Class aClass, Class... params) { + try { + return aClass.getDeclaredConstructor(params); + } catch (NoSuchMethodException | SecurityException e) { + throw new Error("TESTBUG : cannot get constructor" + Arrays.toString(params), e); + } + } +} + +class TestCase { + public TestCase() { + foo(); + } + + public void foo() { + qux(true); + bar(); + foo(2); + } + + private void foo(int i) { + } + + private void bar() { + baz(); + qux(false); + qux(true); + } + + protected static double baz() { + qux(false); + return .0; + } + + private static int qux(boolean b) { + qux(b); + return 0; + } +} + +/** + * data structure for method call + */ +class Call { + public final MethodDesc caller; + public final MethodDesc callee; + public final int bci; + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || !(o instanceof Call)) + return false; + + Call call = (Call) o; + + if (bci != call.bci) + return false; + if (!callee.equals(call.callee)) + return false; + if (!caller.equals(call.caller)) + return false; + + return true; + } + + @Override + public int hashCode() { + int result = caller.hashCode(); + result = 31 * result + callee.hashCode(); + result = 47 * result + bci; + return result; + } + + public Call(MethodDesc caller, MethodDesc callee, int bci) { + Objects.requireNonNull(caller); + Objects.requireNonNull(callee); + this.caller = caller; + this.callee = callee; + this.bci = bci; + } + + @Override + public String toString() { + return String.format("Call{caller='%s', callee='%s', bci=%d}", caller, callee, bci); + } +} + +/** + * data structure for method description + */ +class MethodDesc { + public final String className; + public final String methodName; + public final String descriptor; + + public MethodDesc(Class aClass, String methodName, String descriptor) { + this(aClass.getName().replace('.', '/'), methodName, descriptor); + } + + public MethodDesc(String className, String methodName, String descriptor) { + Objects.requireNonNull(className); + Objects.requireNonNull(methodName); + Objects.requireNonNull(descriptor); + this.className = className.replace('.', '/'); + this.methodName = methodName; + this.descriptor = descriptor; + } + + public MethodDesc(Executable executable) { + Class aClass = executable.getDeclaringClass(); + className = Type.getInternalName(aClass).replace('.', '/'); + + if (executable instanceof Constructor) { + methodName = ""; + descriptor = Type.getConstructorDescriptor((Constructor) executable); + } else { + methodName = executable.getName(); + descriptor = Type.getMethodDescriptor((Method) executable); + } + + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + + MethodDesc that = (MethodDesc) o; + + if (!className.equals(that.className)) + return false; + if (!methodName.equals(that.methodName)) + return false; + if (!descriptor.equals(that.descriptor)) + return false; + + return true; + } + + @Override + public int hashCode() { + int result = className.hashCode(); + result = 31 * result + methodName.hashCode(); + result = 47 * result + descriptor.hashCode(); + return result; + } + + @Override + public String toString() { + return String.format("MethodDesc{className='%s', methodName='%s', descriptor='%s'}", className, methodName, descriptor); + } +} + +/** + * Aux class to get all calls in an arbitrary class. + */ +class InlineCalls { + private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); + + private final Collection calls; + private final Map inline; + + public InlineCalls(Class aClass) { + calls = getCalls(aClass); + inline = new HashMap<>(); + } + + /** + * @return expected inline events + */ + public Map getExpected(Executable entry) { + Map result = new HashMap<>(); + Queue methods = new ArrayDeque<>(); + Set finished = new HashSet<>(); + methods.add(new MethodDesc(entry)); + while (!methods.isEmpty()) { + MethodDesc method = methods.poll(); + if (finished.add(method)) { + inline.entrySet().stream().filter(k -> k.getKey().caller.equals(method)).forEach(k -> { + result.put(k.getKey(), k.getValue()); + if (k.getValue()) { + methods.add(k.getKey().callee); + } + }); + } + } + + return result; + } + + public void disableInline(Executable executable) { + WHITE_BOX.testSetDontInlineMethod(executable, true); + MethodDesc md = new MethodDesc(executable); + calls.stream().filter(c -> c.callee.equals(md)).forEach(c -> inline.put(c, false)); + } + + public void forceInline(Executable executable) { + WHITE_BOX.testSetForceInlineMethod(executable, true); + MethodDesc md = new MethodDesc(executable); + calls.stream().filter(c -> c.callee.equals(md)).forEach(c -> inline.putIfAbsent(c, true)); + } + + private static Collection getCalls(Class aClass) { + List calls = new ArrayList<>(); + ClassWriter cw; + ClassReader cr; + try { + cr = new ClassReader(aClass.getName()); + } catch (IOException e) { + throw new Error("TESTBUG : unexpected IOE during class reading", e); + } + cw = new ClassWriter(cr, 0); + ClassVisitor cv = new ClassVisitor(Opcodes.ASM5, cw) { + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String descriptor, String[] exceptions) { + System.out.println("Method: " +name); + MethodVisitor mv = super.visitMethod(access, name, desc, descriptor, exceptions); + return new CallTracer(aClass, name, desc, mv, calls); + } + }; + cr.accept(cv, 0); + + return calls; + } + + private static class CallTracer extends MethodVisitor { + private final MethodDesc caller; + private Collection calls; + + public CallTracer(Class aClass, String name, String desc, MethodVisitor mv, Collection calls) { + super(Opcodes.ASM5, mv); + caller = new MethodDesc(aClass.getName(), name, desc); + this.calls = calls; + } + + @Override + public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { + Label label = new Label(); + visitLabel(label); + super.visitMethodInsn(opcode, owner, name, desc, itf); + calls.add(new Call(caller, new MethodDesc(owner, name, desc), label.getOffset())); + } + } +} --- /dev/null 2019-01-28 17:48:55.000000000 +0800 +++ new/test/jfr/event/compiler/TestCompilerPhase.java 2019-01-28 17:48:55.000000000 +0800 @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2013, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jfr.event.compiler; + +import java.lang.reflect.Method; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import com.oracle.java.testlibrary.Utils; +import com.oracle.java.testlibrary.jfr.EventNames; +import com.oracle.java.testlibrary.jfr.Events; +import sun.hotspot.WhiteBox; + +/* + * @test + * @requires vm.compMode!="Xint" & vm.flavor == "server" & (vm.opt.TieredStopAtLevel == 4 | vm.opt.TieredStopAtLevel == null) + * @library /testlibrary /testlibrary/jfr /testlibrary/whitebox + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:CompileOnly=jfr.event.compiler.TestCompilerPhase::dummyMethod + * -XX:+SegmentedCodeCache -Xbootclasspath/a:. + * jfr.event.compiler.TestCompilerPhase + */ +public class TestCompilerPhase { + private final static String EVENT_NAME = EventNames.CompilerPhase; + private final static String METHOD_NAME = "dummyMethod"; + private static final int COMP_LEVEL_SIMPLE = 1; + private static final int COMP_LEVEL_FULL_OPTIMIZATION = 4; + + public static void main(String[] args) throws Exception { + Recording recording = new Recording(); + recording.enable(EVENT_NAME); + recording.start(); + + // Provoke compilation + Method mtd = TestCompilerPhase.class.getDeclaredMethod(METHOD_NAME, new Class[0]); + WhiteBox WB = WhiteBox.getWhiteBox(); + String directive = "[{ match: \"" + TestCompilerPhase.class.getName().replace('.', '/') + + "." + METHOD_NAME + "\", " + "BackgroundCompilation: false }]"; + WB.addCompilerDirective(directive); + if (!WB.enqueueMethodForCompilation(mtd, COMP_LEVEL_FULL_OPTIMIZATION)) { + WB.enqueueMethodForCompilation(mtd, COMP_LEVEL_SIMPLE); + } + Utils.waitForCondition(() -> WB.isMethodCompiled(mtd)); + dummyMethod(); + + recording.stop(); + + List events = Events.fromRecording(recording); + Events.hasEvents(events); + for (RecordedEvent event : events) { + System.out.println("Event:" + event); + Events.assertField(event, "phase").notEmpty(); + Events.assertField(event, "compileId").atLeast(0); + Events.assertField(event, "phaseLevel").atLeast((short)0).atMost((short)4); + Events.assertEventThread(event); + } + } + + static void dummyMethod() { + System.out.println("hello!"); + } +} --- /dev/null 2019-01-28 17:48:56.000000000 +0800 +++ new/test/jfr/event/gc/configuration/GCHeapConfigurationEventTester.java 2019-01-28 17:48:56.000000000 +0800 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2013, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jfr.event.gc.configuration; + +import static com.oracle.java.testlibrary.Asserts.assertGreaterThanOrEqual; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import com.oracle.java.testlibrary.jfr.EventNames; +import com.oracle.java.testlibrary.jfr.EventVerifier; +import com.oracle.java.testlibrary.jfr.Events; + + +public abstract class GCHeapConfigurationEventTester { + public void run() throws Exception { + Recording recording = new Recording(); + recording.enable(EventNames.GCHeapConfiguration); + recording.start(); + recording.stop(); + List events = Events.fromRecording(recording); + assertGreaterThanOrEqual(events.size(), 1, "Expected at least 1 event"); + EventVerifier v = createVerifier(events.get(0)); + v.verify(); + } + + protected abstract EventVerifier createVerifier(RecordedEvent e); +} --- /dev/null 2019-01-28 17:48:57.000000000 +0800 +++ new/test/jfr/event/gc/configuration/GCHeapConfigurationEventVerifier.java 2019-01-28 17:48:57.000000000 +0800 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2013, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jfr.event.gc.configuration; + +import jdk.jfr.consumer.RecordedEvent; +import com.oracle.java.testlibrary.jfr.EventVerifier; + +public abstract class GCHeapConfigurationEventVerifier extends EventVerifier { + public GCHeapConfigurationEventVerifier(RecordedEvent e) { + super(e); + } + + protected void verifyMinHeapSizeIs(long expected) throws Exception { + verifyEquals("minSize", expected); + } + + protected void verifyInitialHeapSizeIs(long expected) throws Exception { + verifyEquals("initialSize", expected); + } + + protected void verifyMaxHeapSizeIs(long expected) throws Exception { + verifyEquals("maxSize", expected); + } + + protected void verifyUsesCompressedOopsIs(boolean expected) throws Exception { + verifyEquals("usesCompressedOops", expected); + } + + protected void verifyObjectAlignmentInBytesIs(int expected) throws Exception { + verifyEquals("objectAlignment", (long)expected); + } + + protected void verifyHeapAddressBitsIs(int expected) throws Exception { + verifyEquals("heapAddressBits", (byte)expected); + } + + protected void verifyCompressedOopModeIs(String expected) throws Exception { + verifyEquals("compressedOopsMode", expected); + } + + protected void verifyCompressedOopModeContains(String expected) throws Exception { + verifyContains("compressedOopsMode", expected); + } + +} --- /dev/null 2019-01-28 17:48:58.000000000 +0800 +++ new/test/jfr/event/gc/configuration/TestGCHeapConfigurationEventWith32BitOops.java 2019-01-28 17:48:58.000000000 +0800 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2013, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jfr.event.gc.configuration; + +import jdk.jfr.consumer.RecordedEvent; +import com.oracle.java.testlibrary.jfr.EventVerifier; +import com.oracle.java.testlibrary.jfr.GCHelper; +import sun.hotspot.WhiteBox; + +/* See the shell script wrapper for the flags used when invoking the JVM */ +public class TestGCHeapConfigurationEventWith32BitOops extends GCHeapConfigurationEventTester { + public static void main(String[] args) throws Exception { + GCHeapConfigurationEventTester t = new TestGCHeapConfigurationEventWith32BitOops(); + t.run(); + } + + @Override + protected EventVerifier createVerifier(RecordedEvent e) { + return new ThirtyTwoBitsVerifier(e); + } +} + +class ThirtyTwoBitsVerifier extends GCHeapConfigurationEventVerifier { + public ThirtyTwoBitsVerifier(RecordedEvent event) { + super(event); + } + + @Override + public void verify() throws Exception { + WhiteBox wb = WhiteBox.getWhiteBox(); + long heapAlignment = wb.getHeapAlignment(); + long alignedHeapSize = GCHelper.alignUp(megabytes(100), heapAlignment); + verifyMinHeapSizeIs(megabytes(100)); + verifyInitialHeapSizeIs(alignedHeapSize); + verifyMaxHeapSizeIs(alignedHeapSize); + verifyUsesCompressedOopsIs(true); + verifyObjectAlignmentInBytesIs(8); + verifyHeapAddressBitsIs(32); + verifyCompressedOopModeIs("32-bit"); + } +} --- /dev/null 2019-01-28 17:48:59.000000000 +0800 +++ new/test/jfr/event/gc/configuration/TestGCHeapConfigurationEventWith32BitOops.sh 2019-01-28 17:48:59.000000000 +0800 @@ -0,0 +1,61 @@ +# +# Copyright (c) 2013, 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. +# +# @test TestGCHeapConfigurationEventWith32BitOops +# @requires vm.gc == "Parallel" | vm.gc == null +# @library /testlibrary /testlibrary/whitebox +# @build jfr.event.gc.configuration.GCHeapConfigurationEventTester +# @build jfr.event.gc.configuration.GCHeapConfigurationEventVerifier +# @build jfr.event.gc.configuration.TestGCHeapConfigurationEventWith32BitOops sun.hotspot.WhiteBox +# @run main ClassFileInstaller sun.hotspot.WhiteBox +# @run shell TestGCHeapConfigurationEventWith32BitOops.sh + +uses_64_bit_testjava() { + ${TESTJAVA}/bin/java ${TESTVMOPTS} -version 2>&1 | grep '64-Bit' > /dev/null +} + +uses_windows_or_linux() { + case `uname -s` in + Linux | CYGWIN* | Windows* ) + return 0 + ;; + esac + return 1 +} + +TEST='jfr.event.gc.configuration.TestGCHeapConfigurationEventWith32BitOops' + +OPTIONS='-XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:+UseCompressedOops -Xmx100m -Xms100m -XX:InitialHeapSize=100m -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+EnableJFR' + +if [ -z "${TESTCLASSPATH}" ]; then + echo "Using TESTCLASSES" + MY_CLASSPATH=${TESTCLASSES} +else + echo "Using TESTCLASSPATH" + MY_CLASSPATH=${TESTCLASSPATH} +fi + +if uses_windows_or_linux && uses_64_bit_testjava; then + printenv + echo "${TESTJAVA}/bin/java ${TESTVMOPTS} ${OPTIONS} -cp ${MY_CLASSPATH} ${TEST}" + ${TESTJAVA}/bin/java ${TESTVMOPTS} ${OPTIONS} -cp ${MY_CLASSPATH} ${TEST} +fi --- /dev/null 2019-01-28 17:49:00.000000000 +0800 +++ new/test/jfr/event/gc/heapsummary/TestHeapSummaryCommittedSize.java 2019-01-28 17:49:00.000000000 +0800 @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2013, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jfr.event.gc.heapsummary; + +import java.time.Duration; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import com.oracle.java.testlibrary.Asserts; +import com.oracle.java.testlibrary.jfr.EventNames; +import com.oracle.java.testlibrary.jfr.EventVerifier; +import com.oracle.java.testlibrary.jfr.Events; +import com.oracle.java.testlibrary.jfr.GCHelper; +import sun.hotspot.WhiteBox; +import com.oracle.java.testlibrary.jfr.HeapSummaryEventAllGcs; + +/* + * @test + * @requires vm.gc == "Parallel" | vm.gc == null + * @library /testlibrary /testlibrary/whitebox + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -XX:+UnlockExperimentalVMOptions + -XX:-UseFastUnorderedTimeStamps -Xmx16m -XX:+UseParallelGC + -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+EnableJFR + jfr.event.gc.heapsummary.TestHeapSummaryCommittedSize + */ +public class TestHeapSummaryCommittedSize { + private final static String EVENT_NAME = EventNames.GCHeapSummary; + + public static void main(String[] args) throws Exception { + Recording recording = new Recording(); + recording.enable(EVENT_NAME).withThreshold(Duration.ofMillis(0)); + + recording.start(); + System.gc(); + recording.stop(); + + boolean isAnyFound = false; + for (RecordedEvent event : Events.fromRecording(recording)) { + System.out.println("Event: " + event); + if (!Events.isEventType(event, EVENT_NAME)) { + continue; + } + isAnyFound = true; + CommittedHeapSizeVerifier verifier = new CommittedHeapSizeVerifier(event); + verifier.verify(); + } + Asserts.assertTrue(isAnyFound, "No matching event"); + } +} + +class CommittedHeapSizeVerifier extends EventVerifier { + private final static long MAX_UNALIGNED_COMMITTED_SIZE = 16 * 1024 * 1024; + private final long MAX_ALIGNED_COMMITTED_SIZE; + + public CommittedHeapSizeVerifier(RecordedEvent event) { + super(event); + WhiteBox wb = WhiteBox.getWhiteBox(); + long heapAlignment = wb.getHeapAlignment(); + MAX_ALIGNED_COMMITTED_SIZE = GCHelper.alignUp( + MAX_UNALIGNED_COMMITTED_SIZE,heapAlignment); + } + + public void verify() throws Exception { + Events.assertField(event, "heapSpace.committedSize").atLeast(0L).atMost(MAX_ALIGNED_COMMITTED_SIZE); + } +} --- /dev/null 2019-01-28 17:49:01.000000000 +0800 +++ new/test/jfr/event/runtime/TestSafepointEvents.java 2019-01-28 17:49:01.000000000 +0800 @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2013, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jfr.event.runtime; + +import static com.oracle.java.testlibrary.Asserts.assertTrue; + +import java.nio.file.Paths; +import java.time.Duration; +import java.util.*; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; + +import com.oracle.java.testlibrary.Asserts; +import com.oracle.java.testlibrary.jfr.EventNames; +import com.oracle.java.testlibrary.jfr.Events; +import sun.hotspot.WhiteBox; + +/* + * @test TestSafepointEvents + * @library /testlibrary /testlibrary/whitebox + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main/othervm -Xbootclasspath/a:. -XX:+PrintJFRLog + * -XX:+FlightRecorder -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+EnableJFR + * jfr.event.runtime.TestSafepointEvents + */ +public class TestSafepointEvents { + + static final String[] EVENT_NAMES = new String[] { + EventNames.SafepointBegin, + EventNames.SafepointStateSyncronization, + EventNames.SafepointWaitBlocked, + EventNames.SafepointCleanup, + EventNames.SafepointCleanupTask, + EventNames.SafepointEnd + }; + + public static void main(String[] args) throws Exception { + Recording recording = new Recording(); + for (String name : EVENT_NAMES) { + recording.enable(name).withThreshold(Duration.ofMillis(0)); + } + recording.start(); + WhiteBox.getWhiteBox().forceSafepoint(); + WhiteBox.getWhiteBox().forceSafepoint(); + recording.stop(); + + try { + // Verify that each event type was seen at least once + for (String name : EVENT_NAMES) { + boolean found = false; + for (RecordedEvent event : Events.fromRecording(recording)) { + found = event.getEventType().getName().equals(name); + if (found) { + break; + } + } + assertTrue(found, "Expected event from test [" + name + "]"); + } + + // Collect all events grouped by safepoint id + SortedMap> safepointIds = new TreeMap<>(); + for (RecordedEvent event : Events.fromRecording(recording)) { + Integer safepointId = event.getValue("safepointId"); + if (!safepointIds.containsKey(safepointId)) { + safepointIds.put(safepointId, new HashSet<>()); + } + safepointIds.get(safepointId).add(event.getEventType().getName()); + } + + // The last safepoint may be related to stopping the recording and can thus be + // incomplete - so if there is more than one, ignore the last one + if (safepointIds.size() > 1) { + safepointIds.remove(safepointIds.lastKey()); + } + Asserts.assertGreaterThanOrEqual(safepointIds.size(), 1, "At least 1 safepoint must have occured"); + + // Verify that each safepoint id has an occurence of every event type, + // this ensures that all events related to a given safepoint had the same id + for (Set safepointEvents : safepointIds.values()) { + for (String name : EVENT_NAMES) { + assertTrue(safepointEvents.contains(name), "Expected event '" + name + "' to be present"); + } + } + } catch (Throwable e) { + recording.dump(Paths.get("failed.jfr")); + throw e; + } finally { + recording.close(); + } + } +} --- /dev/null 2019-01-28 17:49:02.000000000 +0800 +++ new/test/jfr/event/runtime/TestThrowableInstrumentation.java 2019-01-28 17:49:02.000000000 +0800 @@ -0,0 +1,61 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jfr.event.runtime; + +import sun.hotspot.WhiteBox; +import java.util.Objects; +import com.oracle.java.testlibrary.Platform; + +/* + * @test + * @bug 8153324 + * @summary Verify instrumented Throwable bytecode by compiling it with C1. + * @library /testlibrary /testlibrary/whitebox + * @modules java.base/jdk.internal.misc + * java.management + * @requires vm.compMode!="Xint" + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -Xbatch -XX:+EnableJFR -XX:StartFlightRecording=dumponexit=true jfr.event.runtime.TestThrowableInstrumentation + */ +public class TestThrowableInstrumentation { + private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); + private static int COMP_LEVEL_SIMPLE = 1; + + private static boolean isTieredCompilationEnabled() { + return Boolean.valueOf(Objects.toString(WHITE_BOX.getVMFlag("TieredCompilation"))); + } + + public static void main(String[] args) { + // Compile Throwable:: with C1 (if available) + if (!WHITE_BOX.enqueueInitializerForCompilation(java.lang.Throwable.class, COMP_LEVEL_SIMPLE)) { + if (!Platform.isServer() || isTieredCompilationEnabled() || Platform.isEmulatedClient()) { + throw new RuntimeException("Unable to compile Throwable:: with C1"); + } + } + } +} --- /dev/null 2019-01-28 17:49:03.000000000 +0800 +++ new/test/jfr/jvm/TestJFRIntrinsic.java 2019-01-28 17:49:03.000000000 +0800 @@ -0,0 +1,155 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary Intrinsic for JFR + * @library /testlibrary /testlibrary/whitebox + * + * @modules jdk.jfr/jdk.jfr.internal + * java.base/jdk.internal.misc + * java.management + * + * @build sun.hotspot.code.NMethod + * @build sun.hotspot.WhiteBox + * @build ClassFileInstaller + * + * @run driver ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * + * @run main/othervm -Xbootclasspath/a:. -ea -Xmixed -Xbatch -XX:TieredStopAtLevel=4 -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+EnableJFR + * jdk.jfr.jvm.TestJFRIntrinsic + * + * @run main/othervm -Xbootclasspath/a:. -ea -Xmixed -Xbatch -XX:TieredStopAtLevel=1 -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+EnableJFR + * jdk.jfr.jvm.TestJFRIntrinsic + */ + +package jdk.jfr.jvm; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.stream.IntStream; +import jdk.jfr.internal.JVM; +import com.oracle.java.testlibrary.Platform; +import sun.hotspot.WhiteBox; +import sun.hotspot.code.NMethod; + +public class TestJFRIntrinsic { + + private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); + public Object eventWriter; + + public static void main(String... args) throws Exception { + /* + Temporarily excluded until getClassId is reworked to accommodate epoch shift tagging + Method classid = TestJFRIntrinsic.class.getDeclaredMethod("getClassIdIntrinsic", Class.class); + ti.runIntrinsicTest(classid); + */ + TestJFRIntrinsic ti = new TestJFRIntrinsic(); + Method eventWriterMethod = TestJFRIntrinsic.class.getDeclaredMethod("getEventWriterIntrinsic", Class.class); + ti.runIntrinsicTest(eventWriterMethod); + } + + /* + public void getClassIdIntrinsic(Class cls) { + long exp = JVM.getClassId(cls); + if (exp == 0) { + throw new RuntimeException("Class id is zero"); + } + } + */ + + public void getEventWriterIntrinsic(Class cls) { + Object o = JVM.getEventWriter(); + if (o != null) { + eventWriter = o; + } + } + + void runIntrinsicTest(Method method) throws Exception { + if (getMaxCompilationLevel() < 1) { + /* no compiler */ + return; + } + /* load it */ + try { + method.invoke(this, TestJFRIntrinsic.class); + } catch(Exception e) { + throw new RuntimeException(e); + } + + int[] lvls = getAvailableCompilationLevels(); + for (int i : lvls) { + if (!WHITE_BOX.enqueueMethodForCompilation(method, i)) { + throw new RuntimeException("Failed to enqueue method on level: " + i); + } + + if (WHITE_BOX.isMethodCompiled(method)) { + NMethod nm = NMethod.get(method, false); + if (nm.comp_level != i) { + throw new RuntimeException("Failed to compile on correct level: " + i); + } + System.out.println("Compiled " + method + " on level " + i); + } + } + } + + /* below is copied from CompilerUtil in hotspot test lib, removed this when it's moved */ + + /** + * Returns available compilation levels + * + * @return int array with compilation levels + */ + public static int[] getAvailableCompilationLevels() { + if (!WhiteBox.getWhiteBox().getBooleanVMFlag("UseCompiler")) { + return new int[0]; + } + if (WhiteBox.getWhiteBox().getBooleanVMFlag("TieredCompilation")) { + Long flagValue = WhiteBox.getWhiteBox() + .getIntxVMFlag("TieredStopAtLevel"); + int maxLevel = flagValue.intValue(); + return IntStream.rangeClosed(1, maxLevel).toArray(); + } else { + if (Platform.isServer() && !Platform.isEmulatedClient()) { + return new int[]{4}; + } + if (Platform.isClient() || Platform.isMinimal() || Platform.isEmulatedClient()) { + return new int[]{1}; + } + } + return new int[0]; + } + + /** + * Returns maximum compilation level available + * @return an int value representing maximum compilation level available + */ + public static int getMaxCompilationLevel() { + return Arrays.stream(getAvailableCompilationLevels()) + .max() + .getAsInt(); + } +} --- /dev/null 2019-01-28 17:49:04.000000000 +0800 +++ new/test/jfr/precompiled.hpp 2019-01-28 17:49:04.000000000 +0800 @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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. + * + */ + +// This file is included once when using gcc, the other time the real +// precompiled.hpp is used. --- /dev/null 2019-01-28 17:49:05.000000000 +0800 +++ new/test/jfr/test_threadCpuLoad.cpp 2019-01-28 17:49:05.000000000 +0800 @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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" + +// This test performs mocking of certain JVM functionality. This works by +// including the source file under test inside an anonymous namespace (which +// prevents linking conflicts) with the mocked symbols redefined. + +// The include list should mirror the one found in the included source file - +// with the ones that should pick up the mocks removed. Those should be included +// later after the mocks have been defined. + +#include "jvm.h" +#include "classfile/classLoaderStats.hpp" +#include "classfile/javaClasses.hpp" +#include "code/codeCache.hpp" +#include "compiler/compileBroker.hpp" +#include "gc/g1/g1HeapRegionEventSender.hpp" +#include "gc/shared/gcConfiguration.hpp" +#include "gc/shared/gcTrace.hpp" +#include "gc/shared/objectCountEventSender.hpp" +#include "gc/shared/vmGCOperations.hpp" +#include "jfr/periodic/jfrModuleEvent.hpp" +#include "jfr/periodic/jfrOSInterface.hpp" +#include "jfr/periodic/jfrThreadCPULoadEvent.hpp" +#include "jfr/periodic/jfrThreadDumpEvent.hpp" +#include "jfr/recorder/jfrRecorder.hpp" +#include "jfr/utilities/jfrTraceTime.hpp" +#include "logging/log.hpp" +#include "memory/heapInspection.hpp" +#include "memory/resourceArea.hpp" +#include "oops/oop.inline.hpp" +#include "runtime/arguments.hpp" +#include "runtime/globals.hpp" +#include "runtime/os.hpp" +#include "runtime/os_perf.hpp" +#include "runtime/thread.inline.hpp" +#include "runtime/threadSMR.hpp" +#include "runtime/sweeper.hpp" +#include "runtime/vmThread.hpp" +#include "services/classLoadingService.hpp" +#include "services/management.hpp" +#include "services/threadService.hpp" +#include "trace/tracing.hpp" +#include "utilities/exceptions.hpp" +#include "utilities/globalDefinitions.hpp" + +#include "unittest.hpp" + +namespace { + + class MockEventThreadCPULoad : public ::EventThreadCPULoad + { + public: + float user; + float system; + + public: + MockEventThreadCPULoad(EventStartTime timing=TIMED) : ::EventThreadCPULoad(timing) {} + + void set_user(float new_value) { + user = new_value; + } + void set_system(float new_value) { + system = new_value; + } + }; + + class MockOs : public ::os { + public: + static jlong user_cpu_time; + static jlong system_cpu_time; + + static jlong thread_cpu_time(Thread *thread, bool user_sys_cpu_time) { + return user_sys_cpu_time ? user_cpu_time + system_cpu_time : user_cpu_time; + } + }; + + jlong MockOs::user_cpu_time; + jlong MockOs::system_cpu_time; + +// Reincluding source files in the anonymous namespace unfortunately seems to +// behave strangely with precompiled headers (only when using gcc though) +#ifndef DONT_USE_PRECOMPILED_HEADER +#define DONT_USE_PRECOMPILED_HEADER +#endif + +#define os MockOs +#define EventThreadCPULoad MockEventThreadCPULoad + +#include "tracefiles/tracePeriodic.hpp" +#include "jfr/periodic/jfrPeriodic.cpp" + +#undef os +#undef EventThreadCPULoad + +} // anonymous namespace + +class JfrTestThreadCPULoadSingle : public ::testing::Test { +protected: + JavaThread* thread; + JfrThreadData* thread_data; + MockEventThreadCPULoad event; + + void SetUp() { + thread = new JavaThread(); + thread_data = thread->trace_data(); + thread_data->set_wallclock_time(0); + thread_data->set_user_time(0); + thread_data->set_cpu_time(0); + } + + void TearDown() { + delete thread; + } +}; + +TEST_VM_F(JfrTestThreadCPULoadSingle, SingleCpu) { + MockOs::user_cpu_time = 100 * NANOSECS_PER_MILLISEC; + MockOs::system_cpu_time = 100 * NANOSECS_PER_MILLISEC; + EXPECT_TRUE(JfrThreadCPULoadEvent::update_event(event, thread, 400 * NANOSECS_PER_MILLISEC, 1)); + EXPECT_FLOAT_EQ(0.25, event.user); + EXPECT_FLOAT_EQ(0.25, event.system); +} + +TEST_VM_F(JfrTestThreadCPULoadSingle, MultipleCpus) { + MockOs::user_cpu_time = 100 * NANOSECS_PER_MILLISEC; + MockOs::system_cpu_time = 100 * NANOSECS_PER_MILLISEC; + EXPECT_TRUE(JfrThreadCPULoadEvent::update_event(event, thread, 400 * NANOSECS_PER_MILLISEC, 2)); + EXPECT_FLOAT_EQ(0.125, event.user); + EXPECT_FLOAT_EQ(0.125, event.system); +} + +TEST_VM_F(JfrTestThreadCPULoadSingle, BelowThreshold) { + MockOs::user_cpu_time = 100; + MockOs::system_cpu_time = 100; + EXPECT_FALSE(JfrThreadCPULoadEvent::update_event(event, thread, 400 * NANOSECS_PER_MILLISEC, 2)); +} + +TEST_VM_F(JfrTestThreadCPULoadSingle, UserAboveMaximum) { + + // First call will not report above 100% + MockOs::user_cpu_time = 200 * NANOSECS_PER_MILLISEC; + MockOs::system_cpu_time = 100 * NANOSECS_PER_MILLISEC; + EXPECT_TRUE(JfrThreadCPULoadEvent::update_event(event, thread, 200 * NANOSECS_PER_MILLISEC, 1)); + EXPECT_FLOAT_EQ(0.5, event.user); + EXPECT_FLOAT_EQ(0.5, event.system); + + // Second call will see an extra 100 millisecs user time from the remainder + EXPECT_TRUE(JfrThreadCPULoadEvent::update_event(event, thread, (200 + 400) * NANOSECS_PER_MILLISEC, 1)); + EXPECT_FLOAT_EQ(0.25, event.user); + EXPECT_FLOAT_EQ(0, event.system); +} + +TEST_VM_F(JfrTestThreadCPULoadSingle, SystemAboveMaximum) { + + // First call will not report above 100% + MockOs::user_cpu_time = 100 * NANOSECS_PER_MILLISEC; + MockOs::system_cpu_time = 300 * NANOSECS_PER_MILLISEC; + EXPECT_TRUE(JfrThreadCPULoadEvent::update_event(event, thread, 200 * NANOSECS_PER_MILLISEC, 1)); + EXPECT_FLOAT_EQ(0, event.user); + EXPECT_FLOAT_EQ(1, event.system); + + // Second call will see an extra 100 millisecs user and system time from the remainder + EXPECT_TRUE(JfrThreadCPULoadEvent::update_event(event, thread, (200 + 400) * NANOSECS_PER_MILLISEC, 1)); + EXPECT_FLOAT_EQ(0.25, event.user); + EXPECT_FLOAT_EQ(0.25, event.system); +} + +TEST_VM_F(JfrTestThreadCPULoadSingle, SystemTimeDecreasing) { + + // As seen in an actual run - caused by different resolution for total and user time + // Total time User time (Calculated system time) + // 200 100 100 + // 210 200 10 + // 400 300 100 + + MockOs::user_cpu_time = 100 * NANOSECS_PER_MILLISEC; + MockOs::system_cpu_time = 100 * NANOSECS_PER_MILLISEC; + EXPECT_TRUE(JfrThreadCPULoadEvent::update_event(event, thread, 400 * NANOSECS_PER_MILLISEC, 1)); + EXPECT_FLOAT_EQ(0.25, event.user); + EXPECT_FLOAT_EQ(0.25, event.system); + + MockOs::user_cpu_time += 100 * NANOSECS_PER_MILLISEC; + MockOs::system_cpu_time -= 90 * NANOSECS_PER_MILLISEC; + EXPECT_TRUE(JfrThreadCPULoadEvent::update_event(event, thread, (400 + 400) * NANOSECS_PER_MILLISEC, 1)); + EXPECT_FLOAT_EQ(0.25, event.user); + EXPECT_FLOAT_EQ(0, event.system); + + MockOs::user_cpu_time += 100 * NANOSECS_PER_MILLISEC; + MockOs::system_cpu_time += 90 * NANOSECS_PER_MILLISEC; + EXPECT_TRUE(JfrThreadCPULoadEvent::update_event(event, thread, (400 + 400 + 400) * NANOSECS_PER_MILLISEC, 1)); + EXPECT_FLOAT_EQ(0.25, event.user); + EXPECT_FLOAT_EQ(0, event.system); +} --- /dev/null 2019-01-28 17:49:06.000000000 +0800 +++ new/test/testlibrary/com/oracle/java/testlibrary/jfr/EventField.java 2019-01-28 17:49:06.000000000 +0800 @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2013, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.java.testlibrary.jfr; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import jdk.jfr.ValueDescriptor; +import jdk.jfr.consumer.RecordedObject; +import com.oracle.java.testlibrary.Asserts; + + +public final class EventField { + public final RecordedObject event; + public final ValueDescriptor desc; + + public EventField(RecordedObject event, ValueDescriptor valueDescriptor) { + this.event = event; + this.desc = valueDescriptor; + } + + @SuppressWarnings("unchecked") + public > boolean isEqual(T value) { + return value == (T)getValue(); + } + + @SuppressWarnings("unchecked") + public > EventField equal(T value) { + doAssert(()-> Asserts.assertEquals((T)getValue(), value, getErrMsg("Value not equal to " + value))); + return this; + } + + @SuppressWarnings("unchecked") + public > EventField notEqual(T value) { + doAssert(()-> Asserts.assertNotEquals((T)getValue(), value, getErrMsg("Value equal to " + value))); + return this; + } + + @SuppressWarnings("unchecked") + public > EventField above(T value) { + doAssert(()-> Asserts.assertGreaterThan((T)getValue(), value, getErrMsg("Value not above " + value))); + return this; + } + + @SuppressWarnings("unchecked") + public > EventField below(T value) { + doAssert(()-> Asserts.assertLessThan((T)getValue(), value, getErrMsg("Value not below " + value))); + return this; + } + + @SuppressWarnings("unchecked") + public > EventField atLeast(T value) { + doAssert(()-> Asserts.assertGreaterThanOrEqual((T)getValue(), value, getErrMsg("Value not atLeast" + value))); + return this; + } + + @SuppressWarnings("unchecked") + public > EventField atMost(T value) { + doAssert(()-> Asserts.assertLessThanOrEqual((T)getValue(), value, getErrMsg("Value not atMost " + value))); + return this; + } + + public > EventField instring(String part) { + final String value = getValue(); + doAssert(()-> Asserts.assertTrue(value.contains(part), getErrMsg("Value does not contain '" + part +"'"))); + return this; + } + + @SuppressWarnings("unchecked") + public T getValue() { + return (T)event.getValue(desc.getName()); + } + + public EventField notNull() { + doAssert(()-> Asserts.assertNotNull(getValue(), getErrMsg("Field is null"))); + return this; + } + + public EventField isNull() { + doAssert(()-> Asserts.assertNull(getValue(), getErrMsg("Field is not null"))); + return this; + } + + public EventField notEmpty() { + notNull(); + final String s = getValue(); + doAssert(()-> Asserts.assertFalse(s.isEmpty(), getErrMsg("Field is empty"))); + return this; + } + + private void doAssert(AssertFunction f) { + try { + f.doAssert(); + } catch (RuntimeException e) { + System.out.printf("Error: %s%nFailed event:%n%s%n", e.getMessage(), event.toString()); + throw e; + } + } + + public EventField containsAny(String... allowed) { + final String value = getValue(); + final List allowedValues = Arrays.asList(allowed); + boolean contains = false; + for(String allowedValue : allowed) { + if (value.contains(allowedValue)) { + contains = true; + } + } + if (!contains) { + doAssert(()-> Asserts.fail(getErrMsg(String.format("Value not in (%s)", + allowedValues.stream().collect(Collectors.joining(", ")))))); + } + return this; + } + + private String getErrMsg(String msg) { + final String name = desc.getName(); + final Object value = event.getValue(name); + return String.format("%s, field='%s', value='%s'", msg, name, value); + } + + @FunctionalInterface + public interface AssertFunction { + void doAssert(); + } +} --- /dev/null 2019-01-28 17:49:07.000000000 +0800 +++ new/test/testlibrary/com/oracle/java/testlibrary/jfr/EventNames.java 2019-01-28 17:49:07.000000000 +0800 @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2015, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.java.testlibrary.jfr; + +import jdk.jfr.EventType; + +/** + * Contains id for events that are shipped with the JDK. + * + */ +public class EventNames { + + public final static String PREFIX = "com.oracle.jdk."; + private static final String GC_CATEGORY = "GC"; + + // JVM Configuration + public final static String JVMInformation = PREFIX + "JVMInformation"; // "vm.info"; + public final static String InitialSystemProperty = PREFIX + "InitialSystemProperty";// "vm.initial_system_property"; + public final static String IntFlag = PREFIX + "IntFlag"; // "vm.flag.int"; + public final static String UnsignedIntFlag = PREFIX + "UnsignedIntFlag"; // "vm.flag.uint"; + public final static String LongFlag = PREFIX + "LongFlag"; // "vm.flag.long"; + public final static String UnsignedLongFlag = PREFIX + "UnsignedLongFlag"; // "vm.flag.ulong"; + public final static String DoubleFlag = PREFIX + "DoubleFlag"; // "vm.flag.double"; + public final static String BooleanFlag = PREFIX + "BooleanFlag"; // vm.flag.boolean"; + public final static String StringFlag = PREFIX + "StringFlag"; // vm.flag.string"; + public final static String IntFlagChanged = PREFIX + "IntFlagChanged"; // vm.flag.int_changed"; + public final static String UnsignedIntFlagChanged = PREFIX + "UnsignedIntFlagChanged"; // "vm.flag.uint_changed"; + public final static String LongFlagChanged = PREFIX + "LongFlagChanged"; // "vm.flag.long_changed"; + public final static String UnsignedLongFlagChanged = PREFIX + "UnsignedLongFlagChanged"; // "vm.flag.ulong_changed"; + public final static String DoubleFlagChanged = PREFIX + "DoubleFlagChanged"; // vm.flag.double_changed"; + public final static String BooleanFlagChanged = PREFIX + "BooleanFlagChanged"; // "vm.flag.boolean_changed"; + public final static String StringFlagChanged = PREFIX + "StringFlagChanged"; // "vm.flag.string_changed"; + + // Runtime + public final static String VMException = PREFIX + "JavaErrorThrow"; // java.vm.exception_throw"; + public final static String ThreadStart = PREFIX + "ThreadStart";// "java.thread_start"; + public final static String ThreadEnd = PREFIX + "ThreadEnd";// "java.thread_end"; + public final static String ThreadSleep = PREFIX + "ThreadSleep"; // "java.thread_sleep"; + public final static String ThreadPark = PREFIX + "ThreadPark"; // "java.thread_park"; + public final static String JavaMonitorEnter = PREFIX + "JavaMonitorEnter"; // "java.monitor_enter"; + public final static String JavaMonitorWait = PREFIX + "JavaMonitorWait"; // "java.monitor_wait"; + public final static String JavaMonitorInflate = PREFIX + "JavaMonitorInflate"; // "java.monitor_inflate"; + public final static String ClassLoad = PREFIX + "ClassLoad"; // "vm.class.load"; + public final static String ClassDefine = PREFIX + "ClassDefine";// "vm.class.define"; + public final static String ClassUnload = PREFIX + "ClassUnload";// "vm.class.unload"; + public final static String SafepointBegin = PREFIX + "SafepointBegin";// "vm.runtime.safepoint.begin"; + public final static String SafepointStateSyncronization = PREFIX + "SafepointStateSynchronization";// "vm.runtime.safepoint.statesync"; + public final static String SafepointWaitBlocked = PREFIX + "SafepointWaitBlocked";// "vm.runtime.safepoint.waitblocked"; + public final static String SafepointCleanup = PREFIX + "SafepointCleanup";// "vm.runtime.safepoint.cleanup"; + public final static String SafepointCleanupTask = PREFIX + "SafepointCleanupTask";// "vm.runtime.safepoint.cleanuptask"; + public final static String SafepointEnd = PREFIX + "SafepointEnd";// "vm.runtime.safepoint.end"; + public final static String ExecuteVMOperation = PREFIX + "ExecuteVMOperation"; // "vm.runtime.execute_vm_operation"; + public final static String Shutdown = PREFIX + "Shutdown"; // "vm.runtime.shutdown"; + public final static String VMError = PREFIX + "VMError"; // "vm.runtime.vm_error"; + public final static String JavaThreadStatistics = PREFIX + "JavaThreadStatistics"; // "java.statistics.threads"; + public final static String ClassLoadingStatistics = PREFIX + "ClassLoadingStatistics"; // "java.statistics.class_loading"; + public final static String ClassLoaderStatistics = PREFIX + "ClassLoaderStatistics"; // "java.statistics.class_loaders"; + public final static String ThreadAllocationStatistics = PREFIX + "ThreadAllocationStatistics"; // "java.statistics.thread_allocation"; + public final static String ExecutionSample = PREFIX + "ExecutionSample"; // "vm.prof.execution_sample"; + public final static String ExecutionSampling = PREFIX + "ExecutionSampling"; // "vm.prof.execution_sampling_info"; + public final static String ThreadDump = PREFIX + "ThreadDump"; // "vm.runtime.thread_dump"; + public final static String OldObjectSample = PREFIX + "OldObjectSample"; + public final static String BiasedLockRevocation = PREFIX + "BiasedLockRevocation"; // "java.biased_lock_revocation"; + public final static String BiasedLockSelfRevocation = PREFIX + "BiasedLockSelfRevocation"; // "java.biased_lock_self_revocation"; + public final static String BiasedLockClassRevocation = PREFIX + "BiasedLockClassRevocation"; // "java.biased_lock_class_revocation"; + + // GC + public final static String GCHeapSummary = PREFIX + "GCHeapSummary"; // "vm.gc.heap.summary"; + public final static String MetaspaceSummary = PREFIX + "MetaspaceSummary"; // "vm.gc.heap.metaspace_summary"; + public final static String MetaspaceGCThreshold = PREFIX + "MetaspaceGCThreshold"; // "vm.gc.metaspace.gc_threshold"; + public final static String MetaspaceAllocationFailure = PREFIX + "MetaspaceAllocationFailure"; // "vm.gc.metaspace.allocation_failure"; + public final static String MetaspaceOOM = PREFIX + "MetaspaceOOM"; // "vm.gc.metaspace.out_of_memory"; + public final static String MetaspaceChunkFreeListSummary = PREFIX + "MetaspaceChunkFreeListSummary"; // "vm.gc.metaspace.chunk_free_list_summary"; + public final static String PSHeapSummary = PREFIX + "PSHeapSummary"; // "vm.gc.heap.ps_summary"; + public final static String G1HeapSummary = PREFIX + "G1HeapSummary"; // "vm.gc.heap.g1_summary"; + public final static String G1HeapRegionInformation = PREFIX + "G1HeapRegionInformation"; // "vm.gc.detailed.g1_heap_region_information"; + public final static String G1HeapRegionTypeChange = PREFIX + "G1HeapRegionTypeChange"; // "vm.gc.detailed.g1_heap_region_type_change"; + public final static String TenuringDistribution = PREFIX + "TenuringDistribution"; // "vm.gc.detailed.tenuring_distribution"; + public final static String GarbageCollection = PREFIX + "GarbageCollection"; // "vm.gc.collector.garbage_collection"; + public final static String ParallelOldCollection = PREFIX + "ParallelOldGarbageCollection"; // "vm.gc.collector.parold_garbage_collection"; + public final static String YoungGarbageCollection = PREFIX + "YoungGarbageCollection";// "vm.gc.collector.young_garbage_collection"; + public final static String OldGarbageCollection = PREFIX + "OldGarbageCollection"; // "vm.gc.collector.old_garbage_collection"; + public final static String G1GarbageCollection = PREFIX + "G1GarbageCollection"; // "vm.gc.collector.g1_garbage_collection"; + public final static String G1MMU = PREFIX + "G1MMU"; // "vm.gc.detailed.g1_mmu_info"; + public final static String EvacuationInfo = PREFIX + "EvacuationInfo";// "vm.gc.detailed.evacuation_info"; + public final static String GCReferenceStatistics = PREFIX + "GCReferenceStatistics";// "vm.gc.reference.statistics"; + public final static String ObjectCountAfterGC = PREFIX + "ObjectCountAfterGC"; // "vm.gc.detailed.object_count_after_gc"; + public final static String PromoteObjectInNewPLAB = PREFIX + "PromoteObjectInNewPLAB";// "vm.gc.detailed.object_promotion_in_new_PLAB"; + public final static String PromoteObjectOutsidePLAB = PREFIX + "PromoteObjectOutsidePLAB"; // "vm.gc.detailed.object_promotion_outside_PLAB"; + public final static String PromotionFailed = PREFIX + "PromotionFailed"; // "vm.gc.detailed.promotion_failed"; + public final static String EvacuationFailed = PREFIX + "EvacuationFailed"; // "vm.gc.detailed.evacuation_failed"; + public final static String ConcurrentModeFailure = PREFIX + "ConcurrentModeFailure"; // "vm.gc.detailed.concurrent_mode_failure"; + public final static String GCPhasePause = PREFIX + "GCPhasePause"; // "vm.gc.phases.pause"; + public final static String GCPhasePauseLevel1 = PREFIX + "GCPhasePauseLevel1"; // "vm.gc.phases.pause_level_1"; + public final static String GCPhasePauseLevel2 = PREFIX + "GCPhasePauseLevel2"; // vm.gc.phases.pause_level_2"; + public final static String GCPhasePauseLevel3 = PREFIX + "GCPhasePauseLevel3"; // "vm.gc.phases.pause_level_3"; + public final static String ObjectCount = PREFIX + "ObjectCount"; // "vm.gc.detailed.object_count"; + public final static String GCConfiguration = PREFIX + "GCConfiguration"; // vm.gc.configuration.gc"; + public final static String GCSurvivorConfiguration = PREFIX + "GCSurvivorConfiguration"; // "vm.gc.configuration.survivor"; + public final static String GCTLABConfiguration = PREFIX + "GCTLABConfiguration";// "vm.gc.configuration.tlab"; + public final static String GCHeapConfiguration = PREFIX + "GCHeapConfiguration";// "vm.gc.configuration.heap"; + public final static String YoungGenerationConfiguration = PREFIX + "YoungGenerationConfiguration"; // "vm.gc.configuration.young_generation"; + public final static String G1AdaptiveIHOP = PREFIX + "G1AdaptiveIHOP"; // "vm/gc/detailed/g1_adaptive_ihop_status" + public final static String G1EvacuationYoungStatistics = PREFIX + "G1EvacuationYoungStatistics"; // "vm/gc/detailed/g1_evac_young_stats" + public final static String G1EvacuationOldStatistics = PREFIX + "G1EvacuationOldStatistics"; // "vm/gc/detailed/g1_evac_old_stats" + public final static String G1BasicIHOP = PREFIX + "G1BasicIHOP"; // "vm/gc/detailed/g1_basic_ihop_status" + public final static String AllocationRequiringGC = PREFIX + "AllocationRequiringGC"; // "vm/gc/detailed/allocation_requiring_gc" + + // Compiler + public final static String Compilation = PREFIX + "Compilation";// "vm.compiler.compilation"; + public final static String CompilerPhase = PREFIX + "CompilerPhase";// "vm.compiler.phase"; + public final static String CompilationFailure = PREFIX + "CompilationFailure";// "vm.compiler.failure"; + public final static String CompilerInlining = PREFIX + "CompilerInlining";// "vm.compiler.optimization.inlining"; + public final static String CompilerStatistics = PREFIX + "CompilerStatistics";// "vm.compiler.stats"; + public final static String CompilerConfig = PREFIX + "CompilerConfiguration";// "vm.compiler.config"; + public final static String CodeCacheStatistics = PREFIX + "CodeCacheStatistics";// "vm.code_cache.stats"; + public final static String CodeCacheConfiguration = PREFIX + "CodeCacheConfiguration";// "vm.code_cache.config"; + public final static String CodeSweeperStatistics = PREFIX + "CodeSweeperStatistics";// "vm.code_sweeper.stats"; + public final static String CodeSweeperConfiguration = PREFIX + "CodeSweeperConfiguration";// "vm.code_sweeper.config"; + public final static String SweepCodeCache = PREFIX + "SweepCodeCache";// "vm.code_sweeper.sweep"; + public final static String CodeCacheFull = PREFIX + "CodeCacheFull";// "vm.code_cache.full"; + public final static String ObjectAllocationInNewTLAB = PREFIX + "ObjectAllocationInNewTLAB";// "java.object_alloc_in_new_TLAB"; + public final static String ObjectAllocationOutsideTLAB = PREFIX + "ObjectAllocationOutsideTLAB";// "java.object_alloc_outside_TLAB"; + + // OS + public final static String OSInformation = PREFIX + "OSInformation";// "os.information"; + public final static String CPUInformation = PREFIX + "CPUInformation";// "os.processor.cpu_information"; + public final static String CPULoad = PREFIX + "CPULoad";// "os.processor.cpu_load"; + public final static String ThreadCPULoad = PREFIX + "ThreadCPULoad"; // "os.processor.thread_cpu_load"; + public final static String SystemProcess = PREFIX + "SystemProcess";// "os.system_process"; + public final static String ThreadContextSwitchRate = PREFIX + "ThreadContextSwitchRate";// "os.processor.context_switch_rate"; + public final static String InitialEnvironmentVariable = PREFIX + "InitialEnvironmentVariable";// "os.initial_environment_variable"; + public final static String NativeLibrary = PREFIX + "NativeLibrary";// "vm.runtime.native_libraries"; + public final static String PhysicalMemory = PREFIX + "PhysicalMemory";// "os.memory.physical_memory"; + + // JDK + public static final String FileForce = PREFIX + "FileForce";// "java.file_force"; + public static final String FileRead = PREFIX + "FileRead";// "java.file_read"; + public static final String FileWrite = PREFIX + "FileWrite"; // "java.file_write"; + public static final String SocketRead = PREFIX + "SocketRead";// "java.socket_read"; + public static final String SocketWrite = PREFIX + "SocketWrite";// "java.socket_write"; + public final static String ExceptionStatistics = PREFIX + "ExceptionStatistics"; // "java.statistics.throwables"; + public final static String JavaExceptionThrow = PREFIX + "JavaExceptionThrow"; // java.exception_throw"; + public final static String JavaErrorThrow = PREFIX + "JavaErrorThrow"; // "java.error_throw";; + public final static String ModuleRequire = PREFIX + "ModuleRequire"; // "com.oracle.jdk.ModuleRequire" + public final static String ModuleExport = PREFIX + "ModuleExport"; // "com.oracle.jdk.ModuleExport" + + // Flight Recorder + public final static String DumpReason = PREFIX + "DumpReason";// "flight_recorder.dump_reason"; + public final static String DataLoss = PREFIX + "DataLoss"; // "flight_recorder.data_loss"; + public final static String CPUTimeStampCounter = PREFIX + "CPUTimeStampCounter";// "os.processor.cpu_tsc"; + public final static String ActiveRecording = PREFIX + "ActiveRecording";//"com.oracle.jdk.ActiveRecording" + public final static String ActiveSetting = PREFIX + "ActiveSetting";//"com.oracle.jdk.ActiveSetting" + + public static boolean isGcEvent(EventType et) { + return et.getCategoryNames().contains(GC_CATEGORY); + } + +} --- /dev/null 2019-01-28 17:49:08.000000000 +0800 +++ new/test/testlibrary/com/oracle/java/testlibrary/jfr/EventVerifier.java 2019-01-28 17:49:08.000000000 +0800 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2013, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.java.testlibrary.jfr; + +import jdk.jfr.consumer.RecordedEvent; + +public abstract class EventVerifier { + protected final RecordedEvent event; + + public EventVerifier(RecordedEvent event) { + this.event = event; + } + + public > void verifyEquals(String name, T value) { + Events.assertField(event, name).equal(value); + } + + public void verifyContains(String name, String value) { + Events.assertField(event, name).containsAny(value); + } + + protected long gigabytes(int num) { + return num * 1024L * 1024L * 1024L; + } + + protected long megabytes(int num) { + return num * 1024L * 1024L; + } + + public abstract void verify() throws Exception; +} --- /dev/null 2019-01-28 17:49:09.000000000 +0800 +++ new/test/testlibrary/com/oracle/java/testlibrary/jfr/Events.java 2019-01-28 17:49:09.000000000 +0800 @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2013, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.java.testlibrary.jfr; + +import static com.oracle.java.testlibrary.Asserts.assertEquals; +import static com.oracle.java.testlibrary.Asserts.assertFalse; +import static com.oracle.java.testlibrary.Asserts.assertNotNull; +import static com.oracle.java.testlibrary.Asserts.assertTrue; +import static com.oracle.java.testlibrary.Asserts.fail; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; + +import jdk.jfr.AnnotationElement; +import jdk.jfr.EventType; +import jdk.jfr.Recording; +import jdk.jfr.SettingDescriptor; +import jdk.jfr.ValueDescriptor; +import jdk.jfr.consumer.RecordingFile; +import com.oracle.java.testlibrary.Asserts; +import com.oracle.java.testlibrary.ProcessTools; +import jdk.jfr.consumer.RecordedClass; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedObject; +import jdk.jfr.consumer.RecordedThread; +import jdk.jfr.consumer.RecordedThreadGroup; + + +/** + * Helper class to verify RecordedEvent content + */ +public class Events { + + public static EventField assertField(RecordedEvent event, String name) { + String[] partNames = name.split("\\."); + RecordedObject struct = event; + try { + for (int i=0; i valueDescriptors = struct.getFields(); + for (ValueDescriptor d : valueDescriptors) { + if (name.equals(d.getName())) { + return d; + } + } + System.out.printf("Failed struct:%s", struct.toString()); + fail(String.format("Field %s not in struct", name)); + return null; + } + + public static void hasEvents(List events) { + assertFalse(events.isEmpty(), "No events"); + } + + public static void hasEvents(RecordingFile file) { + assertTrue(file.hasMoreEvents(), "No events"); + } + + public static void assertEventThread(RecordedEvent event) { + RecordedThread eventThread = event.getThread(); + if (eventThread == null) { + System.out.printf("Failed event:%n%s%n", event.toString()); + fail("No thread in event"); + } + } + + public static void assertJavaMethod(RecordedEvent event) { + assertField(event, "method.name").notEmpty(); + assertField(event, "method.descriptor").notEmpty(); + assertField(event, "method.modifiers").atLeast(0); + assertField(event, "method.hidden"); + assertField(event, "method.type.name").notEmpty(); + assertField(event, "method.type.modifiers").atLeast(0); + } + + public static void assertEventThread(RecordedEvent event, Thread thread) { + assertThread(event.getThread(), thread); + } + + public static void assertEventThread(RecordedEvent event, String structName, Thread thread) { + assertThread(assertField(event, structName).notNull().getValue(), thread); + } + + private static void assertThread(RecordedThread eventThread, Thread thread) { + assertNotNull(eventThread, "Thread in event was null"); + assertEquals(eventThread.getJavaThreadId(), thread.getId(), "Wrong thread id"); + assertEquals(eventThread.getJavaName(), thread.getName(), "Wrong thread name"); + + ThreadGroup threadGroup = thread.getThreadGroup(); + RecordedThreadGroup eventThreadGroup = eventThread.getThreadGroup(); + assertNotNull(eventThreadGroup, "eventThreadGroup was null"); + + // Iterate and check all threadGroups + while (eventThreadGroup != null) { + final String groupName = eventThreadGroup.getName(); + if (threadGroup != null) { + assertEquals(groupName, threadGroup.getName(), "Wrong threadGroup name"); + threadGroup = threadGroup.getParent(); + } else { + assertNotNull(groupName, "threadGroup name was null"); + assertFalse(groupName.isEmpty(), "threadGroup name was empty"); + } + eventThreadGroup = eventThreadGroup.getParent(); + } + } + + public static boolean hasField(RecordedEvent event, String name) { + return event.getFields().stream().map(vd -> vd.getName()).anyMatch(s -> s.equals(name)); + } + + public static boolean isEventType(RecordedEvent event, String typeName) { + return typeName.equals(event.getEventType().getName()); + } + + + /** + * Creates a list of events from a recording. + * + * @param recording recording, not {@code null} + * @return an a list, not null + * @throws IOException if an event set could not be created due to I/O + * errors. + */ + public static List fromRecording(Recording recording) throws IOException { + return RecordingFile.readAllEvents(makeCopy(recording)); + } + + public static RecordingFile copyTo(Recording r) throws IOException { + return new RecordingFile(makeCopy(r)); + } + + private static Path makeCopy(Recording recording) throws IOException { + Path p = recording.getDestination(); + int pid = 0; + try { + pid = ProcessTools.getProcessId(); + } catch (Exception e) { + //do nothing, let's use 0 + } + + if (p == null) { + File directory = new File("."); + // FIXME: Must come up with a way to give human-readable name + // this will at least not clash when running parallel. + p = new File(directory.getAbsolutePath(), "recording-" + recording.getId() + "-pid" + pid + ".jfr").toPath(); + recording.dump(p); + } + return p; + } + + public static void hasAnnotation(ValueDescriptor field, Class annotationClass) throws Exception { + AnnotationElement a = getAnnotation(field, annotationClass); + if (a == null) { + throw new Exception("Expected " + annotationClass.getSimpleName() + " on field " + field.getName()); + } + } + + public static void assertAnnotation(ValueDescriptor field, Class annotationClass, String value) throws Exception { + AnnotationElement a = getAnnotation(field, annotationClass); + Object v = a.getValue("value"); + if (!v.equals(value)) { + throw new Exception("Expected " + annotationClass.getSimpleName() + " on field " + field.getName() + " to have value " + value + ", but got " + v); + } + } + + // candidate for moving into API + public static AnnotationElement getAnnotation(ValueDescriptor v, Class clazz) throws Exception { + for (AnnotationElement a : v.getAnnotationElements()) { + if (a.getTypeName().equals(clazz.getName())) { + return a; + } + } + + throw new Exception("Could not find annotation " + clazz.getName()); + } + + // candidate for moving into API + public static AnnotationElement getAnnotationByName(EventType t, String name) throws Exception { + for (AnnotationElement a : t.getAnnotationElements()) { + if (a.getTypeName().equals(name)) { + return a; + } + } + throw new Exception("Could not find annotation '" + name + " in type " + t.getName()); + } + + // candidate for moving into API + public static SettingDescriptor getSetting(EventType type, String name) { + for (SettingDescriptor s : type.getSettingDescriptors()) { + if (s.getName().equals(name)) { + return s; + } + } + throw new IllegalArgumentException("Could not setting with name " + name); + } + + public static void hasEvent(Recording r, String name) throws IOException { + List events = fromRecording(r); + Events.hasEvents(events); + Events.hasEvent(events, name); + } + + public static void hasEvent(List events, String name) throws IOException { + if (!containsEvent(events, name)) { + Asserts.fail("Missing event " + name + " in recording " + events.toString()); + } + } + + public static void hasNotEvent(List events, String name) throws IOException { + if (containsEvent(events, name)) { + Asserts.fail("Rercording should not contain event " + name + " " + events.toString()); + } + } + + private static boolean containsEvent(List events, String name) { + for (RecordedEvent event : events) { + if (event.getEventType().getName().equals(name)) { + return true; + } + } + return false; + } +} --- /dev/null 2019-01-28 17:49:10.000000000 +0800 +++ new/test/testlibrary/com/oracle/java/testlibrary/jfr/GCHelper.java 2019-01-28 17:49:10.000000000 +0800 @@ -0,0 +1,468 @@ +/* + * Copyright (c) 2013, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.java.testlibrary.jfr; + +import static com.oracle.java.testlibrary.Asserts.assertEquals; +import static com.oracle.java.testlibrary.Asserts.assertNotEquals; +import static com.oracle.java.testlibrary.Asserts.assertNotNull; +import static com.oracle.java.testlibrary.Asserts.assertNull; +import static com.oracle.java.testlibrary.Asserts.fail; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Stack; + +import jdk.jfr.ValueDescriptor; +import jdk.jfr.consumer.RecordedEvent; + +/** + * Mixed helper classes to test GC events. + */ +public class GCHelper { + public static final String event_garbage_collection = EventNames.GarbageCollection; + public static final String event_young_garbage_collection = EventNames.YoungGarbageCollection; + public static final String event_old_garbage_collection = EventNames.OldGarbageCollection; + public static final String event_parold_garbage_collection = EventNames.ParallelOldCollection; + public static final String event_g1_garbage_collection = EventNames.G1GarbageCollection; + public static final String event_heap_summary = EventNames.GCHeapSummary; + public static final String event_heap_ps_summary = EventNames.PSHeapSummary; + public static final String event_heap_metaspace_summary = EventNames.MetaspaceSummary; + public static final String event_reference_statistics = EventNames.GCReferenceStatistics; + public static final String event_phases_pause = EventNames.GCPhasePause; + public static final String event_phases_level_1 = EventNames.GCPhasePauseLevel1; + public static final String event_phases_level_2 = EventNames.GCPhasePauseLevel2; + public static final String event_phases_level_3 = EventNames.GCPhasePauseLevel3; + + public static final String gcG1New = "G1New"; + public static final String gcParNew = "ParNew"; + public static final String gcDefNew = "DefNew"; + public static final String gcParallelScavenge = "ParallelScavenge"; + public static final String gcG1Old = "G1Old"; + public static final String gcG1Full = "G1Full"; + public static final String gcConcurrentMarkSweep = "ConcurrentMarkSweep"; + public static final String gcSerialOld = "SerialOld"; + public static final String gcPSMarkSweep = "PSMarkSweep"; + public static final String gcParallelOld = "ParallelOld"; + public static final String pauseLevelEvent = "GCPhasePauseLevel"; + + private static final List g1HeapRegionTypes; + private static PrintStream defaultErrorLog = null; + + public static int getGcId(RecordedEvent event) { + return Events.assertField(event, "gcId").getValue(); + } + + public static boolean isGcEvent(RecordedEvent event) { + for (ValueDescriptor v : event.getFields()) { + if ("gcId".equals(v.getName())) { + return true; + } + } + return false; + } + +// public static String getEventDesc(RecordedEvent event) { +// final String path = event.getEventType().getName(); +// if (!isGcEvent(event)) { +// return path; +// } +// if (event_garbage_collection.equals(path)) { +// String name = Events.assertField(event, "name").getValue(); +// String cause = Events.assertField(event, "cause").getValue(); +// return String.format("path=%s, gcId=%d, endTime=%d, name=%s, cause=%s, startTime=%d", +// path, getGcId(event), event.getEndTime(), name, cause, event.getStartTime()); +// } else { +// return String.format("path=%s, gcId=%d, endTime=%d", path, getGcId(event), event.getEndTime()); +// } +// } + + public static RecordedEvent getConfigEvent(List events) throws Exception { + for (RecordedEvent event : events) { + if (EventNames.GCConfiguration.equals(event.getEventType().getName())) { + return event; + } + } + fail("Could not find event " + EventNames.GCConfiguration); + return null; + } + + public static void callSystemGc(int num, boolean withGarbage) { + for (int i = 0; i < num; i++) { + if (withGarbage) { + makeGarbage(); + } + System.gc(); + } + } + + private static void makeGarbage() { + Object[] garbage = new Object[1024]; + for (int i = 0; i < 1024; i++) { + garbage[i] = new Object(); + } + } + + // Removes gcEvents with lowest and highest gcID. This is used to filter out + // any incomplete GCs if the recording started/stopped in the middle of a GC. + // We also filters out events without gcId. Those events are not needed. + public static List removeFirstAndLastGC(List events) { + int minGcId = Integer.MAX_VALUE; + int maxGcId = Integer.MIN_VALUE; + // Find min/max gcId + for (RecordedEvent event : events) { + if (Events.hasField(event, "gcId")) { + int gcId = Events.assertField(event, "gcId").getValue(); + minGcId = Math.min(gcId, minGcId); + maxGcId = Math.max(gcId, maxGcId); + } + } + + // Add all events except those with gcId = min/max gcId + List filteredEvents = new ArrayList<>(); + for (RecordedEvent event : events) { + if (Events.hasField(event, "gcId")) { + int gcId = Events.assertField(event, "gcId").getValue(); + if (gcId != minGcId && gcId != maxGcId) { + filteredEvents.add(event); + } + } + } + return filteredEvents; + } + + public static Map beanCollectorTypes = new HashMap<>(); + public static Set collectorOverrides = new HashSet<>(); + public static Map requiredEvents = new HashMap<>(); + + static { + // young GarbageCollectionMXBeans. + beanCollectorTypes.put("G1 Young Generation", true); + beanCollectorTypes.put("Copy", true); + beanCollectorTypes.put("PS Scavenge", true); + beanCollectorTypes.put("ParNew", true); + + // old GarbageCollectionMXBeans. + beanCollectorTypes.put("G1 Old Generation", false); + beanCollectorTypes.put("ConcurrentMarkSweep", false); + beanCollectorTypes.put("PS MarkSweep", false); + beanCollectorTypes.put("MarkSweepCompact", false); + + // List of expected collector overrides. "A.B" means that collector A may use collector B. + collectorOverrides.add("G1Old.G1Full"); + collectorOverrides.add("G1Old.SerialOld"); + collectorOverrides.add("ConcurrentMarkSweep.SerialOld"); + collectorOverrides.add("SerialOld.PSMarkSweep"); + + requiredEvents.put(gcG1New, new String[] {event_heap_summary, event_young_garbage_collection}); + requiredEvents.put(gcParNew, new String[] {event_heap_summary, event_heap_metaspace_summary, event_phases_pause, event_phases_level_1, event_young_garbage_collection}); + requiredEvents.put(gcDefNew, new String[] {event_heap_summary, event_heap_metaspace_summary, event_phases_pause, event_phases_level_1, event_young_garbage_collection}); + requiredEvents.put(gcParallelScavenge, new String[] {event_heap_summary, event_heap_ps_summary, event_heap_metaspace_summary, event_reference_statistics, event_phases_pause, event_phases_level_1, event_young_garbage_collection}); + requiredEvents.put(gcG1Old, new String[] {event_heap_summary, event_old_garbage_collection}); + requiredEvents.put(gcG1Full, new String[] {event_heap_summary, event_heap_metaspace_summary, event_phases_pause, event_phases_level_1, event_old_garbage_collection}); + requiredEvents.put(gcConcurrentMarkSweep, new String[] {event_phases_pause, event_phases_level_1, event_old_garbage_collection}); + requiredEvents.put(gcSerialOld, new String[] {event_heap_summary, event_heap_metaspace_summary, event_phases_pause, event_phases_level_1, event_old_garbage_collection}); + requiredEvents.put(gcParallelOld, new String[] {event_heap_summary, event_heap_ps_summary, event_heap_metaspace_summary, event_reference_statistics, event_phases_pause, event_phases_level_1, event_old_garbage_collection, event_parold_garbage_collection}); + + String[] g1HeapRegionTypeLiterals = new String[] { + "Free", + "Eden", + "Survivor", + "Starts Humongous", + "Continues Humongous", + "Old", + "Archive" + }; + + g1HeapRegionTypes = Collections.unmodifiableList(Arrays.asList(g1HeapRegionTypeLiterals)); + } + + /** + * Contains all GC events belonging to the same GC (same gcId). + */ + public static class GcBatch { + private List events = new ArrayList<>(); + + public int getGcId() { + if (events.isEmpty()) { + return -1; + } + return GCHelper.getGcId(events.get(0)); + } + + public String getName() { + RecordedEvent endEvent = getEndEvent(); + String name = endEvent == null ? null : Events.assertField(endEvent, "name").getValue(); + return name == null ? "null" : name; + } + + public RecordedEvent getEndEvent() { + return getEvent(event_garbage_collection); + } + + public boolean addEvent(RecordedEvent event) { + if (!events.isEmpty()) { + assertEquals(getGcId(), GCHelper.getGcId(event), "Wrong gcId in event. Error in test code."); + } + boolean isEndEvent = event_garbage_collection.equals(event.getEventType().getName()); + if (isEndEvent) { + // Verify that we have not already got a garbage_collection event with this gcId. + assertNull(getEndEvent(), String.format("Multiple %s for gcId %d", event_garbage_collection, getGcId())); + } + events.add(event); + return isEndEvent; + } + + public boolean isYoungCollection() { + boolean isYoung = containsEvent(event_young_garbage_collection); + boolean isOld = containsEvent(event_old_garbage_collection); + assertNotEquals(isYoung, isOld, "isYoung and isOld was same for batch: " + toString()); + return isYoung; + } + + public int getEventCount() { + return events.size(); + } + + public RecordedEvent getEvent(int index) { + return events.get(index); + } + + public List getEvents() { + return events; + } + + public RecordedEvent getEvent(String eventPath) { + for (RecordedEvent event : events) { + if (eventPath.equals(event.getEventType().getName())) { + return event; + } + } + return null; + } + + public boolean containsEvent(String eventPath) { + return getEvent(eventPath) != null; + } + + public String toString() { + RecordedEvent endEvent = getEndEvent(); + Instant startTime = Instant.EPOCH; + String cause = "?"; + String name = "?"; + if (endEvent != null) { + name = getName(); + startTime = endEvent.getStartTime(); + cause = Events.assertField(endEvent, "cause").getValue(); + } + return String.format("GcEvent: gcId=%d, method=%s, cause=%s, startTime=%s", + getGcId(), name, cause, startTime); + } + + public String getLog() { + StringBuilder sb = new StringBuilder(); + sb.append(this.toString() + System.getProperty("line.separator")); + for (RecordedEvent event : events) { + sb.append(String.format("event: %s%n", event)); + } + return sb.toString(); + } + + // Group all events info batches. + public static List createFromEvents(List events) throws Exception { + Stack openGcIds = new Stack<>(); + List batches = new ArrayList<>(); + GcBatch currBatch = null; + + for (RecordedEvent event : events) { + if (!isGcEvent(event)) { + continue; + } + int gcId = GCHelper.getGcId(event); + if (currBatch == null || currBatch.getGcId() != gcId) { + currBatch = null; + // Search for existing batch + for (GcBatch loopBatch : batches) { + if (gcId == loopBatch.getGcId()) { + currBatch = loopBatch; + break; + } + } + if (currBatch == null) { + // No existing batch. Create new. + currBatch = new GcBatch(); + batches.add(currBatch); + openGcIds.push(new Integer(gcId)); + } + } + boolean isEndEvent = currBatch.addEvent(event); + if (isEndEvent) { + openGcIds.pop(); + } + } + // Verify that all start_garbage_collection events have received a corresponding "garbage_collection" event. + for (GcBatch batch : batches) { + if (batch.getEndEvent() == null) { + System.out.println(batch.getLog()); + } + assertNotNull(batch.getEndEvent(), "GcBatch has no end event"); + } + return batches; + } + } + + /** + * Contains number of collections and sum pause time for young and old collections. + */ + public static class CollectionSummary { + public long collectionCountOld; + public long collectionCountYoung; + public long collectionTimeOld; + public long collectionTimeYoung; + private Set names = new HashSet<>(); + + public void add(String collectorName, boolean isYoung, long count, long time) { + if (isYoung) { + collectionCountYoung += count; + collectionTimeYoung += time; + } else { + collectionCountOld += count; + collectionTimeOld += time; + } + if (!names.contains(collectorName)) { + names.add(collectorName); + } + } + + public long sum() { + return collectionCountOld + collectionCountYoung; + } + + public CollectionSummary calcDelta(CollectionSummary prev) { + CollectionSummary delta = new CollectionSummary(); + delta.collectionCountOld = this.collectionCountOld - prev.collectionCountOld; + delta.collectionTimeOld = this.collectionTimeOld - prev.collectionTimeOld; + delta.collectionCountYoung = this.collectionCountYoung - prev.collectionCountYoung; + delta.collectionTimeYoung = this.collectionTimeYoung - prev.collectionTimeYoung; + delta.names.addAll(this.names); + delta.names.addAll(prev.names); + return delta; + } + + public static CollectionSummary createFromMxBeans() { + CollectionSummary summary = new CollectionSummary(); + List gcBeans = ManagementFactory.getGarbageCollectorMXBeans(); + for (int c=0; c batches) { + CollectionSummary summary = new CollectionSummary(); + for (GcBatch batch : batches) { + RecordedEvent endEvent = batch.getEndEvent(); + assertNotNull(endEvent, "No end event in batch with gcId " + batch.getGcId()); + String name = batch.getName(); + summary.add(name, batch.isYoungCollection(), 1, Events.assertField(endEvent, "sumOfPauses").getValue()); + } + return summary; + } + + public String toString() { + StringBuilder collectorNames = new StringBuilder(); + for (String s : names) { + if (collectorNames.length() > 0) { + collectorNames.append(", "); + } + collectorNames.append(s); + } + return String.format("CollectionSummary: young.collections=%d, young.time=%d, old.collections=%d, old.time=%d, collectors=(%s)", + collectionCountYoung, collectionTimeYoung, collectionCountOld, collectionTimeOld, collectorNames); + } + } + + public static PrintStream getDefaultErrorLog() { + if (defaultErrorLog == null) { + try { + defaultErrorLog = new PrintStream(new FileOutputStream("error.log", true)); + } catch (IOException e) { + e.printStackTrace(); + defaultErrorLog = System.err; + } + } + return defaultErrorLog; + } + + public static void log(Object msg) { + log(msg, System.err); + log(msg, getDefaultErrorLog()); + } + + public static void log(Object msg, PrintStream ps) { + ps.println(msg); + } + + public static boolean isValidG1HeapRegionType(final String type) { + return g1HeapRegionTypes.contains(type); + } + + /** + * Helper function to align heap size up. + * + * @param value + * @param alignment + * @return aligned value + */ + public static long alignUp(long value, long alignment) { + return (value + alignment - 1) & ~(alignment - 1); + } + + /** + * Helper function to align heap size down. + * + * @param value + * @param alignment + * @return aligned value + */ + public static long alignDown(long value, long alignment) { + return value & ~(alignment - 1); + } +} --- /dev/null 2019-01-28 17:49:11.000000000 +0800 +++ new/test/testlibrary/com/oracle/java/testlibrary/jfr/HeapSummaryEventAllGcs.java 2019-01-28 17:49:11.000000000 +0800 @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2013, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.java.testlibrary.jfr; + +import java.time.Duration; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import com.oracle.java.testlibrary.Asserts; +import com.oracle.java.testlibrary.jfr.EventNames; +import com.oracle.java.testlibrary.jfr.Events; +import com.oracle.java.testlibrary.jfr.GCHelper; + +public class HeapSummaryEventAllGcs { + + public static void test(String expectedYoungCollector, String expectedOldCollector) throws Exception { + Recording recording = new Recording(); + recording.enable(EventNames.GCConfiguration); + recording.enable(EventNames.GCHeapSummary); + recording.enable(EventNames.PSHeapSummary); + recording.enable(EventNames.MetaspaceSummary).withThreshold(Duration.ofMillis(0)); + + recording.start(); + // To eliminate the risk of being in the middle of a GC when the recording starts/stops, + // we run 5 System.gc() and ignores the first and last GC. + GCHelper.callSystemGc(5, true); + recording.stop(); + + if (!checkCollectors(recording, expectedYoungCollector, expectedOldCollector)) { + return; + } + List events = GCHelper.removeFirstAndLastGC(Events.fromRecording(recording)); + for (RecordedEvent event : events) { + System.out.println("Event:" + event); + } + + Asserts.assertFalse(events.isEmpty(), "Expected at least one event."); + Asserts.assertEquals(events.size() % 2, 0, "Events should come in pairs"); + + int lastHeapGcId = -1; + int lastPSGcId = -1; + int lastMetaspaceGcId = -1; + + for (RecordedEvent event : events) { + final String eventName = event.getEventType().getName(); + switch (eventName) { + case EventNames.GCHeapSummary: + lastHeapGcId = checkGcId(event, lastHeapGcId); + checkHeapEventContent(event); + break; + case EventNames.PSHeapSummary: + lastPSGcId = checkGcId(event, lastPSGcId); + checkPSEventContent(event); + break; + case EventNames.MetaspaceSummary: + lastMetaspaceGcId = checkGcId(event, lastMetaspaceGcId); + checkMetaspaceEventContent(event); + break; + default: + System.out.println("Failed event: " + event); + Asserts.fail("Unknown event type: " + eventName); + } + } + + // Sanity check. Not complete. + Asserts.assertEquals(lastHeapGcId, lastMetaspaceGcId, "Should have gotten perm gen events for all GCs"); + } + + private static void checkMetaspaceEventContent(RecordedEvent event) { + long totalUsed = Events.assertField(event, "metaspace.used").atLeast(0L).getValue(); + long totalCommitted = Events.assertField(event, "metaspace.committed").atLeast(totalUsed).getValue(); + long totalReserved = Events.assertField(event, "metaspace.reserved").atLeast(totalCommitted).getValue(); + + long dataUsed = Events.assertField(event, "dataSpace.used").atLeast(0L).getValue(); + long dataCommitted = Events.assertField(event, "dataSpace.committed").atLeast(dataUsed).getValue(); + long dataReserved = Events.assertField(event, "dataSpace.reserved").atLeast(dataCommitted).getValue(); + + long classUsed = Events.assertField(event, "classSpace.used").atLeast(0L).getValue(); + long classCommitted = Events.assertField(event, "classSpace.committed").atLeast(classUsed).getValue(); + long classReserved = Events.assertField(event, "classSpace.reserved").atLeast(classCommitted).getValue(); + + Asserts.assertEquals(dataCommitted + classCommitted, totalCommitted, "Wrong committed memory"); + Asserts.assertEquals(dataUsed + classUsed, totalUsed, "Wrong used memory"); + Asserts.assertEquals(dataReserved + classReserved, totalReserved, "Wrong reserved memory"); + } + + private static int checkGcId(RecordedEvent event, int currGcId) { + int gcId = Events.assertField(event, "gcId").getValue(); + String when = Events.assertField(event, "when").notEmpty().getValue(); + if ("Before GC".equals(when)) { + Asserts.assertGreaterThan(gcId, currGcId, "gcId should be increasing"); + } else { + Asserts.assertEquals(gcId, currGcId, "After should have same gcId as last Before event"); + } + return gcId; + } + + private static void checkHeapEventContent(RecordedEvent event) { + checkVirtualSpace(event, "heapSpace"); + long heapUsed = Events.assertField(event, "heapUsed").atLeast(0L).getValue(); + long start = Events.assertField(event, "heapSpace.start").atLeast(0L).getValue(); + long committedEnd = Events.assertField(event, "heapSpace.committedEnd").above(start).getValue(); + Asserts.assertLessThanOrEqual(heapUsed, committedEnd- start, "used can not exceed size"); + } + + private static void checkPSEventContent(RecordedEvent event) { + checkVirtualSpace(event, "oldSpace"); + checkVirtualSpace(event, "youngSpace"); + checkSpace(event, "oldObjectSpace"); + checkSpace(event, "edenSpace"); + checkSpace(event, "fromSpace"); + checkSpace(event, "toSpace"); + + checkPSYoungSizes(event); + checkPSYoungStartEnd(event); + } + + private static void checkPSYoungSizes(RecordedEvent event) { + long youngSize = (long)Events.assertField(event, "youngSpace.committedEnd").getValue() - + (long)Events.assertField(event, "youngSpace.start").getValue(); + long edenSize = (long)Events.assertField(event, "edenSpace.end").getValue() - + (long)Events.assertField(event, "edenSpace.start").getValue(); + long fromSize = (long)Events.assertField(event, "fromSpace.end").getValue() - + (long)Events.assertField(event, "fromSpace.start").getValue(); + long toSize = (long)Events.assertField(event, "toSpace.end").getValue() - + (long)Events.assertField(event, "toSpace.start").getValue(); + Asserts.assertGreaterThanOrEqual(youngSize, edenSize + fromSize + toSize, "Young sizes don't match"); + } + + private static void checkPSYoungStartEnd(RecordedEvent event) { + long oldEnd = Events.assertField(event, "oldSpace.reservedEnd").getValue(); + long youngStart = Events.assertField(event, "youngSpace.start").getValue(); + long youngEnd = Events.assertField(event, "youngSpace.committedEnd").getValue(); + long edenStart = Events.assertField(event, "edenSpace.start").getValue(); + long edenEnd = Events.assertField(event, "edenSpace.end").getValue(); + long fromStart = Events.assertField(event, "fromSpace.start").getValue(); + long fromEnd = Events.assertField(event, "fromSpace.end").getValue(); + long toStart = Events.assertField(event, "toSpace.start").getValue(); + long toEnd = Events.assertField(event, "toSpace.end").getValue(); + Asserts.assertEquals(oldEnd, youngStart, "Young should start where old ends"); + Asserts.assertEquals(youngStart, edenStart, "Eden should be placed first in young"); + if (fromStart < toStart) { + // [eden][from][to] + Asserts.assertGreaterThanOrEqual(fromStart, edenEnd, "From should start after eden"); + Asserts.assertLessThanOrEqual(fromEnd, toStart, "To should start after From"); + Asserts.assertLessThanOrEqual(toEnd, youngEnd, "To should start after From"); + } else { + // [eden][to][from] + Asserts.assertGreaterThanOrEqual(toStart, edenEnd, "From should start after eden"); + Asserts.assertLessThanOrEqual(toEnd, fromStart, "To should start after From"); + Asserts.assertLessThanOrEqual(fromEnd, youngEnd, "To should start after From"); + } + } + + private static void checkVirtualSpace(RecordedEvent event, String structName) { + long start = Events.assertField(event, structName + ".start").atLeast(0L).getValue(); + long committedEnd = Events.assertField(event, structName + ".committedEnd").above(start).getValue(); + Events.assertField(event, structName + ".reservedEnd").atLeast(committedEnd); + long committedSize = Events.assertField(event, structName + ".committedSize").atLeast(0L).getValue(); + Events.assertField(event, structName + ".reservedSize").atLeast(committedSize); + } + + private static void checkSpace(RecordedEvent event, String structName) { + long start = Events.assertField(event, structName + ".start").atLeast(0L).getValue(); + long end = Events.assertField(event, structName + ".end").above(start).getValue(); + long used = Events.assertField(event, structName + ".used").atLeast(0L).getValue(); + long size = Events.assertField(event, structName + ".size").atLeast(used).getValue(); + Asserts.assertEquals(size, end - start, "Size mismatch"); + } + + private static boolean checkCollectors(Recording recording, String expectedYoung, String expectedOld) throws Exception { + for (RecordedEvent event : Events.fromRecording(recording)) { + if (Events.isEventType(event, EventNames.GCConfiguration)) { + final String young = Events.assertField(event, "youngCollector").notEmpty().getValue(); + final String old = Events.assertField(event, "oldCollector").notEmpty().getValue(); + if (young.equals(expectedYoung) && old.equals(expectedOld)) { + return true; + } + // TODO: We treat wrong collector types as an error. Old test only warned. Not sure what is correct. + Asserts.fail(String.format("Wrong collector types: got('%s','%s'), expected('%s','%s')", + young, old, expectedYoung, expectedOld)); + } + } + Asserts.fail("Missing event type " + EventNames.GCConfiguration); + return false; + } +} --- /dev/null 2019-01-28 17:49:12.000000000 +0800 +++ new/test/testlibrary/whitebox/sun/hotspot/code/BlobType.java 2019-01-28 17:49:12.000000000 +0800 @@ -0,0 +1,67 @@ +/* + * Copyright (c) 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. + */ + +package sun.hotspot.code; + +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryPoolMXBean; +import java.util.EnumSet; + +import sun.hotspot.WhiteBox; + +public enum BlobType { + // All types (No code cache segmentation) + All(0, "Code Cache", "ReservedCodeCacheSize"); + + public final int id; + public final String sizeOptionName; + public final String beanName; + + private BlobType(int id, String beanName, String sizeOptionName) { + this.id = id; + this.beanName = beanName; + this.sizeOptionName = sizeOptionName; + } + + public MemoryPoolMXBean getMemoryPool() { + for (MemoryPoolMXBean bean : ManagementFactory.getMemoryPoolMXBeans()) { + String name = bean.getName(); + if (beanName.equals(name)) { + return bean; + } + } + return null; + } + + public boolean allowTypeWhenOverflow(BlobType type) { + return type == this; + } + + public static EnumSet getAvailable() { + return EnumSet.of(All); + } + + public long getSize() { + return WhiteBox.getWhiteBox().getUintxVMFlag(sizeOptionName); + } +} --- /dev/null 2019-01-28 17:49:13.000000000 +0800 +++ new/test/testlibrary/whitebox/sun/hotspot/code/CodeBlob.java 2019-01-28 17:49:13.000000000 +0800 @@ -0,0 +1,74 @@ +/* + * Copyright (c) 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. + */ + +package sun.hotspot.code; + +import sun.hotspot.WhiteBox; + +public class CodeBlob { + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + public static CodeBlob[] getCodeBlobs(BlobType type) { + Object[] obj = WB.getCodeHeapEntries(type.id); + if (obj == null) { + return null; + } + CodeBlob[] result = new CodeBlob[obj.length]; + for (int i = 0, n = result.length; i < n; ++i) { + result[i] = new CodeBlob((Object[]) obj[i]); + } + return result; + } + public static CodeBlob getCodeBlob(long addr) { + Object[] obj = WB.getCodeBlob(addr); + if (obj == null) { + return null; + } + return new CodeBlob(obj); + } + protected CodeBlob(Object[] obj) { + assert obj.length == 4; + name = (String) obj[0]; + size = (Integer) obj[1]; + int blob_type_index = (Integer) obj[2]; + if (blob_type_index == -1) { // AOT + code_blob_type = null; + } else { + code_blob_type = BlobType.values()[blob_type_index]; + assert code_blob_type.id == (Integer) obj[2]; + } + address = (Long) obj[3]; + } + public final String name; + public final int size; + public final BlobType code_blob_type; + public final long address; + @Override + public String toString() { + return "CodeBlob{" + + "name=" + name + + ", size=" + size + + ", code_blob_type=" + code_blob_type + + ", address=" + address + + '}'; + } +}