8211238: @Deprecated JFR event
Reviewed-by: egahlin, jbachorik
This commit is contained in:
parent
656b446289
commit
49fff0132b
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 2023, 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,7 @@ public class GenerateJfrFiles {
|
||||
String period = "";
|
||||
boolean cutoff;
|
||||
boolean throttle;
|
||||
String level = "";
|
||||
boolean experimental;
|
||||
boolean internal;
|
||||
long id;
|
||||
@ -222,6 +223,7 @@ public class GenerateJfrFiles {
|
||||
pos.writeUTF(period);
|
||||
pos.writeBoolean(cutoff);
|
||||
pos.writeBoolean(throttle);
|
||||
pos.writeUTF(level);
|
||||
pos.writeBoolean(experimental);
|
||||
pos.writeBoolean(internal);
|
||||
pos.writeLong(id);
|
||||
@ -520,6 +522,7 @@ public class GenerateJfrFiles {
|
||||
currentType.startTime = getBoolean(attributes, "startTime", true);
|
||||
currentType.period = getString(attributes, "period");
|
||||
currentType.cutoff = getBoolean(attributes, "cutoff", false);
|
||||
currentType.level = getString(attributes, "level");
|
||||
currentType.throttle = getBoolean(attributes, "throttle", false);
|
||||
currentType.commitState = getString(attributes, "commitState");
|
||||
currentType.isEvent = "Event".equals(qName);
|
||||
@ -651,7 +654,7 @@ public class GenerateJfrFiles {
|
||||
out.write("");
|
||||
out.write("struct jfrNativeEventSetting {");
|
||||
out.write(" jlong threshold_ticks;");
|
||||
out.write(" jlong cutoff_ticks;");
|
||||
out.write(" jlong miscellaneous;");
|
||||
out.write(" u1 stacktrace;");
|
||||
out.write(" u1 enabled;");
|
||||
out.write(" u1 large;");
|
||||
|
@ -200,6 +200,7 @@ class ciMethod : public ciMetadata {
|
||||
bool intrinsic_candidate() const { return get_Method()->intrinsic_candidate(); }
|
||||
bool is_static_initializer() const { return get_Method()->is_static_initializer(); }
|
||||
bool changes_current_thread() const { return get_Method()->changes_current_thread(); }
|
||||
bool deprecated() const { return is_loaded() && get_Method()->deprecated(); }
|
||||
|
||||
bool check_intrinsic_candidate() const {
|
||||
if (intrinsic_id() == vmIntrinsics::_blackhole) {
|
||||
|
@ -951,6 +951,8 @@ public:
|
||||
_field_Stable,
|
||||
_jdk_internal_vm_annotation_ReservedStackAccess,
|
||||
_jdk_internal_ValueBased,
|
||||
_java_lang_Deprecated,
|
||||
_java_lang_Deprecated_for_removal,
|
||||
_annotation_LIMIT
|
||||
};
|
||||
const Location _location;
|
||||
@ -1122,6 +1124,7 @@ static void parse_annotations(const ConstantPool* const cp,
|
||||
s_tag_val = 's', // payload is String
|
||||
s_con_off = 7, // utf8 payload, such as 'Ljava/lang/String;'
|
||||
s_size = 9,
|
||||
b_tag_val = 'Z', // payload is boolean
|
||||
min_size = 6 // smallest possible size (zero members)
|
||||
};
|
||||
// Cannot add min_size to index in case of overflow MAX_INT
|
||||
@ -1144,6 +1147,32 @@ static void parse_annotations(const ConstantPool* const cp,
|
||||
AnnotationCollector::ID id = coll->annotation_index(loader_data, aname, can_access_vm_annotations);
|
||||
if (AnnotationCollector::_unknown == id) continue;
|
||||
coll->set_annotation(id);
|
||||
if (AnnotationCollector::_java_lang_Deprecated == id) {
|
||||
assert(count <= 2, "change this if more element-value pairs are added to the @Deprecated annotation");
|
||||
// @Deprecated can specify forRemoval=true
|
||||
const u1* offset = abase + member_off;
|
||||
for (int i = 0; i < count; ++i) {
|
||||
int member_index = Bytes::get_Java_u2((address)offset);
|
||||
offset += 2;
|
||||
member = check_symbol_at(cp, member_index);
|
||||
if (member == vmSymbols::since()) {
|
||||
assert(*((address)offset) == s_tag_val, "invariant");
|
||||
offset += 3;
|
||||
continue;
|
||||
}
|
||||
if (member == vmSymbols::for_removal()) {
|
||||
assert(*((address)offset) == b_tag_val, "invariant");
|
||||
const u2 boolean_value_index = Bytes::get_Java_u2((address)offset + 1);
|
||||
if (cp->int_at(boolean_value_index) == 1) {
|
||||
// forRemoval == true
|
||||
coll->set_annotation(AnnotationCollector::_java_lang_Deprecated_for_removal);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (AnnotationCollector::_jdk_internal_vm_annotation_Contended == id) {
|
||||
// @Contended can optionally specify the contention group.
|
||||
@ -1959,6 +1988,9 @@ AnnotationCollector::annotation_index(const ClassLoaderData* loader_data,
|
||||
if (!privileged) break; // only allow in privileged code
|
||||
return _jdk_internal_ValueBased;
|
||||
}
|
||||
case VM_SYMBOL_ENUM_NAME(java_lang_Deprecated): {
|
||||
return _java_lang_Deprecated;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
@ -2003,6 +2035,10 @@ void MethodAnnotationCollector::apply_to(const methodHandle& m) {
|
||||
m->set_intrinsic_candidate();
|
||||
if (has_annotation(_jdk_internal_vm_annotation_ReservedStackAccess))
|
||||
m->set_has_reserved_stack_access();
|
||||
if (has_annotation(_java_lang_Deprecated))
|
||||
m->set_deprecated();
|
||||
if (has_annotation(_java_lang_Deprecated_for_removal))
|
||||
m->set_deprecated_for_removal();
|
||||
}
|
||||
|
||||
void ClassFileParser::ClassAnnotationCollector::apply_to(InstanceKlass* ik) {
|
||||
@ -2016,6 +2052,22 @@ void ClassFileParser::ClassAnnotationCollector::apply_to(InstanceKlass* ik) {
|
||||
ik->set_is_value_based();
|
||||
}
|
||||
}
|
||||
if (has_annotation(_java_lang_Deprecated)) {
|
||||
Array<Method*>* methods = ik->methods();
|
||||
int length = ik->methods()->length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
Method* m = methods->at(i);
|
||||
m->set_deprecated();
|
||||
}
|
||||
}
|
||||
if (has_annotation(_java_lang_Deprecated_for_removal)) {
|
||||
Array<Method*>* methods = ik->methods();
|
||||
int length = ik->methods()->length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
Method* m = methods->at(i);
|
||||
m->set_deprecated_for_removal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define MAX_ARGS_SIZE 255
|
||||
|
@ -162,7 +162,9 @@ class SerializeClosure;
|
||||
template(jdk_internal_loader_BuiltinClassLoader, "jdk/internal/loader/BuiltinClassLoader") \
|
||||
template(jdk_internal_loader_ClassLoaders_AppClassLoader, "jdk/internal/loader/ClassLoaders$AppClassLoader") \
|
||||
template(jdk_internal_loader_ClassLoaders_PlatformClassLoader, "jdk/internal/loader/ClassLoaders$PlatformClassLoader") \
|
||||
\
|
||||
template(java_lang_Deprecated, "Ljava/lang/Deprecated;") \
|
||||
template(since, "since") \
|
||||
template(for_removal, "forRemoval") \
|
||||
/* Java runtime version access */ \
|
||||
template(java_lang_VersionProps, "java/lang/VersionProps") \
|
||||
template(java_version_name, "java_version") \
|
||||
|
@ -1,134 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 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 "ci/ciKlass.hpp"
|
||||
#include "ci/ciMethod.hpp"
|
||||
#include "classfile/vmSymbols.hpp"
|
||||
#include "interpreter/linkResolver.hpp"
|
||||
#include "jfr/instrumentation/jfrResolution.hpp"
|
||||
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdMacros.hpp"
|
||||
#include "oops/method.inline.hpp"
|
||||
#include "runtime/javaThread.hpp"
|
||||
#include "runtime/vframe.inline.hpp"
|
||||
#ifdef COMPILER1
|
||||
#include "c1/c1_GraphBuilder.hpp"
|
||||
#endif
|
||||
#ifdef COMPILER2
|
||||
#include "opto/parse.hpp"
|
||||
#endif
|
||||
|
||||
static const char* const link_error_msg = "illegal access linking method 'jdk.jfr.internal.event.EventWriterFactory.getEventWriter(long)'";
|
||||
|
||||
static const Method* ljf_sender_method(JavaThread* jt) {
|
||||
assert(jt != nullptr, "invariant");
|
||||
if (!jt->has_last_Java_frame()) {
|
||||
return nullptr;
|
||||
}
|
||||
const vframeStream ljf(jt, false, false);
|
||||
return ljf.method();
|
||||
}
|
||||
|
||||
void JfrResolution::on_runtime_resolution(const CallInfo & info, TRAPS) {
|
||||
assert(info.selected_method() != nullptr, "invariant");
|
||||
assert(info.resolved_klass() != nullptr, "invariant");
|
||||
static const Symbol* const event_writer_method_name = vmSymbols::getEventWriter_name();
|
||||
assert(event_writer_method_name != nullptr, "invariant");
|
||||
// Fast path
|
||||
if (info.selected_method()->name() != event_writer_method_name) {
|
||||
return;
|
||||
}
|
||||
static const Symbol* const event_writer_factory_klass_name = vmSymbols::jdk_jfr_internal_event_EventWriterFactory();
|
||||
assert(event_writer_factory_klass_name != nullptr, "invariant");
|
||||
if (info.resolved_klass()->name() != event_writer_factory_klass_name) {
|
||||
return;
|
||||
}
|
||||
// Attempting to link against jdk.jfr.internal.event.EventWriterFactory.getEventWriter().
|
||||
// The sender, i.e. the method attempting to link, is in the ljf (if one exists).
|
||||
const Method* const sender = ljf_sender_method(THREAD);
|
||||
if (sender == nullptr) {
|
||||
// A compiler thread is doing linktime resolution but there is no information about the sender available.
|
||||
// For the compiler threads, the sender is instead found as part of bytecode parsing.
|
||||
return;
|
||||
}
|
||||
// Is the sender method blessed for linkage?
|
||||
if (IS_METHOD_BLESSED(sender)) {
|
||||
return;
|
||||
}
|
||||
#if INCLUDE_JVMCI
|
||||
// JVMCI compiler is doing linktime resolution
|
||||
if (sender->method_holder()->name() == vmSymbols::jdk_vm_ci_hotspot_CompilerToVM()) {
|
||||
if (sender->name()->equals("lookupMethodInPool")) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
THROW_MSG(vmSymbols::java_lang_IllegalAccessError(), link_error_msg);
|
||||
}
|
||||
|
||||
static inline bool is_compiler_linking_event_writer(const Symbol* holder, const Symbol* name) {
|
||||
static const Symbol* const event_writer_factory_klass_name = vmSymbols::jdk_jfr_internal_event_EventWriterFactory();
|
||||
assert(event_writer_factory_klass_name != nullptr, "invariant");
|
||||
if (holder != event_writer_factory_klass_name) {
|
||||
return false;
|
||||
}
|
||||
static const Symbol* const event_writer_method_name = vmSymbols::getEventWriter_name();
|
||||
assert(event_writer_method_name != nullptr, "invariant");
|
||||
return name == event_writer_method_name;
|
||||
}
|
||||
|
||||
static inline bool is_compiler_linking_event_writer(const ciKlass * holder, const ciMethod * target) {
|
||||
assert(holder != nullptr, "invariant");
|
||||
assert(target != nullptr, "invariant");
|
||||
return is_compiler_linking_event_writer(holder->name()->get_symbol(), target->name()->get_symbol());
|
||||
}
|
||||
|
||||
#ifdef COMPILER1
|
||||
// C1
|
||||
void JfrResolution::on_c1_resolution(const GraphBuilder * builder, const ciKlass * holder, const ciMethod * target) {
|
||||
if (is_compiler_linking_event_writer(holder, target) && !IS_METHOD_BLESSED(builder->method()->get_Method())) {
|
||||
builder->bailout(link_error_msg);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef COMPILER2
|
||||
// C2
|
||||
void JfrResolution::on_c2_resolution(const Parse * parse, const ciKlass * holder, const ciMethod * target) {
|
||||
if (is_compiler_linking_event_writer(holder, target) && !IS_METHOD_BLESSED(parse->method()->get_Method())) {
|
||||
parse->C->record_failure(link_error_msg);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if INCLUDE_JVMCI
|
||||
// JVMCI
|
||||
void JfrResolution::on_jvmci_resolution(const Method* caller, const Method* target, TRAPS) {
|
||||
if (is_compiler_linking_event_writer(target->method_holder()->name(), target->name())) {
|
||||
if (caller == nullptr || !IS_METHOD_BLESSED(caller)) {
|
||||
THROW_MSG(vmSymbols::java_lang_IllegalAccessError(), link_error_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2023, 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,7 +23,6 @@
|
||||
*/
|
||||
|
||||
#include "precompiled.hpp"
|
||||
#include "jfr/instrumentation/jfrResolution.hpp"
|
||||
#include "jfr/jfr.hpp"
|
||||
#include "jfr/jni/jfrJavaSupport.hpp"
|
||||
#include "jfr/leakprofiler/leakProfiler.hpp"
|
||||
@ -33,6 +32,7 @@
|
||||
#include "jfr/recorder/service/jfrOptionSet.hpp"
|
||||
#include "jfr/recorder/service/jfrOptionSet.hpp"
|
||||
#include "jfr/recorder/repository/jfrRepository.hpp"
|
||||
#include "jfr/support/jfrResolution.hpp"
|
||||
#include "jfr/support/jfrThreadLocal.hpp"
|
||||
#include "runtime/java.hpp"
|
||||
|
||||
@ -67,9 +67,7 @@ void Jfr::on_create_vm_3() {
|
||||
}
|
||||
|
||||
void Jfr::on_unloading_classes() {
|
||||
if (JfrRecorder::is_created()) {
|
||||
JfrCheckpointManager::on_unloading_classes();
|
||||
}
|
||||
JfrCheckpointManager::on_unloading_classes();
|
||||
}
|
||||
|
||||
bool Jfr::is_excluded(Thread* t) {
|
||||
@ -104,6 +102,10 @@ void Jfr::on_resolution(const CallInfo& info, TRAPS) {
|
||||
JfrResolution::on_runtime_resolution(info, THREAD);
|
||||
}
|
||||
|
||||
void Jfr::on_backpatching(const Method* callee_method, JavaThread* jt) {
|
||||
JfrResolution::on_backpatching(callee_method, jt);
|
||||
}
|
||||
|
||||
#ifdef COMPILER1
|
||||
void Jfr::on_resolution(const GraphBuilder* builder, const ciKlass* holder, const ciMethod* target) {
|
||||
JfrResolution::on_c1_resolution(builder, holder, target);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 2023, 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
|
||||
@ -70,6 +70,7 @@ class Jfr : AllStatic {
|
||||
static void on_vm_error_report(outputStream* st);
|
||||
static bool on_flight_recorder_option(const JavaVMOption** option, char* delimiter);
|
||||
static bool on_start_flight_recording_option(const JavaVMOption** option, char* delimiter);
|
||||
static void on_backpatching(const Method* callee_method, JavaThread* jt);
|
||||
};
|
||||
|
||||
#endif // SHARE_JFR_JFR_HPP
|
||||
|
@ -45,6 +45,7 @@
|
||||
#include "jfr/instrumentation/jfrEventClassTransformer.hpp"
|
||||
#include "jfr/instrumentation/jfrJvmtiAgent.hpp"
|
||||
#include "jfr/leakprofiler/leakProfiler.hpp"
|
||||
#include "jfr/support/jfrDeprecationManager.hpp"
|
||||
#include "jfr/support/jfrJdkJfrEvent.hpp"
|
||||
#include "jfr/support/jfrKlassUnloading.hpp"
|
||||
#include "jfr/utilities/jfrJavaLog.hpp"
|
||||
@ -159,15 +160,19 @@ NO_TRANSITION(jdouble, jfr_time_conv_factor(JNIEnv* env, jclass jvm))
|
||||
return (jdouble)JfrTimeConverter::nano_to_counter_multiplier();
|
||||
NO_TRANSITION_END
|
||||
|
||||
NO_TRANSITION(jboolean, jfr_set_cutoff(JNIEnv* env, jclass jvm, jlong event_type_id, jlong cutoff_ticks))
|
||||
return JfrEventSetting::set_cutoff(event_type_id, cutoff_ticks) ? JNI_TRUE : JNI_FALSE;
|
||||
NO_TRANSITION_END
|
||||
|
||||
NO_TRANSITION(jboolean, jfr_set_throttle(JNIEnv* env, jclass jvm, jlong event_type_id, jlong event_sample_size, jlong period_ms))
|
||||
JfrEventThrottler::configure(static_cast<JfrEventId>(event_type_id), event_sample_size, period_ms);
|
||||
return JNI_TRUE;
|
||||
NO_TRANSITION_END
|
||||
|
||||
NO_TRANSITION(void, jfr_set_miscellaneous(JNIEnv* env, jobject jvm, jlong event_type_id, jlong value))
|
||||
JfrEventSetting::set_miscellaneous(event_type_id, value);
|
||||
const JfrEventId typed_event_id = (JfrEventId)event_type_id;
|
||||
if (EventDeprecatedInvocation::eventId == typed_event_id) {
|
||||
JfrDeprecationManager::on_level_setting_update(value);
|
||||
}
|
||||
NO_TRANSITION_END
|
||||
|
||||
NO_TRANSITION(jboolean, jfr_should_rotate_disk(JNIEnv* env, jclass jvm))
|
||||
return JfrChunkRotation::should_rotate() ? JNI_TRUE : JNI_FALSE;
|
||||
NO_TRANSITION_END
|
||||
|
@ -127,10 +127,10 @@ void JNICALL jfr_set_force_instrumentation(JNIEnv* env, jclass jvm, jboolean for
|
||||
|
||||
jlong JNICALL jfr_get_unloaded_event_classes_count(JNIEnv* env, jclass jvm);
|
||||
|
||||
jboolean JNICALL jfr_set_cutoff(JNIEnv* env, jclass jvm, jlong event_type_id, jlong cutoff_ticks);
|
||||
|
||||
jboolean JNICALL jfr_set_throttle(JNIEnv* env, jclass jvm, jlong event_type_id, jlong event_sample_size, jlong period_ms);
|
||||
|
||||
void JNICALL jfr_set_miscellaneous(JNIEnv* env, jobject jvm, jlong id, jlong value);
|
||||
|
||||
void JNICALL jfr_emit_old_object_samples(JNIEnv* env, jclass jvm, jlong cutoff_ticks, jboolean, jboolean);
|
||||
|
||||
jboolean JNICALL jfr_should_rotate_disk(JNIEnv* env, jclass jvm);
|
||||
|
@ -82,7 +82,7 @@ JfrJniMethodRegistration::JfrJniMethodRegistration(JNIEnv* env) {
|
||||
(char*)"uncaughtException", (char*)"(Ljava/lang/Thread;Ljava/lang/Throwable;)V", (void*)jfr_uncaught_exception,
|
||||
(char*)"setForceInstrumentation", (char*)"(Z)V", (void*)jfr_set_force_instrumentation,
|
||||
(char*)"getUnloadedEventClassCount", (char*)"()J", (void*)jfr_get_unloaded_event_classes_count,
|
||||
(char*)"setCutoff", (char*)"(JJ)Z", (void*)jfr_set_cutoff,
|
||||
(char*)"setMiscellaneous", (char*)"(JJ)V", (void*)jfr_set_miscellaneous,
|
||||
(char*)"setThrottle", (char*)"(JJJ)Z", (void*)jfr_set_throttle,
|
||||
(char*)"emitOldObjectSamples", (char*)"(JZZ)V", (void*)jfr_emit_old_object_samples,
|
||||
(char*)"shouldRotateDisk", (char*)"()Z", (void*)jfr_should_rotate_disk,
|
||||
|
@ -416,9 +416,9 @@ static void install_type_set_blobs() {
|
||||
iterate_samples(installer);
|
||||
}
|
||||
|
||||
static void save_type_set_blob(JfrCheckpointWriter& writer, bool copy = false) {
|
||||
static void save_type_set_blob(JfrCheckpointWriter& writer) {
|
||||
assert(writer.has_data(), "invariant");
|
||||
const JfrBlobHandle blob = copy ? writer.copy() : writer.move();
|
||||
const JfrBlobHandle blob = writer.copy();
|
||||
if (saved_type_set_blobs.valid()) {
|
||||
saved_type_set_blobs->set_next(blob);
|
||||
} else {
|
||||
@ -438,9 +438,8 @@ void ObjectSampleCheckpoint::on_type_set(JfrCheckpointWriter& writer) {
|
||||
}
|
||||
|
||||
void ObjectSampleCheckpoint::on_type_set_unload(JfrCheckpointWriter& writer) {
|
||||
assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
|
||||
assert(LeakProfiler::is_running(), "invariant");
|
||||
if (writer.has_data() && ObjectSampler::sampler()->last() != nullptr) {
|
||||
save_type_set_blob(writer, true);
|
||||
save_type_set_blob(writer);
|
||||
}
|
||||
}
|
||||
|
@ -1252,6 +1252,13 @@
|
||||
<Field type="string" name="path" label="Path" description="The path of the library" />
|
||||
</Event>
|
||||
|
||||
<Event name="DeprecatedInvocation" description= "A unique invocation of a method that is annotated with @Deprecated. Packages and modules that are deprecated are ignored. At most 10 000 invocation sites and only the first invocation from a class is guaranteed to be included."
|
||||
category="Java Application, Statistics" label="Deprecated Method Invocation" thread="false" stackTrace="true" startTime="false" level="forRemoval,all">
|
||||
<Field type="Method" name="method" label="Deprecated Method" />
|
||||
<Field type="Ticks" name="invocationTime" label="Invocation Time" description="The time the deprecated method was invoked for the first time" />
|
||||
<Field type="boolean" name="forRemoval" label="For Removal" />
|
||||
</Event>
|
||||
|
||||
<Type name="DeoptimizationReason" label="Deoptimization Reason">
|
||||
<Field type="string" name="reason" label="Reason" />
|
||||
</Type>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!--
|
||||
Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
|
||||
This code is free software; you can redistribute it and/or modify it
|
||||
@ -72,6 +72,7 @@
|
||||
<xs:attribute name="period" type="periodType" use="optional" />
|
||||
<xs:attribute name="cutoff" type="xs:boolean" use="optional" />
|
||||
<xs:attribute name="throttle" type="xs:boolean" use="optional" />
|
||||
<xs:attribute name="level" type="xs:string" use="optional" />
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element maxOccurs="unbounded" name="Type">
|
||||
|
@ -139,7 +139,7 @@ TRACE_REQUEST_FUNC(ModuleExport) {
|
||||
/*
|
||||
* This is left empty on purpose, having ExecutionSample as a requestable
|
||||
* is a way of getting the period. The period is passed to ThreadSampling::update_period.
|
||||
* Implementation in jfrSamples.cpp
|
||||
* Implementation in periodic/sampling/jfrThreadSampler.cpp.
|
||||
*/
|
||||
TRACE_REQUEST_FUNC(ExecutionSample) {
|
||||
}
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include "jfr/recorder/storage/jfrMemorySpace.inline.hpp"
|
||||
#include "jfr/recorder/storage/jfrStorageUtils.inline.hpp"
|
||||
#include "jfr/recorder/stringpool/jfrStringPool.hpp"
|
||||
#include "jfr/support/jfrDeprecationManager.hpp"
|
||||
#include "jfr/support/jfrKlassUnloading.hpp"
|
||||
#include "jfr/support/jfrThreadLocal.hpp"
|
||||
#include "jfr/utilities/jfrBigEndian.hpp"
|
||||
@ -66,30 +67,24 @@ JfrCheckpointManager& JfrCheckpointManager::instance() {
|
||||
return *_instance;
|
||||
}
|
||||
|
||||
JfrCheckpointManager* JfrCheckpointManager::create(JfrChunkWriter& cw) {
|
||||
JfrCheckpointManager* JfrCheckpointManager::create() {
|
||||
assert(_instance == nullptr, "invariant");
|
||||
_instance = new JfrCheckpointManager(cw);
|
||||
_instance = new JfrCheckpointManager();
|
||||
return _instance;
|
||||
}
|
||||
|
||||
void JfrCheckpointManager::destroy() {
|
||||
assert(_instance != nullptr, "invariant");
|
||||
delete _instance;
|
||||
_instance = nullptr;
|
||||
JfrTypeManager::destroy();
|
||||
JfrTraceIdLoadBarrier::destroy();
|
||||
}
|
||||
|
||||
JfrCheckpointManager::JfrCheckpointManager(JfrChunkWriter& cw) :
|
||||
JfrCheckpointManager::JfrCheckpointManager() :
|
||||
_global_mspace(nullptr),
|
||||
_thread_local_mspace(nullptr),
|
||||
_virtual_thread_local_mspace(nullptr),
|
||||
_chunkwriter(cw) {}
|
||||
_chunkwriter(nullptr) {}
|
||||
|
||||
JfrCheckpointManager::~JfrCheckpointManager() {
|
||||
JfrTraceIdLoadBarrier::destroy();
|
||||
JfrTypeManager::destroy();
|
||||
delete _global_mspace;
|
||||
delete _thread_local_mspace;
|
||||
}
|
||||
JfrCheckpointManager::~JfrCheckpointManager() {}
|
||||
|
||||
static const size_t global_buffer_prealloc_count = 2;
|
||||
static const size_t global_buffer_size = 512 * K;
|
||||
@ -100,7 +95,10 @@ static const size_t thread_local_buffer_size = 256;
|
||||
static const size_t virtual_thread_local_buffer_prealloc_count = 0;
|
||||
static const size_t virtual_thread_local_buffer_size = 4 * K;
|
||||
|
||||
bool JfrCheckpointManager::initialize() {
|
||||
// We expose an early initialization routine to support class unloading
|
||||
// even though the full JFR system is not yet started. It requires
|
||||
// backing of global buffers should it write a class unload type set blob.
|
||||
bool JfrCheckpointManager::initialize_early() {
|
||||
assert(_global_mspace == nullptr, "invariant");
|
||||
_global_mspace = create_mspace<JfrCheckpointMspace, JfrCheckpointManager>(global_buffer_size, 0, 0, false, this); // post-pone preallocation
|
||||
if (_global_mspace == nullptr) {
|
||||
@ -131,9 +129,21 @@ bool JfrCheckpointManager::initialize() {
|
||||
virtual_thread_local_buffer_prealloc_count)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// The instance is already created and so we only complete the setup of additional subsystems.
|
||||
bool JfrCheckpointManager::initialize(JfrChunkWriter* cw) {
|
||||
assert(cw != nullptr, "invariant");
|
||||
_chunkwriter = cw;
|
||||
return JfrTypeManager::initialize() && JfrTraceIdLoadBarrier::initialize();
|
||||
}
|
||||
|
||||
JfrChunkWriter& JfrCheckpointManager::chunkwriter() {
|
||||
assert(_chunkwriter != nullptr, "invariant");
|
||||
return *_chunkwriter;
|
||||
}
|
||||
|
||||
#ifdef ASSERT
|
||||
static void assert_lease(ConstBufferPtr buffer) {
|
||||
if (buffer == nullptr) {
|
||||
@ -502,7 +512,7 @@ void JfrCheckpointManager::end_epoch_shift() {
|
||||
|
||||
size_t JfrCheckpointManager::write() {
|
||||
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(JavaThread::current()));
|
||||
WriteOperation wo(_chunkwriter);
|
||||
WriteOperation wo(chunkwriter());
|
||||
MutexedWriteOperation mwo(wo);
|
||||
_thread_local_mspace->iterate(mwo, true); // previous epoch list
|
||||
assert(_global_mspace->free_list_is_empty(), "invariant");
|
||||
@ -510,7 +520,7 @@ size_t JfrCheckpointManager::write() {
|
||||
WriteReleaseOperation wro(&mwo, &ro);
|
||||
process_live_list(wro, _global_mspace, true); // previous epoch list
|
||||
// Do virtual thread local list last. Careful, the vtlco destructor writes to chunk.
|
||||
VirtualThreadLocalCheckpointOperation vtlco(_chunkwriter);
|
||||
VirtualThreadLocalCheckpointOperation vtlco(chunkwriter());
|
||||
VirtualThreadLocalWriteOperation vtlwo(vtlco);
|
||||
_virtual_thread_local_mspace->iterate(vtlwo, true); // previous epoch list
|
||||
return wo.processed() + vtlco.processed();
|
||||
@ -534,7 +544,7 @@ size_t JfrCheckpointManager::clear() {
|
||||
|
||||
size_t JfrCheckpointManager::write_static_type_set(Thread* thread) {
|
||||
assert(thread != nullptr, "invariant");
|
||||
JfrCheckpointWriter writer(true, thread, STATICS);
|
||||
JfrCheckpointWriter writer(true /* prev epoch */, thread, true /* header */, STATICS);
|
||||
JfrTypeManager::write_static_types(writer);
|
||||
return writer.used_size();
|
||||
}
|
||||
@ -545,7 +555,7 @@ size_t JfrCheckpointManager::write_threads(JavaThread* thread) {
|
||||
ThreadInVMfromNative transition(thread);
|
||||
ResourceMark rm(thread);
|
||||
HandleMark hm(thread);
|
||||
JfrCheckpointWriter writer(true, thread, THREADS);
|
||||
JfrCheckpointWriter writer(true /* prev epoch */, thread, true /* header */, THREADS);
|
||||
JfrTypeManager::write_threads(writer);
|
||||
return writer.used_size();
|
||||
}
|
||||
@ -566,13 +576,26 @@ void JfrCheckpointManager::on_rotation() {
|
||||
|
||||
void JfrCheckpointManager::clear_type_set() {
|
||||
assert(!JfrRecorder::is_recording(), "invariant");
|
||||
JavaThread* t = JavaThread::current();
|
||||
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(t));
|
||||
JavaThread* thread = JavaThread::current();
|
||||
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(thread));
|
||||
// can safepoint here
|
||||
ThreadInVMfromNative transition(t);
|
||||
MutexLocker cld_lock(ClassLoaderDataGraph_lock);
|
||||
MutexLocker module_lock(Module_lock);
|
||||
JfrTypeSet::clear();
|
||||
ThreadInVMfromNative transition(thread);
|
||||
MutexLocker cld_lock(thread, ClassLoaderDataGraph_lock);
|
||||
// Marks leakp. Place prepare_type_set before writer construction.
|
||||
JfrDeprecationManager::prepare_type_set(thread);
|
||||
JfrCheckpointWriter leakp_writer(true /* prev epoch */, thread);
|
||||
JfrCheckpointWriter writer(true /* prev epoch */, thread);
|
||||
{
|
||||
MutexLocker module_lock(Module_lock);
|
||||
JfrTypeSet::clear(&writer, &leakp_writer);
|
||||
}
|
||||
JfrDeprecationManager::on_type_set(leakp_writer, nullptr, thread);
|
||||
// We placed a blob in the Deprecated subsystem by moving the information
|
||||
// from the leakp writer. For the real writer, the data will not be
|
||||
// committed, because the JFR system is yet to be started.
|
||||
// Therefore, the writer is cancelled before its destructor is run,
|
||||
// to avoid writing unnecessary information into the checkpoint system.
|
||||
writer.cancel();
|
||||
}
|
||||
|
||||
void JfrCheckpointManager::write_type_set() {
|
||||
@ -582,16 +605,19 @@ void JfrCheckpointManager::write_type_set() {
|
||||
// can safepoint here
|
||||
ThreadInVMfromNative transition(thread);
|
||||
MutexLocker cld_lock(thread, ClassLoaderDataGraph_lock);
|
||||
MutexLocker module_lock(thread, Module_lock);
|
||||
if (LeakProfiler::is_running()) {
|
||||
JfrCheckpointWriter leakp_writer(true, thread);
|
||||
JfrCheckpointWriter writer(true, thread);
|
||||
// Marks leakp. Place prepare_type_set before writer construction.
|
||||
JfrDeprecationManager::prepare_type_set(thread);
|
||||
JfrCheckpointWriter leakp_writer(true /* prev epoch */, thread);
|
||||
JfrCheckpointWriter writer(true /* prev epoch */, thread);
|
||||
{
|
||||
MutexLocker module_lock(thread, Module_lock);
|
||||
JfrTypeSet::serialize(&writer, &leakp_writer, false, false);
|
||||
ObjectSampleCheckpoint::on_type_set(leakp_writer);
|
||||
} else {
|
||||
JfrCheckpointWriter writer(true, thread);
|
||||
JfrTypeSet::serialize(&writer, nullptr, false, false);
|
||||
}
|
||||
if (LeakProfiler::is_running()) {
|
||||
ObjectSampleCheckpoint::on_type_set(leakp_writer);
|
||||
}
|
||||
// Place this call after ObjectSampleCheckpoint::on_type_set.
|
||||
JfrDeprecationManager::on_type_set(leakp_writer, _chunkwriter, thread);
|
||||
}
|
||||
write();
|
||||
}
|
||||
@ -603,6 +629,7 @@ void JfrCheckpointManager::on_unloading_classes() {
|
||||
if (LeakProfiler::is_running()) {
|
||||
ObjectSampleCheckpoint::on_type_set_unload(writer);
|
||||
}
|
||||
JfrDeprecationManager::on_type_set_unload(writer);
|
||||
}
|
||||
|
||||
static size_t flush_type_set(Thread* thread) {
|
||||
@ -626,14 +653,14 @@ size_t JfrCheckpointManager::flush_type_set() {
|
||||
}
|
||||
}
|
||||
if (_new_checkpoint.is_signaled_with_reset()) {
|
||||
WriteOperation wo(_chunkwriter);
|
||||
WriteOperation wo(chunkwriter());
|
||||
MutexedWriteOperation mwo(wo);
|
||||
_thread_local_mspace->iterate(mwo); // current epoch list
|
||||
assert(_global_mspace->free_list_is_empty(), "invariant");
|
||||
assert(_global_mspace->live_list_is_nonempty(), "invariant");
|
||||
process_live_list(mwo, _global_mspace); // current epoch list
|
||||
// Do virtual thread local list last. Careful, the vtlco destructor writes to chunk.
|
||||
VirtualThreadLocalCheckpointOperation vtlco(_chunkwriter);
|
||||
VirtualThreadLocalCheckpointOperation vtlco(chunkwriter());
|
||||
VirtualThreadLocalWriteOperation vtlwo(vtlco);
|
||||
_virtual_thread_local_mspace->iterate(vtlwo); // current epoch list
|
||||
}
|
||||
|
@ -62,15 +62,18 @@ class JfrCheckpointManager : public JfrCHeapObj {
|
||||
JfrCheckpointMspace* _global_mspace;
|
||||
JfrThreadLocalCheckpointMspace* _thread_local_mspace;
|
||||
JfrThreadLocalCheckpointMspace* _virtual_thread_local_mspace;
|
||||
JfrChunkWriter& _chunkwriter;
|
||||
JfrChunkWriter* _chunkwriter;
|
||||
|
||||
JfrCheckpointManager(JfrChunkWriter& cw);
|
||||
JfrCheckpointManager();
|
||||
~JfrCheckpointManager();
|
||||
static JfrCheckpointManager& instance();
|
||||
static JfrCheckpointManager* create(JfrChunkWriter& cw);
|
||||
bool initialize();
|
||||
static JfrCheckpointManager* create();
|
||||
bool initialize_early();
|
||||
bool initialize(JfrChunkWriter* cw);
|
||||
static void destroy();
|
||||
|
||||
JfrChunkWriter& chunkwriter();
|
||||
|
||||
static BufferPtr get_virtual_thread_local(Thread* thread);
|
||||
static void set_virtual_thread_local(Thread* thread, BufferPtr buffer);
|
||||
static BufferPtr acquire_virtual_thread_local(Thread* thread, size_t size);
|
||||
|
@ -60,13 +60,13 @@ JfrCheckpointWriter::JfrCheckpointWriter(Thread* thread, bool header /* true */,
|
||||
}
|
||||
}
|
||||
|
||||
JfrCheckpointWriter::JfrCheckpointWriter(bool previous_epoch, Thread* thread, JfrCheckpointType type /* GENERIC */) :
|
||||
JfrCheckpointWriter::JfrCheckpointWriter(bool previous_epoch, Thread* thread, bool header /* true */, JfrCheckpointType type /* GENERIC */) :
|
||||
JfrCheckpointWriterBase(JfrCheckpointManager::lease_global(thread, previous_epoch), thread),
|
||||
_time(JfrTicks::now()),
|
||||
_offset(0),
|
||||
_count(0),
|
||||
_type(type),
|
||||
_header(true) {
|
||||
_header(header) {
|
||||
assert(this->is_acquired(), "invariant");
|
||||
assert(0 == this->current_offset(), "invariant");
|
||||
if (_header) {
|
||||
@ -162,8 +162,10 @@ const u1* JfrCheckpointWriter::session_data(size_t* size, bool move /* false */,
|
||||
}
|
||||
*size = this->used_size();
|
||||
assert(this->start_pos() + *size == this->current_pos(), "invariant");
|
||||
write_checkpoint_header(const_cast<u1*>(this->start_pos()), this->used_offset(), _time, (u4)_type, count());
|
||||
_header = false; // the header was just written
|
||||
if (_header) {
|
||||
write_checkpoint_header(const_cast<u1*>(this->start_pos()), this->used_offset(), _time, (u4)_type, count());
|
||||
_header = false; // the header was just written
|
||||
}
|
||||
if (move) {
|
||||
this->seek(_offset);
|
||||
}
|
||||
|
@ -69,8 +69,8 @@ class JfrCheckpointWriter : public JfrCheckpointWriterBase {
|
||||
void increment();
|
||||
const u1* session_data(size_t* size, bool move = false, const JfrCheckpointContext* ctx = nullptr);
|
||||
void release();
|
||||
JfrCheckpointWriter(bool previous_epoch, Thread* thread, JfrCheckpointType type = GENERIC);
|
||||
public:
|
||||
public:
|
||||
JfrCheckpointWriter(bool previous_epoch, Thread* thread, bool header = true, JfrCheckpointType type = GENERIC);
|
||||
JfrCheckpointWriter(bool header = true, JfrCheckpointType mode = GENERIC, JfrCheckpointBufferKind kind = JFR_GLOBAL);
|
||||
JfrCheckpointWriter(Thread* thread, bool header = true, JfrCheckpointType mode = GENERIC, JfrCheckpointBufferKind kind = JFR_GLOBAL);
|
||||
~JfrCheckpointWriter();
|
||||
|
@ -286,15 +286,6 @@ static bool register_klass_unload(Klass* klass) {
|
||||
return JfrKlassUnloading::on_unload(klass);
|
||||
}
|
||||
|
||||
static void on_klass_unload(Klass* klass) {
|
||||
register_klass_unload(klass);
|
||||
}
|
||||
|
||||
static size_t register_unloading_klasses() {
|
||||
ClassLoaderDataGraph::classes_unloading_do(&on_klass_unload);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void do_unloading_klass(Klass* klass) {
|
||||
assert(klass != nullptr, "invariant");
|
||||
assert(_subsystem_callback != nullptr, "invariant");
|
||||
@ -362,6 +353,63 @@ static void do_klasses() {
|
||||
do_object();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void do_previous_epoch_artifact(JfrArtifactClosure* callback, T* value) {
|
||||
assert(callback != nullptr, "invariant");
|
||||
assert(value != nullptr, "invariant");
|
||||
if (USED_PREVIOUS_EPOCH(value)) {
|
||||
callback->do_artifact(value);
|
||||
}
|
||||
if (IS_SERIALIZED(value)) {
|
||||
CLEAR_SERIALIZED(value);
|
||||
}
|
||||
assert(IS_NOT_SERIALIZED(value), "invariant");
|
||||
}
|
||||
|
||||
static void do_previous_epoch_klass(JfrArtifactClosure* callback, const Klass* value) {
|
||||
assert(callback != nullptr, "invariant");
|
||||
assert(value != nullptr, "invariant");
|
||||
if (USED_PREVIOUS_EPOCH(value)) {
|
||||
callback->do_artifact(value);
|
||||
}
|
||||
}
|
||||
|
||||
static void do_klass_on_clear(Klass* klass) {
|
||||
assert(klass != nullptr, "invariant");
|
||||
assert(_subsystem_callback != nullptr, "invariant");
|
||||
do_previous_epoch_klass(_subsystem_callback, klass);
|
||||
}
|
||||
|
||||
static void do_loader_klass_on_clear(const Klass* klass) {
|
||||
if (klass != nullptr && _artifacts->should_do_loader_klass(klass)) {
|
||||
if (_leakp_writer != nullptr) {
|
||||
SET_LEAKP(klass);
|
||||
}
|
||||
SET_TRANSIENT(klass);
|
||||
do_previous_epoch_klass(_subsystem_callback, klass);
|
||||
}
|
||||
}
|
||||
|
||||
static void do_classloaders_on_clear() {
|
||||
for (ClassHierarchyIterator iter(vmClasses::ClassLoader_klass()); !iter.done(); iter.next()) {
|
||||
Klass* subk = iter.klass();
|
||||
if (is_classloader_klass_allowed(subk)) {
|
||||
do_loader_klass_on_clear(subk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void do_object_on_clear() {
|
||||
SET_TRANSIENT(vmClasses::Object_klass());
|
||||
do_klass_on_clear(vmClasses::Object_klass());
|
||||
}
|
||||
|
||||
static void do_all_klasses() {
|
||||
ClassLoaderDataGraph::classes_do(&do_klass_on_clear);
|
||||
do_classloaders_on_clear();
|
||||
do_object_on_clear();
|
||||
}
|
||||
|
||||
typedef SerializePredicate<KlassPtr> KlassPredicate;
|
||||
typedef JfrPredicatedTypeWriterImplHost<KlassPtr, KlassPredicate, write__klass> KlassWriterImpl;
|
||||
typedef JfrTypeWriterHost<KlassWriterImpl, TYPE_CLASS> KlassWriter;
|
||||
@ -414,32 +462,23 @@ static bool write_klasses() {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void do_previous_epoch_artifact(JfrArtifactClosure* callback, T* value) {
|
||||
assert(callback != nullptr, "invariant");
|
||||
assert(value != nullptr, "invariant");
|
||||
if (USED_PREVIOUS_EPOCH(value)) {
|
||||
callback->do_artifact(value);
|
||||
}
|
||||
if (IS_SERIALIZED(value)) {
|
||||
CLEAR_SERIALIZED(value);
|
||||
}
|
||||
assert(IS_NOT_SERIALIZED(value), "invariant");
|
||||
}
|
||||
|
||||
typedef JfrArtifactCallbackHost<KlassPtr, KlassArtifactRegistrator> RegisterKlassCallback;
|
||||
|
||||
static void register_klass(Klass* klass) {
|
||||
assert(klass != nullptr, "invariant");
|
||||
assert(_subsystem_callback != nullptr, "invariant");
|
||||
do_previous_epoch_artifact(_subsystem_callback, klass);
|
||||
}
|
||||
|
||||
static void register_klasses() {
|
||||
static bool write_klasses_on_clear() {
|
||||
assert(!_artifacts->has_klass_entries(), "invariant");
|
||||
assert(_writer != nullptr, "invariant");
|
||||
assert(_leakp_writer != nullptr, "invariant");
|
||||
KlassArtifactRegistrator reg(_artifacts);
|
||||
RegisterKlassCallback callback(&_subsystem_callback, ®);
|
||||
ClassLoaderDataGraph::classes_do(®ister_klass);
|
||||
KlassWriter kw(_writer, _class_unload);
|
||||
KlassWriterRegistration kwr(&kw, ®);
|
||||
LeakKlassWriter lkw(_leakp_writer, _class_unload);
|
||||
CompositeKlassWriter ckw(&lkw, &kw);
|
||||
CompositeKlassWriterRegistration ckwr(&ckw, ®);
|
||||
CompositeKlassCallback callback(&_subsystem_callback, &ckwr);
|
||||
do_all_klasses();
|
||||
if (is_complete()) {
|
||||
return false;
|
||||
}
|
||||
_artifacts->tally(kw);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int write_package(JfrCheckpointWriter* writer, PkgPtr pkg, bool leakp) {
|
||||
@ -530,12 +569,21 @@ static void write_packages() {
|
||||
_artifacts->tally(pw);
|
||||
}
|
||||
|
||||
typedef JfrArtifactCallbackHost<PkgPtr, ClearArtifact<PkgPtr> > ClearPackageCallback;
|
||||
|
||||
static void clear_packages() {
|
||||
static void write_packages_on_clear() {
|
||||
assert(_writer != nullptr, "invariant");
|
||||
assert(_leakp_writer != nullptr, "invariant");
|
||||
assert(previous_epoch(), "invariant");
|
||||
PackageWriter pw(_writer, _class_unload);
|
||||
KlassPackageWriter kpw(&pw);
|
||||
LeakPackageWriter lpw(_leakp_writer, _class_unload);
|
||||
CompositePackageWriter cpw(&lpw, &pw);
|
||||
KlassCompositePackageWriter kcpw(&cpw);
|
||||
_artifacts->iterate_klasses(kcpw);
|
||||
ClearArtifact<PkgPtr> clear;
|
||||
ClearPackageCallback callback(&_subsystem_callback, &clear);
|
||||
CompositePackageWriterWithClear cpwwc(&cpw, &clear);
|
||||
CompositePackageCallback callback(&_subsystem_callback, &cpwwc);
|
||||
do_packages();
|
||||
_artifacts->tally(pw);
|
||||
}
|
||||
|
||||
static int write_module(JfrCheckpointWriter* writer, ModPtr mod, bool leakp) {
|
||||
@ -626,12 +674,21 @@ static void write_modules() {
|
||||
_artifacts->tally(mw);
|
||||
}
|
||||
|
||||
typedef JfrArtifactCallbackHost<ModPtr, ClearArtifact<ModPtr> > ClearModuleCallback;
|
||||
|
||||
static void clear_modules() {
|
||||
static void write_modules_on_clear() {
|
||||
assert(_writer != nullptr, "invariant");
|
||||
assert(_leakp_writer != nullptr, "invariant");
|
||||
assert(previous_epoch(), "invariant");
|
||||
ModuleWriter mw(_writer, _class_unload);
|
||||
KlassModuleWriter kmw(&mw);
|
||||
LeakModuleWriter lmw(_leakp_writer, _class_unload);
|
||||
CompositeModuleWriter cmw(&lmw, &mw);
|
||||
KlassCompositeModuleWriter kcpw(&cmw);
|
||||
_artifacts->iterate_klasses(kcpw);
|
||||
ClearArtifact<ModPtr> clear;
|
||||
ClearModuleCallback callback(&_subsystem_callback, &clear);
|
||||
CompositeModuleWriterWithClear cmwwc(&cmw, &clear);
|
||||
CompositeModuleCallback callback(&_subsystem_callback, &cmwwc);
|
||||
do_modules();
|
||||
_artifacts->tally(mw);
|
||||
}
|
||||
|
||||
static int write_classloader(JfrCheckpointWriter* writer, CldPtr cld, bool leakp) {
|
||||
@ -759,12 +816,24 @@ static void write_classloaders() {
|
||||
_artifacts->tally(cldw);
|
||||
}
|
||||
|
||||
typedef JfrArtifactCallbackHost<CldPtr, ClearArtifact<CldPtr> > ClearCLDCallback;
|
||||
|
||||
static void clear_classloaders() {
|
||||
static void write_classloaders_on_clear() {
|
||||
assert(_writer != nullptr, "invariant");
|
||||
assert(_leakp_writer != nullptr, "invariant");
|
||||
CldWriter cldw(_writer, _class_unload);
|
||||
KlassCldWriter kcw(&cldw);
|
||||
ModuleCldWriter mcw(&cldw);
|
||||
KlassAndModuleCldWriter kmcw(&kcw, &mcw);
|
||||
LeakCldWriter lcldw(_leakp_writer, _class_unload);
|
||||
CompositeCldWriter ccldw(&lcldw, &cldw);
|
||||
KlassCompositeCldWriter kccldw(&ccldw);
|
||||
ModuleCompositeCldWriter mccldw(&ccldw);
|
||||
KlassAndModuleCompositeCldWriter kmccldw(&kccldw, &mccldw);
|
||||
_artifacts->iterate_klasses(kmccldw);
|
||||
ClearArtifact<CldPtr> clear;
|
||||
ClearCLDCallback callback(&_subsystem_callback, &clear);
|
||||
CompositeCldWriterWithClear ccldwwc(&ccldw, &clear);
|
||||
CompositeCldCallback callback(&_subsystem_callback, &ccldwwc);
|
||||
do_class_loaders();
|
||||
_artifacts->tally(cldw);
|
||||
}
|
||||
|
||||
static u1 get_visibility(MethodPtr method) {
|
||||
@ -776,7 +845,7 @@ template <>
|
||||
void set_serialized<Method>(MethodPtr method) {
|
||||
assert(method != nullptr, "invariant");
|
||||
SET_METHOD_SERIALIZED(method);
|
||||
assert(IS_METHOD_SERIALIZED(method), "invariant");
|
||||
assert(METHOD_IS_SERIALIZED(method), "invariant");
|
||||
if (current_epoch()) {
|
||||
CLEAR_THIS_EPOCH_METHOD_CLEARED_BIT(method);
|
||||
}
|
||||
@ -917,6 +986,17 @@ static void write_methods() {
|
||||
_artifacts->tally(mw);
|
||||
}
|
||||
|
||||
static void write_methods_on_clear() {
|
||||
assert(_writer != nullptr, "invariant");
|
||||
assert(_leakp_writer != nullptr, "invariant");
|
||||
assert(previous_epoch(), "invariant");
|
||||
MethodWriter mw(_writer, current_epoch(), _class_unload);
|
||||
LeakMethodWriter lpmw(_leakp_writer, current_epoch(), _class_unload);
|
||||
CompositeMethodWriter cmw(&lpmw, &mw);
|
||||
_artifacts->iterate_klasses(cmw);
|
||||
_artifacts->tally(mw);
|
||||
}
|
||||
|
||||
template <>
|
||||
void set_serialized<JfrSymbolTable::SymbolEntry>(SymbolEntryPtr ptr) {
|
||||
assert(ptr != nullptr, "invariant");
|
||||
@ -1005,6 +1085,23 @@ static void write_symbols_with_leakp() {
|
||||
_artifacts->tally(sw);
|
||||
}
|
||||
|
||||
static void write_symbols_on_clear() {
|
||||
assert(_writer != nullptr, "invariant");
|
||||
assert(_leakp_writer != nullptr, "invariant");
|
||||
assert(previous_epoch(), "invariant");
|
||||
SymbolEntryWriter sw(_writer, _class_unload);
|
||||
LeakSymbolEntryWriter lsw(_leakp_writer, _class_unload);
|
||||
CompositeSymbolWriter csw(&lsw, &sw);
|
||||
_artifacts->iterate_symbols(csw);
|
||||
StringEntryWriter sew(_writer, _class_unload, true); // skip header
|
||||
LeakStringEntryWriter lsew(_leakp_writer, _class_unload, true); // skip header
|
||||
CompositeStringWriter csew(&lsew, &sew);
|
||||
_artifacts->iterate_strings(csew);
|
||||
sw.add(sew.count());
|
||||
lsw.add(lsew.count());
|
||||
_artifacts->tally(sw);
|
||||
}
|
||||
|
||||
static void write_symbols() {
|
||||
assert(_writer != nullptr, "invariant");
|
||||
if (_leakp_writer != nullptr) {
|
||||
@ -1082,18 +1179,16 @@ size_t JfrTypeSet::serialize(JfrCheckpointWriter* writer, JfrCheckpointWriter* l
|
||||
/**
|
||||
* Clear all tags from the previous epoch.
|
||||
*/
|
||||
void JfrTypeSet::clear() {
|
||||
void JfrTypeSet::clear(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer) {
|
||||
ResourceMark rm;
|
||||
JfrKlassUnloading::clear();
|
||||
if (_artifacts != nullptr) {
|
||||
_artifacts->clear();
|
||||
}
|
||||
setup(nullptr, nullptr, false, false);
|
||||
register_klasses();
|
||||
clear_packages();
|
||||
clear_modules();
|
||||
clear_classloaders();
|
||||
clear_klasses_and_methods();
|
||||
setup(writer, leakp_writer, false, false);
|
||||
write_klasses_on_clear();
|
||||
write_packages_on_clear();
|
||||
write_modules_on_clear();
|
||||
write_classloaders_on_clear();
|
||||
write_methods_on_clear();
|
||||
write_symbols_on_clear();
|
||||
teardown();
|
||||
}
|
||||
|
||||
size_t JfrTypeSet::on_unloading_classes(JfrCheckpointWriter* writer) {
|
||||
@ -1101,8 +1196,5 @@ size_t JfrTypeSet::on_unloading_classes(JfrCheckpointWriter* writer) {
|
||||
// The JfrRecorderThread does this as part of normal processing, but with concurrent class unloading, which can
|
||||
// happen in arbitrary threads, we invoke it explicitly.
|
||||
JfrTraceIdEpoch::has_changed_tag_state_no_reset();
|
||||
if (JfrRecorder::is_recording()) {
|
||||
return serialize(writer, nullptr, true, false);
|
||||
}
|
||||
return register_unloading_klasses();
|
||||
return serialize(writer, nullptr, true, false);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2023, 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,7 +31,7 @@ class JfrCheckpointWriter;
|
||||
|
||||
class JfrTypeSet : AllStatic {
|
||||
public:
|
||||
static void clear();
|
||||
static void clear(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer);
|
||||
static size_t serialize(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer, bool class_unload, bool flushpoint);
|
||||
static size_t on_unloading_classes(JfrCheckpointWriter* writer);
|
||||
};
|
||||
|
@ -91,6 +91,8 @@ class ClearArtifact {
|
||||
bool operator()(T const& value) {
|
||||
CLEAR_SERIALIZED(value);
|
||||
assert(IS_NOT_SERIALIZED(value), "invariant");
|
||||
assert(IS_NOT_LEAKP(value), "invariant");
|
||||
assert(IS_NOT_TRANSIENT(value), "invariant");
|
||||
SET_PREVIOUS_EPOCH_CLEARED_BIT(value);
|
||||
CLEAR_PREVIOUS_EPOCH_METHOD_AND_CLASS(value);
|
||||
return true;
|
||||
@ -103,7 +105,9 @@ class ClearArtifact<const Method*> {
|
||||
bool operator()(const Method* method) {
|
||||
assert(METHOD_FLAG_USED_PREVIOUS_EPOCH(method), "invariant");
|
||||
CLEAR_SERIALIZED_METHOD(method);
|
||||
assert(METHOD_NOT_SERIALIZED(method), "invariant");
|
||||
assert(METHOD_IS_NOT_SERIALIZED(method), "invariant");
|
||||
assert(METHOD_IS_NOT_LEAKP(method), "invariant");
|
||||
assert(METHOD_IS_NOT_TRANSIENT(method), "invariant");
|
||||
SET_PREVIOUS_EPOCH_METHOD_CLEARED_BIT(method);
|
||||
CLEAR_PREVIOUS_EPOCH_METHOD_FLAG(method);
|
||||
return true;
|
||||
@ -128,7 +132,7 @@ class SerializePredicate<const Method*> {
|
||||
SerializePredicate(bool class_unload) : _class_unload(class_unload) {}
|
||||
bool operator()(const Method* method) {
|
||||
assert(method != nullptr, "invariant");
|
||||
return _class_unload ? true : METHOD_NOT_SERIALIZED(method);
|
||||
return _class_unload ? true : METHOD_IS_NOT_SERIALIZED(method);
|
||||
}
|
||||
};
|
||||
|
||||
@ -162,9 +166,9 @@ class MethodFlagPredicate {
|
||||
MethodFlagPredicate(bool current_epoch) : _current_epoch(current_epoch) {}
|
||||
bool operator()(const Method* method) {
|
||||
if (_current_epoch) {
|
||||
return leakp ? IS_METHOD_LEAKP_USED(method) : METHOD_FLAG_USED_THIS_EPOCH(method);
|
||||
return leakp ? METHOD_IS_LEAKP(method) : METHOD_FLAG_USED_THIS_EPOCH(method);
|
||||
}
|
||||
return leakp ? IS_METHOD_LEAKP_USED(method) : METHOD_FLAG_USED_PREVIOUS_EPOCH(method);
|
||||
return leakp ? METHOD_IS_LEAKP(method) : METHOD_FLAG_USED_PREVIOUS_EPOCH(method);
|
||||
}
|
||||
};
|
||||
|
||||
@ -183,7 +187,7 @@ class LeakPredicate<const Method*> {
|
||||
LeakPredicate(bool class_unload) {}
|
||||
bool operator()(const Method* method) {
|
||||
assert(method != nullptr, "invariant");
|
||||
return IS_METHOD_LEAKP_USED(method);
|
||||
return METHOD_IS_LEAKP(method);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2023, 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,9 @@ class JfrTraceId : public AllStatic {
|
||||
static traceid load(const PackageEntry* package);
|
||||
static traceid load(const ClassLoaderData* cld);
|
||||
static traceid load_leakp(const Klass* klass, const Method* method); // leak profiler
|
||||
static traceid load_leakp_previous_epoch(const Klass* klass, const Method* method); // leak profiler
|
||||
static traceid load_no_enqueue(const Method* method);
|
||||
static traceid load_no_enqueue(const Klass* klass, const Method* method);
|
||||
|
||||
// load barrier elision
|
||||
static traceid load_raw(const Klass* klass);
|
||||
|
@ -44,10 +44,18 @@ inline traceid JfrTraceId::load(const Method* method) {
|
||||
return JfrTraceIdLoadBarrier::load(method);
|
||||
}
|
||||
|
||||
inline traceid JfrTraceId::load_no_enqueue(const Method* method) {
|
||||
return JfrTraceIdLoadBarrier::load_no_enqueue(method);
|
||||
}
|
||||
|
||||
inline traceid JfrTraceId::load(const Klass* klass, const Method* method) {
|
||||
return JfrTraceIdLoadBarrier::load(klass, method);
|
||||
}
|
||||
|
||||
inline traceid JfrTraceId::load_no_enqueue(const Klass* klass, const Method* method) {
|
||||
return JfrTraceIdLoadBarrier::load_no_enqueue(klass, method);
|
||||
}
|
||||
|
||||
inline traceid JfrTraceId::load(const ModuleEntry* module) {
|
||||
return JfrTraceIdLoadBarrier::load(module);
|
||||
}
|
||||
@ -64,6 +72,10 @@ inline traceid JfrTraceId::load_leakp(const Klass* klass, const Method* method)
|
||||
return JfrTraceIdLoadBarrier::load_leakp(klass, method);
|
||||
}
|
||||
|
||||
inline traceid JfrTraceId::load_leakp_previous_epoch(const Klass* klass, const Method* method) {
|
||||
return JfrTraceIdLoadBarrier::load_leakp_previuos_epoch(klass, method);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline traceid raw_load(const T* t) {
|
||||
assert(t != nullptr, "invariant");
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 2023, 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,7 +88,10 @@ class JfrTraceIdLoadBarrier : AllStatic {
|
||||
static traceid load(const ModuleEntry* module);
|
||||
static traceid load(const PackageEntry* package);
|
||||
static traceid load_leakp(const Klass* klass, const Method* method); // leak profiler
|
||||
static traceid load_leakp_previuos_epoch(const Klass* klass, const Method* method); // leak profiler
|
||||
static void do_klasses(void f(Klass*), bool previous_epoch = false);
|
||||
static traceid load_no_enqueue(const Klass* klass, const Method* method);
|
||||
static traceid load_no_enqueue(const Method* method);
|
||||
};
|
||||
|
||||
#endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDLOADBARRIER_HPP
|
||||
|
@ -99,6 +99,20 @@ inline traceid JfrTraceIdLoadBarrier::load(const Klass* klass, const Method* met
|
||||
return (METHOD_ID(klass, method));
|
||||
}
|
||||
|
||||
inline traceid JfrTraceIdLoadBarrier::load_no_enqueue(const Method* method) {
|
||||
return load_no_enqueue(method->method_holder(), method);
|
||||
}
|
||||
|
||||
inline traceid JfrTraceIdLoadBarrier::load_no_enqueue(const Klass* klass, const Method* method) {
|
||||
assert(klass != nullptr, "invariant");
|
||||
assert(method != nullptr, "invariant");
|
||||
SET_METHOD_AND_CLASS_USED_THIS_EPOCH(klass);
|
||||
SET_METHOD_FLAG_USED_THIS_EPOCH(method);
|
||||
assert(METHOD_AND_CLASS_USED_THIS_EPOCH(klass), "invariant");
|
||||
assert(METHOD_FLAG_USED_THIS_EPOCH(method), "invariant");
|
||||
return (METHOD_ID(klass, method));
|
||||
}
|
||||
|
||||
inline traceid JfrTraceIdLoadBarrier::load(const ModuleEntry* module) {
|
||||
return set_used_and_get(module);
|
||||
}
|
||||
@ -136,4 +150,21 @@ inline traceid JfrTraceIdLoadBarrier::load_leakp(const Klass* klass, const Metho
|
||||
return (METHOD_ID(klass, method));
|
||||
}
|
||||
|
||||
inline traceid JfrTraceIdLoadBarrier::load_leakp_previuos_epoch(const Klass* klass, const Method* method) {
|
||||
assert(klass != nullptr, "invariant");
|
||||
assert(METHOD_AND_CLASS_USED_PREVIOUS_EPOCH(klass), "invariant");
|
||||
assert(method != nullptr, "invariant");
|
||||
assert(klass == method->method_holder(), "invariant");
|
||||
if (METHOD_FLAG_NOT_USED_PREVIOUS_EPOCH(method)) {
|
||||
// the method is already logically tagged, just like the klass,
|
||||
// but because of redefinition, the latest Method*
|
||||
// representation might not have a reified tag.
|
||||
SET_TRANSIENT(method);
|
||||
assert(METHOD_FLAG_USED_PREVIOUS_EPOCH(method), "invariant");
|
||||
}
|
||||
SET_LEAKP(klass);
|
||||
SET_METHOD_LEAKP(method);
|
||||
return (METHOD_ID(klass, method));
|
||||
}
|
||||
|
||||
#endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDBARRIER_INLINE_HPP
|
||||
|
@ -100,9 +100,10 @@
|
||||
#define METHOD_AND_CLASS_USED_THIS_EPOCH(kls) (TRACE_ID_PREDICATE(kls, (THIS_EPOCH_METHOD_AND_CLASS_BITS)))
|
||||
#define METHOD_AND_CLASS_USED_PREVIOUS_EPOCH(kls) (TRACE_ID_PREDICATE(kls, (PREVIOUS_EPOCH_METHOD_AND_CLASS_BITS)))
|
||||
#define METHOD_AND_CLASS_USED_ANY_EPOCH(kls) (METHOD_USED_ANY_EPOCH(kls) && USED_ANY_EPOCH(kls))
|
||||
#define METHOD_FLAG_USED_THIS_EPOCH(method) (METHOD_FLAG_PREDICATE(method, (THIS_EPOCH_BIT)))
|
||||
#define METHOD_FLAG_USED_THIS_EPOCH(method) (METHOD_FLAG_PREDICATE(method, (TRANSIENT_BIT | THIS_EPOCH_BIT)))
|
||||
#define METHOD_FLAG_NOT_USED_THIS_EPOCH(method) (!(METHOD_FLAG_USED_THIS_EPOCH(method)))
|
||||
#define METHOD_FLAG_USED_PREVIOUS_EPOCH(method) (METHOD_FLAG_PREDICATE(method, (PREVIOUS_EPOCH_BIT)))
|
||||
#define METHOD_FLAG_USED_PREVIOUS_EPOCH(method) (METHOD_FLAG_PREDICATE(method, (TRANSIENT_BIT | PREVIOUS_EPOCH_BIT)))
|
||||
#define METHOD_FLAG_NOT_USED_PREVIOUS_EPOCH(method) (!(METHOD_FLAG_USED_PREVIOUS_EPOCH(method)))
|
||||
#define IS_METHOD_BLESSED(method) (METHOD_FLAG_PREDICATE(method, BLESSED_METHOD_BIT))
|
||||
|
||||
// setters
|
||||
@ -130,21 +131,28 @@
|
||||
#define META_MASK (~(SERIALIZED_META_BIT | TRANSIENT_META_BIT | LEAKP_META_BIT))
|
||||
#define SET_LEAKP(ptr) (TRACE_ID_META_TAG(ptr, LEAKP_META_BIT))
|
||||
#define IS_LEAKP(ptr) (TRACE_ID_PREDICATE(ptr, LEAKP_BIT))
|
||||
#define IS_NOT_LEAKP(ptr) (!(IS_LEAKP(ptr)))
|
||||
#define SET_TRANSIENT(ptr) (TRACE_ID_META_TAG(ptr, TRANSIENT_META_BIT))
|
||||
#define IS_TRANSIENT(ptr) (TRACE_ID_PREDICATE(ptr, TRANSIENT_BIT))
|
||||
#define IS_NOT_TRANSIENT(ptr) (!(IS_TRANSIENT(ptr)))
|
||||
#define SET_SERIALIZED(ptr) (TRACE_ID_META_TAG(ptr, SERIALIZED_META_BIT))
|
||||
#define IS_SERIALIZED(ptr) (TRACE_ID_PREDICATE(ptr, SERIALIZED_BIT))
|
||||
#define IS_NOT_SERIALIZED(ptr) (!(IS_SERIALIZED(ptr)))
|
||||
#define SHOULD_TAG(ptr) (NOT_USED_THIS_EPOCH(ptr))
|
||||
#define SHOULD_TAG_KLASS_METHOD(ptr) (METHOD_NOT_USED_THIS_EPOCH(ptr))
|
||||
#define SET_SERIALIZED(ptr) (TRACE_ID_META_TAG(ptr, SERIALIZED_META_BIT))
|
||||
#define CLEAR_SERIALIZED(ptr) (TRACE_ID_META_MASK_CLEAR(ptr, META_MASK))
|
||||
#define SET_PREVIOUS_EPOCH_CLEARED_BIT(ptr) (TRACE_ID_META_TAG(ptr, PREVIOUS_EPOCH_BIT))
|
||||
#define IS_THIS_EPOCH_CLEARED(ptr) (TRACE_ID_PREDICATE(ptr, THIS_EPOCH_BIT))
|
||||
#define IS_PREVIOUS_EPOCH_CLEARED(ptr) (TRACE_ID_PREDICATE(ptr, PREVIOUS_EPOCH_BIT))
|
||||
#define IS_METHOD_SERIALIZED(method) (METHOD_FLAG_PREDICATE(method, SERIALIZED_BIT))
|
||||
#define IS_METHOD_LEAKP_USED(method) (METHOD_FLAG_PREDICATE(method, LEAKP_BIT))
|
||||
#define METHOD_NOT_SERIALIZED(method) (!(IS_METHOD_SERIALIZED(method)))
|
||||
#define SET_METHOD_LEAKP(method) (METHOD_META_TAG(method, LEAKP_META_BIT))
|
||||
#define SET_METHOD_SERIALIZED(method) (METHOD_META_TAG(method, SERIALIZED_META_BIT))
|
||||
#define METHOD_IS_SERIALIZED(method) (METHOD_FLAG_PREDICATE(method, SERIALIZED_BIT))
|
||||
#define METHOD_IS_NOT_SERIALIZED(method) (!(METHOD_IS_SERIALIZED(method)))
|
||||
#define SET_METHOD_LEAKP(method) (METHOD_META_TAG(method, LEAKP_META_BIT))
|
||||
#define METHOD_IS_LEAKP(method) (METHOD_FLAG_PREDICATE(method, LEAKP_BIT))
|
||||
#define METHOD_IS_NOT_LEAKP(method) (!(METHOD_IS_LEAKP(method)))
|
||||
#define SET_METHOD_TRANSIENT(method) (METHOD_META_TAG(method, TRANSIENT_META_BIT))
|
||||
#define METHOD_IS_TRANSIENT(method) (METHOD_FLAG_PREDICATE(method, TRANSIENT_BIT))
|
||||
#define METHOD_IS_NOT_TRANSIENT(method) (!(METHOD_IS_TRANSIENT(method)))
|
||||
#define CLEAR_SERIALIZED_METHOD(method) (METHOD_META_MASK_CLEAR(method, META_MASK))
|
||||
#define SET_PREVIOUS_EPOCH_METHOD_CLEARED_BIT(ptr) (METHOD_META_TAG(ptr, PREVIOUS_EPOCH_BIT))
|
||||
#define CLEAR_LEAKP(ptr) (TRACE_ID_META_MASK_CLEAR(ptr, (~(LEAKP_META_BIT))))
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2023, 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,11 +35,10 @@ bool JfrEventSetting::set_threshold(jlong id, jlong threshold_ticks) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JfrEventSetting::set_cutoff(jlong id, jlong cutoff_ticks) {
|
||||
void JfrEventSetting::set_miscellaneous(jlong id, jlong level) {
|
||||
JfrEventId event_id = (JfrEventId)id;
|
||||
assert(bounds_check_event(event_id), "invariant");
|
||||
setting(event_id).cutoff_ticks = cutoff_ticks;
|
||||
return true;
|
||||
setting(event_id).miscellaneous = level;
|
||||
}
|
||||
|
||||
void JfrEventSetting::set_stacktrace(jlong id, bool enabled) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2023, 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
|
||||
@ -45,8 +45,9 @@ class JfrEventSetting : AllStatic {
|
||||
static bool has_stacktrace(JfrEventId event_id);
|
||||
static bool set_threshold(jlong event_id, jlong threshold_ticks);
|
||||
static jlong threshold(JfrEventId event_id);
|
||||
static bool set_cutoff(jlong event_id, jlong cutoff_ticks);
|
||||
static void set_miscellaneous(jlong event_id, jlong cutoff_ticks);
|
||||
static jlong cutoff(JfrEventId event_id);
|
||||
static jlong level(JfrEventId event_id);
|
||||
static bool is_large(JfrEventId event_id);
|
||||
static void set_large(JfrEventId event_id);
|
||||
static void unhide_internal_types();
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2023, 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
|
||||
@ -43,8 +43,12 @@ inline jlong JfrEventSetting::threshold(JfrEventId event_id) {
|
||||
return setting(event_id).threshold_ticks;
|
||||
}
|
||||
|
||||
inline jlong JfrEventSetting::level(JfrEventId event_id) {
|
||||
return setting(event_id).miscellaneous;
|
||||
}
|
||||
|
||||
inline jlong JfrEventSetting::cutoff(JfrEventId event_id) {
|
||||
return setting(event_id).cutoff_ticks;
|
||||
return setting(event_id).miscellaneous;
|
||||
}
|
||||
|
||||
inline bool JfrEventSetting::is_large(JfrEventId event_id) {
|
||||
|
@ -77,20 +77,33 @@ bool JfrRecorder::is_enabled() {
|
||||
return _enabled;
|
||||
}
|
||||
|
||||
bool JfrRecorder::is_started_on_commandline() {
|
||||
return StartFlightRecording != nullptr;
|
||||
}
|
||||
|
||||
bool JfrRecorder::create_oop_storages() {
|
||||
// currently only a single weak oop storage for Leak Profiler
|
||||
return ObjectSampler::create_oop_storage();
|
||||
}
|
||||
|
||||
// Subsystem
|
||||
static JfrCheckpointManager* _checkpoint_manager = nullptr;
|
||||
|
||||
bool JfrRecorder::on_create_vm_1() {
|
||||
if (!is_disabled()) {
|
||||
if (FlightRecorder || StartFlightRecording != nullptr) {
|
||||
if (FlightRecorder || is_started_on_commandline()) {
|
||||
enable();
|
||||
}
|
||||
}
|
||||
if (!create_oop_storages()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_checkpoint_manager = JfrCheckpointManager::create();
|
||||
if (_checkpoint_manager == nullptr || !_checkpoint_manager->initialize_early()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// fast time initialization
|
||||
return JfrTime::initialize();
|
||||
}
|
||||
@ -303,7 +316,6 @@ bool JfrRecorder::create_components() {
|
||||
// subsystems
|
||||
static JfrPostBox* _post_box = nullptr;
|
||||
static JfrStorage* _storage = nullptr;
|
||||
static JfrCheckpointManager* _checkpoint_manager = nullptr;
|
||||
static JfrRepository* _repository = nullptr;
|
||||
static JfrStackTraceRepository* _stack_trace_repository;
|
||||
static JfrStringPool* _stringpool = nullptr;
|
||||
@ -345,10 +357,9 @@ bool JfrRecorder::create_storage() {
|
||||
}
|
||||
|
||||
bool JfrRecorder::create_checkpoint_manager() {
|
||||
assert(_checkpoint_manager == nullptr, "invariant");
|
||||
assert(_checkpoint_manager != nullptr, "invariant");
|
||||
assert(_repository != nullptr, "invariant");
|
||||
_checkpoint_manager = JfrCheckpointManager::create(_repository->chunkwriter());
|
||||
return _checkpoint_manager != nullptr && _checkpoint_manager->initialize();
|
||||
return _checkpoint_manager->initialize(&_repository->chunkwriter());
|
||||
}
|
||||
|
||||
bool JfrRecorder::create_stacktrace_repository() {
|
||||
@ -390,7 +401,7 @@ void JfrRecorder::destroy_components() {
|
||||
}
|
||||
if (_checkpoint_manager != nullptr) {
|
||||
JfrCheckpointManager::destroy();
|
||||
_checkpoint_manager = nullptr;
|
||||
// do not delete the _checkpoint_manager instance
|
||||
}
|
||||
if (_stack_trace_repository != nullptr) {
|
||||
JfrStackTraceRepository::destroy();
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2023, 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,8 +61,9 @@ class JfrRecorder : public JfrCHeapObj {
|
||||
public:
|
||||
static bool is_enabled();
|
||||
static bool is_disabled();
|
||||
static bool create(bool simulate_failure);
|
||||
static bool is_created();
|
||||
static bool is_started_on_commandline();
|
||||
static bool create(bool simulate_failure);
|
||||
static void destroy();
|
||||
static void start_recording();
|
||||
static bool is_recording();
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include "jfr/recorder/storage/jfrStorage.hpp"
|
||||
#include "jfr/recorder/storage/jfrStorageControl.hpp"
|
||||
#include "jfr/recorder/stringpool/jfrStringPool.hpp"
|
||||
#include "jfr/support/jfrDeprecationManager.hpp"
|
||||
#include "jfr/utilities/jfrAllocation.hpp"
|
||||
#include "jfr/utilities/jfrThreadIterator.hpp"
|
||||
#include "jfr/utilities/jfrTime.hpp"
|
||||
@ -431,6 +432,7 @@ static void start_recorder() {
|
||||
|
||||
static void stop_recorder() {
|
||||
assert(JfrRotationLock::is_owner(), "invariant");
|
||||
JfrDeprecationManager::on_recorder_stop();
|
||||
set_recorder_state(RUNNING, STOPPED);
|
||||
log_debug(jfr, system)("Recording service STOPPED");
|
||||
}
|
||||
@ -478,6 +480,7 @@ void JfrRecorderService::safepoint_clear() {
|
||||
_checkpoint_manager.begin_epoch_shift();
|
||||
_storage.clear();
|
||||
_chunkwriter.set_time_stamp();
|
||||
JfrDeprecationManager::on_safepoint_clear();
|
||||
JfrStackTraceRepository::clear();
|
||||
_checkpoint_manager.end_epoch_shift();
|
||||
}
|
||||
@ -506,6 +509,7 @@ void JfrRecorderService::vm_error_rotation() {
|
||||
Thread* const thread = Thread::current();
|
||||
_storage.flush_regular_buffer(thread->jfr_thread_local()->native_buffer(), thread);
|
||||
_chunkwriter.mark_chunk_final();
|
||||
JfrDeprecationManager::write_edges(_chunkwriter, thread, true);
|
||||
invoke_flush();
|
||||
_chunkwriter.set_time_stamp();
|
||||
_repository.close_chunk();
|
||||
@ -588,6 +592,7 @@ void JfrRecorderService::safepoint_write() {
|
||||
_checkpoint_manager.on_rotation();
|
||||
_storage.write_at_safepoint();
|
||||
_chunkwriter.set_time_stamp();
|
||||
JfrDeprecationManager::on_safepoint_write();
|
||||
write_stacktrace(_stack_trace_repository, _chunkwriter, true);
|
||||
_checkpoint_manager.end_epoch_shift();
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2023, 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
|
||||
@ -69,7 +69,6 @@ class JfrRecorderService : public StackObj {
|
||||
void rotate(int msgs);
|
||||
void flushpoint();
|
||||
void process_full_buffers();
|
||||
void scavenge();
|
||||
void evaluate_chunk_size_for_rotation();
|
||||
static bool is_recording();
|
||||
};
|
||||
|
@ -243,3 +243,8 @@ size_t JfrStackTraceRepository::clear() {
|
||||
clear_leak_profiler();
|
||||
return clear(instance());
|
||||
}
|
||||
|
||||
traceid JfrStackTraceRepository::next_id() {
|
||||
MutexLocker lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag);
|
||||
return ++_next_id;
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ class JfrCheckpointWriter;
|
||||
class JfrChunkWriter;
|
||||
|
||||
class JfrStackTraceRepository : public JfrCHeapObj {
|
||||
friend class JfrDeprecatedEdge;
|
||||
friend class JfrRecorder;
|
||||
friend class JfrRecorderService;
|
||||
friend class JfrThreadSampleClosure;
|
||||
@ -64,6 +65,8 @@ class JfrStackTraceRepository : public JfrCHeapObj {
|
||||
static void record_for_leak_profiler(JavaThread* thread, int skip = 0);
|
||||
static void clear_leak_profiler();
|
||||
|
||||
static traceid next_id();
|
||||
|
||||
traceid add_trace(const JfrStackTrace& stacktrace);
|
||||
static traceid add(JfrStackTraceRepository& repo, const JfrStackTrace& stacktrace);
|
||||
static traceid add(const JfrStackTrace& stacktrace);
|
||||
|
154
src/hotspot/share/jfr/support/jfrDeprecationEventWriter.cpp
Normal file
154
src/hotspot/share/jfr/support/jfrDeprecationEventWriter.cpp
Normal file
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (c) 2023, 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 "jfrfiles/jfrEventIds.hpp"
|
||||
#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
|
||||
#include "jfr/recorder/jfrEventSetting.inline.hpp"
|
||||
#include "jfr/recorder/repository/jfrChunkWriter.hpp"
|
||||
#include "jfr/support/jfrDeprecationEventWriter.hpp"
|
||||
#include "jfr/support/jfrDeprecationManager.hpp"
|
||||
#include "jfr/utilities/jfrTypes.hpp"
|
||||
#include "runtime/thread.inline.hpp"
|
||||
|
||||
// This dual state machine for the level setting is because when multiple recordings are running,
|
||||
// and one of them stops, the newly calculated settings for level is updated before the chunk rotates.
|
||||
// But we need remember what the level setting was before the recording stopped.
|
||||
constexpr const int64_t uninitialized = -1;
|
||||
static int64_t _previous_level_setting = uninitialized;
|
||||
static int64_t _current_level_setting = uninitialized;
|
||||
|
||||
void JfrDeprecatedEventWriterState::on_initialization() {
|
||||
_previous_level_setting = uninitialized;
|
||||
_current_level_setting = uninitialized;
|
||||
}
|
||||
|
||||
void JfrDeprecatedEventWriterState::on_level_setting_update(int64_t new_level) {
|
||||
_previous_level_setting = _current_level_setting;
|
||||
_current_level_setting = new_level;
|
||||
}
|
||||
|
||||
static inline bool level() {
|
||||
assert(_current_level_setting != uninitialized, "invariant");
|
||||
return _previous_level_setting == uninitialized ? _current_level_setting : _previous_level_setting;
|
||||
}
|
||||
|
||||
static inline bool only_for_removal() {
|
||||
assert(JfrEventSetting::is_enabled(JfrDeprecatedInvocationEvent), "invariant");
|
||||
// level 0: forRemoval, level 1: = all
|
||||
return level() == 0;
|
||||
}
|
||||
|
||||
void JfrDeprecatedStackTraceWriter::install_stacktrace_blob(JfrDeprecatedEdge* edge, JfrCheckpointWriter& writer, JavaThread* jt) {
|
||||
assert(edge != nullptr, "invariant");
|
||||
assert(!edge->has_stacktrace(), "invariant");
|
||||
assert(writer.used_offset() == 0, "invariant");
|
||||
writer.write(edge->stacktrace_id());
|
||||
writer.write(true); // truncated
|
||||
writer.write(1); // number of frames
|
||||
writer.write(edge->sender_methodid());
|
||||
writer.write<u4>(edge->linenumber());
|
||||
writer.write<u4>(edge->bci());
|
||||
writer.write<u1>(edge->frame_type());
|
||||
JfrBlobHandle blob = writer.move();
|
||||
edge->set_stacktrace(blob);
|
||||
}
|
||||
|
||||
// This op will collapse all individual stacktrace blobs into a single TYPE_STACKTRACE checkpoint.
|
||||
JfrDeprecatedStackTraceWriter::JfrDeprecatedStackTraceWriter(JfrChunkWriter& cw) :
|
||||
_cw(cw), _begin_offset(cw.current_offset()), _elements_offset(0), _processed(0), _elements(0), _for_removal(only_for_removal()) {
|
||||
const int64_t last_checkpoint = cw.last_checkpoint_offset();
|
||||
const int64_t delta = last_checkpoint == 0 ? 0 : last_checkpoint - _begin_offset;
|
||||
cw.reserve(sizeof(uint64_t));
|
||||
cw.write(EVENT_CHECKPOINT);
|
||||
cw.write(JfrTicks::now().value());
|
||||
cw.write(0);
|
||||
cw.write(delta);
|
||||
cw.write(GENERIC); // Generic checkpoint type.
|
||||
cw.write(1); // Number of types in this checkpoint, only one, TYPE_STACKTRACE.
|
||||
cw.write(TYPE_STACKTRACE); // Constant pool type.
|
||||
_elements_offset = cw.current_offset(); // Offset for the number of entries in the TYPE_STACKTRACE constant pool.
|
||||
cw.reserve(sizeof(uint32_t));
|
||||
}
|
||||
|
||||
JfrDeprecatedStackTraceWriter::~JfrDeprecatedStackTraceWriter() {
|
||||
if (_elements == 0) {
|
||||
// Rewind.
|
||||
_cw.seek(_begin_offset);
|
||||
return;
|
||||
}
|
||||
const int64_t event_size = _cw.current_offset() - _begin_offset;
|
||||
_cw.write_padded_at_offset(_elements, _elements_offset);
|
||||
_cw.write_padded_at_offset(event_size, _begin_offset);
|
||||
_cw.set_last_checkpoint_offset(_begin_offset);
|
||||
}
|
||||
|
||||
bool JfrDeprecatedStackTraceWriter::process(const JfrDeprecatedEdge* edge) {
|
||||
assert(edge != nullptr, "invariant");
|
||||
assert(edge->has_stacktrace(), "invariant");
|
||||
if (_for_removal && !edge->for_removal()) {
|
||||
return true;
|
||||
}
|
||||
++_elements;
|
||||
edge->stacktrace()->write(_cw);
|
||||
_processed += edge->stacktrace()->size();
|
||||
return true;
|
||||
}
|
||||
|
||||
JfrDeprecatedEventWriter::JfrDeprecatedEventWriter(JfrChunkWriter& cw, bool stacktrace) :
|
||||
_now(JfrTicks::now()),_cw(cw), _for_removal(only_for_removal()), _stacktrace(stacktrace), _did_write(false) {}
|
||||
|
||||
static size_t calculate_event_size(const JfrDeprecatedEdge* edge, JfrChunkWriter& cw, const JfrTicks& now, bool stacktrace) {
|
||||
assert(edge != nullptr, "invariant");
|
||||
size_t bytes = cw.size_in_bytes(JfrDeprecatedInvocationEvent);
|
||||
bytes += cw.size_in_bytes(now); // starttime
|
||||
bytes += cw.size_in_bytes(stacktrace ? edge->stacktrace_id() : 0); // stacktrace
|
||||
bytes += cw.size_in_bytes(edge->deprecated_methodid());
|
||||
bytes += cw.size_in_bytes(edge->invocation_time());
|
||||
bytes += cw.size_in_bytes(edge->for_removal());
|
||||
return bytes + cw.size_in_bytes(bytes + cw.size_in_bytes(bytes));
|
||||
}
|
||||
|
||||
static void write_event(const JfrDeprecatedEdge* edge, JfrChunkWriter& cw, const JfrTicks& now, bool stacktrace) {
|
||||
assert(edge != nullptr, "invariant");
|
||||
cw.write(calculate_event_size(edge, cw, now, stacktrace));
|
||||
cw.write(JfrDeprecatedInvocationEvent);
|
||||
cw.write(now);
|
||||
cw.write(stacktrace ? edge->stacktrace_id() : 0);
|
||||
cw.write(edge->deprecated_methodid());
|
||||
cw.write(edge->invocation_time());
|
||||
cw.write(edge->for_removal());
|
||||
}
|
||||
|
||||
bool JfrDeprecatedEventWriter::process(const JfrDeprecatedEdge* edge) {
|
||||
assert(edge != nullptr, "invariant");
|
||||
if (_for_removal && !edge->for_removal()) {
|
||||
return true;
|
||||
}
|
||||
write_event(edge, _cw,_now, _stacktrace);
|
||||
if (!_did_write) {
|
||||
_did_write = true;
|
||||
}
|
||||
return true;
|
||||
}
|
74
src/hotspot/share/jfr/support/jfrDeprecationEventWriter.hpp
Normal file
74
src/hotspot/share/jfr/support/jfrDeprecationEventWriter.hpp
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (c) 2023, 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_JFR_SUPPORT_JFRDEPRECATIONEVENTWRITER_HPP
|
||||
#define SHARE_JFR_SUPPORT_JFRDEPRECATIONEVENTWRITER_HPP
|
||||
|
||||
#include "memory/allocation.hpp"
|
||||
#include "jfr/utilities/jfrBlob.hpp"
|
||||
#include "jfr/utilities/jfrTime.hpp"
|
||||
|
||||
class JfrCheckpointWriter;
|
||||
class JfrChunkWriter;
|
||||
class JfrDeprecatedEdge;
|
||||
|
||||
// This writer will collapse all individual stacktrace blobs into a single TYPE_STACKTRACE checkpoint.
|
||||
class JfrDeprecatedStackTraceWriter : public StackObj{
|
||||
private:
|
||||
JfrChunkWriter& _cw;
|
||||
int64_t _begin_offset;
|
||||
int64_t _elements_offset;
|
||||
size_t _processed;
|
||||
uint32_t _elements;
|
||||
bool _for_removal;
|
||||
public:
|
||||
JfrDeprecatedStackTraceWriter(JfrChunkWriter& cw);
|
||||
~JfrDeprecatedStackTraceWriter();
|
||||
size_t elements() const { return _elements; }
|
||||
size_t processed() const { return _processed; }
|
||||
bool process(const JfrDeprecatedEdge* edge);
|
||||
|
||||
static void install_stacktrace_blob(JfrDeprecatedEdge* edge, JfrCheckpointWriter& writer, JavaThread* jt);
|
||||
};
|
||||
|
||||
class JfrDeprecatedEventWriter : public StackObj {
|
||||
private:
|
||||
JfrTicks _now;
|
||||
JfrChunkWriter& _cw;
|
||||
bool _for_removal;
|
||||
bool _stacktrace;
|
||||
bool _did_write;
|
||||
public:
|
||||
JfrDeprecatedEventWriter(JfrChunkWriter& cw, bool stacktrace);
|
||||
bool did_write() const { return _did_write; }
|
||||
bool process(const JfrDeprecatedEdge* edge);
|
||||
};
|
||||
|
||||
class JfrDeprecatedEventWriterState : AllStatic {
|
||||
public:
|
||||
static void on_initialization();
|
||||
static void on_level_setting_update(int64_t new_level);
|
||||
};
|
||||
|
||||
#endif // SHARE_JFR_SUPPORT_JFRDEPRECATIONEVENTWRITER_HPP
|
380
src/hotspot/share/jfr/support/jfrDeprecationManager.cpp
Normal file
380
src/hotspot/share/jfr/support/jfrDeprecationManager.cpp
Normal file
@ -0,0 +1,380 @@
|
||||
/*
|
||||
* Copyright (c) 2023, 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 "classfile/moduleEntry.hpp"
|
||||
#include "jfrfiles/jfrEventIds.hpp"
|
||||
#include "jfr/jni/jfrJavaSupport.hpp"
|
||||
#include "jfr/recorder/jfrRecorder.hpp"
|
||||
#include "jfr/recorder/jfrEventSetting.inline.hpp"
|
||||
#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
|
||||
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp"
|
||||
#include "jfr/recorder/repository/jfrChunkWriter.hpp"
|
||||
#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp"
|
||||
#include "jfr/support/jfrDeprecationEventWriter.hpp"
|
||||
#include "jfr/support/jfrDeprecationManager.hpp"
|
||||
#include "jfr/support/jfrKlassUnloading.hpp"
|
||||
#include "jfr/support/jfrMethodData.hpp"
|
||||
#include "jfr/support/jfrMethodLookup.hpp"
|
||||
#include "jfr/utilities/jfrBlob.hpp"
|
||||
#include "jfr/utilities/jfrLinkedList.inline.hpp"
|
||||
#include "jfr/utilities/jfrTime.hpp"
|
||||
#include "logging/log.hpp"
|
||||
#include "memory/resourceArea.inline.hpp"
|
||||
#include "oops/instanceKlass.inline.hpp"
|
||||
#include "oops/method.inline.hpp"
|
||||
#include "runtime/atomic.hpp"
|
||||
#include "runtime/interfaceSupport.inline.hpp"
|
||||
#include "runtime/thread.inline.hpp"
|
||||
|
||||
// for strstr
|
||||
#include <string.h>
|
||||
|
||||
static bool _enqueue_klasses = false;
|
||||
|
||||
void JfrDeprecationManager::on_recorder_stop() {
|
||||
_enqueue_klasses = false;
|
||||
}
|
||||
|
||||
static inline traceid load_traceid(const Method* method) {
|
||||
assert(method != nullptr, "invariant");
|
||||
// If the Jfr system is not yet running we only tag the artifacts, not enqueuing klasses.
|
||||
return _enqueue_klasses ? JfrTraceId::load(method) : JfrTraceId::load_no_enqueue(method);
|
||||
}
|
||||
|
||||
JfrDeprecatedEdge::JfrDeprecatedEdge(const Method* method, Method* sender, int bci, u1 frame_type, JavaThread* jt) :
|
||||
_invocation_time(JfrTicks::now()),
|
||||
_stacktrace(),
|
||||
_next(nullptr),
|
||||
_deprecated_ik(method->method_holder()),
|
||||
_deprecated_methodid(load_traceid(method)),
|
||||
_sender_ik(sender->method_holder()),
|
||||
_sender_methodid(load_traceid(sender)),
|
||||
// Our stacktrace will be hand-rolled into a blob.
|
||||
// We don't need anything from the stacktrace
|
||||
// subsystem except for a unique id.
|
||||
_stack_trace_id(JfrStackTraceRepository::next_id()),
|
||||
_bci(bci),
|
||||
_linenumber(sender->line_number_from_bci(bci)),
|
||||
_frame_type(frame_type),
|
||||
_for_removal(method->deprecated_for_removal()) {}
|
||||
|
||||
bool JfrDeprecatedEdge::has_stacktrace() const {
|
||||
return _stacktrace.valid();
|
||||
}
|
||||
|
||||
void JfrDeprecatedEdge::set_stacktrace(const JfrBlobHandle& blob) {
|
||||
assert(!has_stacktrace(), "invariant");
|
||||
_stacktrace = blob;
|
||||
}
|
||||
|
||||
const JfrBlobHandle& JfrDeprecatedEdge::stacktrace() const {
|
||||
assert(has_stacktrace(), "invariant");
|
||||
return _stacktrace;
|
||||
}
|
||||
|
||||
typedef JfrLinkedList<JfrDeprecatedEdge> DeprecatedEdgeList;
|
||||
|
||||
static DeprecatedEdgeList _list; // Newly constructed edges are concurrently added to this list.
|
||||
static DeprecatedEdgeList _pending_list; // During epoch rotation (safepoint) entries in _list are moved onto _pending_list
|
||||
static DeprecatedEdgeList _resolved_list; // Fully resolved edges (event and stacktrace blobs).
|
||||
|
||||
static JfrDeprecatedEdge* allocate_edge(const Method* method, Method* sender, int bci, u1 frame_type, JavaThread* jt) {
|
||||
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt);)
|
||||
assert(method != nullptr, "invariant");
|
||||
assert(method->deprecated(), "invariant");
|
||||
assert(sender != nullptr, "invariant");
|
||||
assert(jt != nullptr, "invariant");
|
||||
return new JfrDeprecatedEdge(method, sender, bci, frame_type, jt);
|
||||
}
|
||||
|
||||
static void create_edge(const Method* method, Method* sender, int bci, u1 frame_type, JavaThread* jt) {
|
||||
JfrDeprecatedEdge* edge;
|
||||
// We need the JavaThread to be in _thread_in_vm when creating the edge.
|
||||
// This is because the method artifacts needs to be tagged in the correct epoch.
|
||||
if (jt->thread_state() != _thread_in_vm) {
|
||||
assert(jt->is_Compiler_thread(), "invariant");
|
||||
// Can safepoint here.
|
||||
ThreadInVMfromNative transition(jt);
|
||||
edge = allocate_edge(method, sender, bci, frame_type, jt);
|
||||
} else {
|
||||
edge = allocate_edge(method, sender, bci, frame_type, jt);
|
||||
}
|
||||
_list.add(edge);
|
||||
}
|
||||
|
||||
static constexpr const size_t max_num_edges = 10000;
|
||||
|
||||
static void log_max_num_edges_reached() {
|
||||
log_info(jfr)("The number of deprecated method invocations recorded has reached a maximum limit of %zu.", max_num_edges);
|
||||
log_info(jfr)("Deprecated method invocations will not be recorded from now on.");
|
||||
log_info(jfr)("Reduce the number of deprecated method invocations and try again.");
|
||||
}
|
||||
|
||||
static bool max_limit_not_reached() {
|
||||
static size_t num_edges = 0;
|
||||
size_t compare_value;
|
||||
do {
|
||||
compare_value = Atomic::load(&num_edges);
|
||||
if (compare_value == max_num_edges) {
|
||||
return false;
|
||||
}
|
||||
} while (compare_value != Atomic::cmpxchg(&num_edges, compare_value, compare_value + 1));
|
||||
if (compare_value + 1 == max_num_edges) {
|
||||
log_max_num_edges_reached();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Two cases for JDK modules as outlined by JEP 200: The Modular JDK.
|
||||
*
|
||||
* The modular structure of the JDK implements the following principles:
|
||||
* 1. Standard modules, whose specifications are governed by the JCP, have names starting with the string "java.".
|
||||
* 2. All other modules are merely part of the JDK, and have names starting with the string "jdk.".
|
||||
* */
|
||||
static inline bool is_jdk_module(const char* module_name) {
|
||||
assert(module_name != nullptr, "invariant");
|
||||
return strstr(module_name, "java.") == module_name || strstr(module_name, "jdk.") == module_name;
|
||||
}
|
||||
|
||||
static inline bool is_unnamed_module(const ModuleEntry* module) {
|
||||
return module == nullptr || !module->is_named();
|
||||
}
|
||||
|
||||
static inline bool is_jdk_module(const ModuleEntry* module, JavaThread* jt) {
|
||||
assert(jt != nullptr, "invariant");
|
||||
if (is_unnamed_module(module)) {
|
||||
return false;
|
||||
}
|
||||
ResourceMark rm(jt);
|
||||
const Symbol* const module_symbol = module->name();
|
||||
assert(module_symbol != nullptr, "invariant");
|
||||
const char* const module_name = module_symbol->as_C_string();
|
||||
return is_jdk_module(module_name);
|
||||
}
|
||||
|
||||
static inline bool is_not_jdk_module(const ModuleEntry* module, JavaThread* jt) {
|
||||
return !is_jdk_module(module, jt);
|
||||
}
|
||||
|
||||
static inline bool jfr_is_started_on_command_line() {
|
||||
return JfrRecorder::is_started_on_commandline();
|
||||
}
|
||||
|
||||
static bool should_record(const Method* method, const Method* sender, JavaThread* jt) {
|
||||
assert(method != nullptr, "invariant");
|
||||
assert(method->deprecated(), "invariant");
|
||||
assert(sender != nullptr, "invariant");
|
||||
assert(!sender->is_native(), "invariant");
|
||||
assert(jt != nullptr, "invariant");
|
||||
assert(jfr_is_started_on_command_line(), "invariant");
|
||||
const ModuleEntry* const deprecated_module = method->method_holder()->module();
|
||||
// Only record invoked deprecated methods in the JDK.
|
||||
if (is_not_jdk_module(deprecated_module, jt)) {
|
||||
return false;
|
||||
}
|
||||
const ModuleEntry* const sender_module = sender->method_holder()->module();
|
||||
// Only record senders not in the JDK and if we are still within budget.
|
||||
return is_not_jdk_module(sender_module, jt) && max_limit_not_reached();
|
||||
}
|
||||
|
||||
// This is the entry point for newly discovered edges in JfrResolution.cpp.
|
||||
void JfrDeprecationManager::on_link(const Method* method, Method* sender, int bci, u1 frame_type, JavaThread* jt) {
|
||||
assert(method != nullptr, "invariant");
|
||||
assert(method->deprecated(), "invariant");
|
||||
assert(sender != nullptr, "invariant");
|
||||
assert(!sender->is_native(), "invariant");
|
||||
assert(jt != nullptr, "invariant");
|
||||
assert(JfrRecorder::is_started_on_commandline(), "invariant");
|
||||
if (JfrMethodData::mark_deprecated_call_site(sender, bci, jt)) {
|
||||
if (should_record(method, sender, jt)) {
|
||||
create_edge(method, sender, bci, frame_type, jt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void transfer_list() {
|
||||
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
|
||||
assert(_pending_list.is_empty(), "invariant");
|
||||
DeprecatedEdgeList::NodePtr head = _list.cut();
|
||||
assert(_list.is_empty(), "invariant");
|
||||
if (head != nullptr) {
|
||||
_pending_list.add_list(head);
|
||||
}
|
||||
}
|
||||
|
||||
void JfrDeprecationManager::on_level_setting_update(int64_t new_level) {
|
||||
JfrDeprecatedEventWriterState::on_level_setting_update(new_level);
|
||||
}
|
||||
|
||||
void JfrDeprecationManager::on_safepoint_clear() {
|
||||
assert(!_enqueue_klasses, "invariant");
|
||||
// We are now starting JFR, so begin enqueuing tagged klasses.
|
||||
_enqueue_klasses = true;
|
||||
JfrDeprecatedEventWriterState::on_initialization();
|
||||
transfer_list();
|
||||
}
|
||||
|
||||
void JfrDeprecationManager::on_safepoint_write() {
|
||||
assert(_enqueue_klasses, "invariant");
|
||||
transfer_list();
|
||||
}
|
||||
|
||||
static bool is_klass_unloaded(traceid klass_id) {
|
||||
assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
|
||||
return JfrKlassUnloading::is_unloaded(klass_id, true);
|
||||
}
|
||||
|
||||
static void add_to_leakp_set(const InstanceKlass* ik, traceid method_id) {
|
||||
// The lock is needed to ensure the klass unloading lists do not grow in the middle of inspection.
|
||||
assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
|
||||
assert(ik != nullptr, "invariant");
|
||||
if (is_klass_unloaded(JfrMethodLookup::klass_id(method_id))) {
|
||||
return;
|
||||
}
|
||||
const Method* const method = JfrMethodLookup::lookup(ik, method_id);
|
||||
assert(method != nullptr, "invariant");
|
||||
assert(method->method_holder() == ik, "invariant");
|
||||
JfrTraceId::load_leakp_previous_epoch(ik, method); // now has the leakp marker
|
||||
}
|
||||
|
||||
static void add_to_leakp_set(const JfrDeprecatedEdge* edge) {
|
||||
assert(edge != nullptr, "invariant");
|
||||
add_to_leakp_set(edge->deprecated_ik(), edge->deprecated_methodid());
|
||||
add_to_leakp_set(edge->sender_ik(), edge->sender_methodid());
|
||||
}
|
||||
|
||||
// Keeps track of nodes processed from the _pending_list.
|
||||
static DeprecatedEdgeList::NodePtr _pending_head = nullptr;
|
||||
static DeprecatedEdgeList::NodePtr _pending_tail = nullptr;
|
||||
|
||||
class PendingListProcessor {
|
||||
private:
|
||||
JfrCheckpointWriter& _writer;
|
||||
JavaThread* _jt;
|
||||
public:
|
||||
PendingListProcessor(JfrCheckpointWriter& writer, JavaThread* jt) : _writer(writer), _jt(jt) {}
|
||||
bool process(DeprecatedEdgeList::NodePtr edge) {
|
||||
assert(edge != nullptr, "invariant");
|
||||
JfrDeprecatedStackTraceWriter::install_stacktrace_blob(edge, _writer, _jt);
|
||||
assert(edge->has_stacktrace(), "invariant");
|
||||
add_to_leakp_set(edge);
|
||||
if (_pending_head == nullptr) {
|
||||
_pending_head = edge;
|
||||
}
|
||||
_pending_tail = edge;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void JfrDeprecationManager::prepare_type_set(JavaThread* jt) {
|
||||
_pending_head = nullptr;
|
||||
_pending_tail = nullptr;
|
||||
if (_pending_list.is_nonempty()) {
|
||||
JfrKlassUnloading::sort(true);
|
||||
JfrCheckpointWriter writer(true /* prev epoch */, jt, false /* header */);
|
||||
PendingListProcessor plp(writer, jt);
|
||||
_pending_list.iterate(plp);
|
||||
assert(_pending_head != nullptr, "invariant");
|
||||
assert(_pending_tail != nullptr, "invariant");
|
||||
assert(_pending_tail->next() == nullptr, "invariant");
|
||||
// Excise already resolved edges to link them.
|
||||
_pending_tail->set_next(_resolved_list.cut());
|
||||
// Re-insertion.
|
||||
_resolved_list.add_list(_pending_head);
|
||||
_pending_list.clear();
|
||||
}
|
||||
assert(_pending_list.is_empty(), "invariant");
|
||||
}
|
||||
|
||||
// A linked-list of blob handles.
|
||||
static JfrBlobHandle type_set_blobs;
|
||||
|
||||
static inline void write_type_set_blobs(JfrCheckpointWriter& writer) {
|
||||
type_set_blobs->write(writer);
|
||||
}
|
||||
|
||||
static void save_type_set_blob(JfrCheckpointWriter& writer, bool copy = false) {
|
||||
assert(writer.has_data(), "invariant");
|
||||
const JfrBlobHandle blob = copy ? writer.copy() : writer.move();
|
||||
if (type_set_blobs.valid()) {
|
||||
type_set_blobs->set_next(blob);
|
||||
} else {
|
||||
type_set_blobs = blob;
|
||||
}
|
||||
}
|
||||
|
||||
void JfrDeprecationManager::on_type_set_unload(JfrCheckpointWriter& writer) {
|
||||
if (writer.has_data()) {
|
||||
save_type_set_blob(writer, true);
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool has_stacktrace() {
|
||||
return JfrEventSetting::has_stacktrace(JfrDeprecatedInvocationEvent);
|
||||
}
|
||||
|
||||
static inline bool write_events(JfrChunkWriter& cw) {
|
||||
assert(_resolved_list.is_nonempty(), "invariant");
|
||||
JfrDeprecatedEventWriter ebw(cw, has_stacktrace());
|
||||
_resolved_list.iterate(ebw);
|
||||
return ebw.did_write();
|
||||
}
|
||||
|
||||
static inline void write_stacktraces(JfrChunkWriter& cw) {
|
||||
assert(has_stacktrace(), "invariant");
|
||||
JfrDeprecatedStackTraceWriter scw(cw);
|
||||
_resolved_list.iterate(scw);
|
||||
}
|
||||
|
||||
static inline void write_type_sets(Thread* thread, bool on_error) {
|
||||
JfrCheckpointWriter writer(!on_error, thread, false);
|
||||
write_type_set_blobs(writer);
|
||||
}
|
||||
|
||||
// First, we consolidate all stacktrace blobs into a single TYPE_STACKTRACE checkpoint and serialize it to the chunk.
|
||||
// Secondly, we serialize all events to the chunk.
|
||||
// Thirdly, the type set blobs are written into the JfrCheckpoint system, to be serialized to the chunk
|
||||
// just after we return from here.
|
||||
void JfrDeprecationManager::write_edges(JfrChunkWriter& cw, Thread* thread, bool on_error /* false */) {
|
||||
if (_resolved_list.is_nonempty() && JfrEventSetting::is_enabled(JfrDeprecatedInvocationEvent)) {
|
||||
if (has_stacktrace()) {
|
||||
write_stacktraces(cw);
|
||||
}
|
||||
if (write_events(cw)) {
|
||||
write_type_sets(thread, on_error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JfrDeprecationManager::on_type_set(JfrCheckpointWriter& writer, JfrChunkWriter* cw, Thread* thread) {
|
||||
assert(_pending_list.is_empty(), "invariant");
|
||||
if (writer.has_data() && _pending_head != nullptr) {
|
||||
save_type_set_blob(writer);
|
||||
}
|
||||
if (cw != nullptr) {
|
||||
write_edges(*cw, thread);
|
||||
}
|
||||
}
|
99
src/hotspot/share/jfr/support/jfrDeprecationManager.hpp
Normal file
99
src/hotspot/share/jfr/support/jfrDeprecationManager.hpp
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (c) 2023, 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_JFR_SUPPORT_JFRDEPRECATIONMANAGER_HPP
|
||||
#define SHARE_JFR_SUPPORT_JFRDEPRECATIONMANAGER_HPP
|
||||
|
||||
#include "memory/allocation.hpp"
|
||||
#include "jfr/utilities/jfrBlob.hpp"
|
||||
#include "jfr/utilities/jfrTypes.hpp"
|
||||
|
||||
class JavaThread;
|
||||
class JfrCheckpointWriter;
|
||||
class JfrChunkWriter;
|
||||
class Method;
|
||||
class Thread;
|
||||
|
||||
class JfrDeprecatedEdge : public CHeapObj<mtTracing> {
|
||||
template<typename, typename>
|
||||
friend class JfrLinkedList;
|
||||
friend class JfrDeprecatedStackTraceWriter;
|
||||
private:
|
||||
JfrTicks _invocation_time;
|
||||
JfrBlobHandle _stacktrace;
|
||||
JfrDeprecatedEdge* _next;
|
||||
InstanceKlass* _deprecated_ik;
|
||||
traceid _deprecated_methodid;
|
||||
InstanceKlass* _sender_ik;
|
||||
traceid _sender_methodid;
|
||||
traceid _stack_trace_id;
|
||||
int _bci;
|
||||
int _linenumber;
|
||||
u1 _frame_type;
|
||||
bool _for_removal;
|
||||
|
||||
void set_stacktrace(const JfrBlobHandle& blob);
|
||||
|
||||
public:
|
||||
JfrDeprecatedEdge(const Method* method, Method* sender, int bci, u1 frame_type, JavaThread* jt);
|
||||
|
||||
const JfrDeprecatedEdge* next() const { return _next; }
|
||||
void set_next(JfrDeprecatedEdge* edge) { _next = edge; }
|
||||
|
||||
bool has_event() const;
|
||||
const JfrBlobHandle& event() const;
|
||||
const JfrBlobHandle& event_no_stacktrace() const;
|
||||
bool has_stacktrace() const;
|
||||
const JfrBlobHandle& stacktrace() const;
|
||||
void install_stacktrace_blob(JavaThread* jt);
|
||||
|
||||
const InstanceKlass* deprecated_ik() const { return _deprecated_ik; }
|
||||
traceid deprecated_methodid() const { return _deprecated_methodid; }
|
||||
|
||||
const InstanceKlass* sender_ik() const { return _sender_ik; }
|
||||
traceid sender_methodid() const { return _sender_methodid; }
|
||||
|
||||
const JfrTicks& invocation_time() const { return _invocation_time; }
|
||||
traceid stacktrace_id() const { return _stack_trace_id; }
|
||||
|
||||
int bci() const { return _bci; }
|
||||
u1 frame_type() const { return _frame_type; }
|
||||
bool for_removal() const { return _for_removal; }
|
||||
int linenumber() const { return _linenumber; }
|
||||
};
|
||||
|
||||
class JfrDeprecationManager : AllStatic {
|
||||
public:
|
||||
static void on_safepoint_clear();
|
||||
static void on_safepoint_write();
|
||||
static void on_recorder_stop();
|
||||
static void prepare_type_set(JavaThread* jt);
|
||||
static void on_type_set(JfrCheckpointWriter& writer, JfrChunkWriter* cw, Thread* thread);
|
||||
static void on_type_set_unload(JfrCheckpointWriter& writer);
|
||||
static void write_edges(JfrChunkWriter& cw, Thread* thread, bool on_error = false);
|
||||
static void on_link(const Method* method, Method* sender, int bci, u1 frame_type, JavaThread* thread);
|
||||
static void on_level_setting_update(int64_t new_level);
|
||||
};
|
||||
|
||||
#endif // SHARE_JFR_SUPPORT_JFRDEPRECATIONMANAGER_HPP
|
77
src/hotspot/share/jfr/support/jfrMethodData.cpp
Normal file
77
src/hotspot/share/jfr/support/jfrMethodData.cpp
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2023, 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 "jfr/support/jfrMethodData.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "oops/method.inline.hpp"
|
||||
#include "oops/methodData.inline.hpp"
|
||||
#include "runtime/handles.inline.hpp"
|
||||
|
||||
// Caller requires ResourceMark.
|
||||
static inline BitData* get_bit_data(MethodData* mdo, int bci) {
|
||||
assert(mdo != nullptr, "invariant");
|
||||
BitData* const bit_data = static_cast<BitData*>(mdo->bci_to_data(bci));
|
||||
assert(bit_data != nullptr, "invariant");
|
||||
assert(bit_data->is_VirtualCallData() || bit_data->is_VirtualCallTypeData() ||
|
||||
bit_data->is_CounterData() || bit_data->is_CallTypeData(), "invariant");
|
||||
return bit_data;
|
||||
}
|
||||
|
||||
static inline MethodData* build_mdo(Method* method, JavaThread* jt) {
|
||||
methodHandle method_handle(jt, method);
|
||||
Method::build_profiling_method_data(method_handle, jt);
|
||||
return method->method_data();
|
||||
}
|
||||
|
||||
static inline MethodData* get_mdo(Method* method, JavaThread* jt) {
|
||||
MethodData* mdo = method->method_data();
|
||||
return mdo != nullptr ? mdo : build_mdo(method, jt);
|
||||
}
|
||||
|
||||
static bool mark_mdo(Method* method, int bci, JavaThread* jt) {
|
||||
assert(method != nullptr, "invariant");
|
||||
assert(!method->is_native(), "native methods have no MDO bit data");
|
||||
assert(jt != nullptr, "invariant");
|
||||
MethodData* const mdo = get_mdo(method, jt);
|
||||
assert(mdo != nullptr, "invariant");
|
||||
// Get the datalayout for the invocation bci.
|
||||
BitData* const bit_data = get_bit_data(mdo, bci);
|
||||
// Returns true if this callsite is not yet linked and
|
||||
// our attempt to set the deprecated flag was succesful.
|
||||
if (bit_data->set_deprecated_method_call_site()) {
|
||||
assert(bit_data->deprecated_method_call_site(), "invariant");
|
||||
return true;
|
||||
}
|
||||
assert(bit_data->deprecated_method_call_site(), "invariant");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool JfrMethodData::mark_deprecated_call_site(Method* method, int bci, JavaThread* jt) {
|
||||
assert(method != nullptr, "invariant");
|
||||
assert(jt != nullptr, "invariant");
|
||||
assert(method->validate_bci(bci) >= 0, "invariant");
|
||||
ResourceMark rm(jt);
|
||||
return mark_mdo(method, bci, jt);
|
||||
}
|
38
src/hotspot/share/jfr/support/jfrMethodData.hpp
Normal file
38
src/hotspot/share/jfr/support/jfrMethodData.hpp
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2023, 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_JFR_SUPPORT_JFRMETHODDATA_HPP
|
||||
#define SHARE_JFR_SUPPORT_JFRMETHODDATA_HPP
|
||||
|
||||
#include "memory/allocation.hpp"
|
||||
|
||||
class Method;
|
||||
class JavaThread;
|
||||
|
||||
class JfrMethodData : AllStatic {
|
||||
public:
|
||||
static bool mark_deprecated_call_site(Method* method, int bci, JavaThread* jt);
|
||||
};
|
||||
|
||||
#endif // SHARE_JFR_SUPPORT_JFRMETHODDATA_HPP
|
@ -32,7 +32,9 @@
|
||||
JfrNativeLibraryEventBase::JfrNativeLibraryEventBase(const char* name) : _name(name), _error_msg(nullptr), _start_time(nullptr) {}
|
||||
|
||||
JfrNativeLibraryEventBase::~JfrNativeLibraryEventBase() {
|
||||
delete _start_time;
|
||||
if (_start_time != nullptr) {
|
||||
delete _start_time;
|
||||
}
|
||||
}
|
||||
|
||||
const char* JfrNativeLibraryEventBase::name() const {
|
||||
|
295
src/hotspot/share/jfr/support/jfrResolution.cpp
Normal file
295
src/hotspot/share/jfr/support/jfrResolution.cpp
Normal file
@ -0,0 +1,295 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 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 "ci/ciKlass.hpp"
|
||||
#include "ci/ciMethod.hpp"
|
||||
#include "classfile/vmSymbols.hpp"
|
||||
#include "interpreter/linkResolver.hpp"
|
||||
#include "jfr/recorder/jfrRecorder.hpp"
|
||||
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdMacros.hpp"
|
||||
#include "jfr/recorder/stacktrace/jfrStackTrace.hpp"
|
||||
#include "jfr/support/jfrDeprecationManager.hpp"
|
||||
#include "jfr/support/jfrResolution.hpp"
|
||||
#include "memory/resourceArea.inline.hpp"
|
||||
#include "oops/method.inline.hpp"
|
||||
#include "runtime/javaThread.inline.hpp"
|
||||
#include "runtime/vframe.inline.hpp"
|
||||
#ifdef COMPILER1
|
||||
#include "c1/c1_GraphBuilder.hpp"
|
||||
#endif
|
||||
#ifdef COMPILER2
|
||||
#include "opto/parse.hpp"
|
||||
#endif
|
||||
|
||||
// for strstr
|
||||
#include <string.h>
|
||||
|
||||
// The following packages are internal implmentation details used by reflection.
|
||||
// We exclude matching frames on the stack in a manner similar to StackWalker.
|
||||
static constexpr const int NUM_EXCLUDED_PACKAGES = 4;
|
||||
static constexpr const char* excluded_packages[NUM_EXCLUDED_PACKAGES] = { "java/lang/invoke/",
|
||||
"jdk/internal/reflect/",
|
||||
"java/lang/reflect/",
|
||||
"sun/invoke/" };
|
||||
|
||||
static inline bool match(const char* str, const char* sub_str) {
|
||||
assert(str != nullptr, "invariant");
|
||||
assert(sub_str != nullptr, "invariant");
|
||||
return strstr(str, sub_str) == str;
|
||||
}
|
||||
|
||||
// Caller requires ResourceMark.
|
||||
static inline bool exclude_frame(const Method* method) {
|
||||
assert(method != nullptr, "invariant");
|
||||
// exclude native methods.
|
||||
if (method->is_native()) {
|
||||
return true;
|
||||
}
|
||||
const Klass* const klass = method->method_holder();
|
||||
assert(klass != nullptr, "invariant");
|
||||
const Symbol* const klass_sym = klass->name();
|
||||
assert(klass_sym != nullptr, "invariant");
|
||||
const char* const klass_name = klass_sym->as_C_string();
|
||||
assert(klass_name != nullptr, "invariant");
|
||||
for (int i = 0; i < NUM_EXCLUDED_PACKAGES; ++i) {
|
||||
if (match(klass_name,excluded_packages[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static Method* find_real_sender(vframeStream& stream, JavaThread* jt) {
|
||||
assert(jt != nullptr, "invariant");
|
||||
assert(stream.method()->is_native(), "invariant");
|
||||
ResourceMark rm(jt);
|
||||
while (!stream.at_end()) {
|
||||
stream.next();
|
||||
Method* method = stream.method();
|
||||
if (!exclude_frame(method)) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static inline bool jfr_is_started_on_command_line() {
|
||||
return JfrRecorder::is_started_on_commandline();
|
||||
}
|
||||
|
||||
static inline Method* frame_context(vframeStream& stream, int& bci, u1& frame_type, JavaThread* jt) {
|
||||
Method* method = stream.method();
|
||||
assert(method != nullptr, "invariant");
|
||||
if (method->is_native()) {
|
||||
method = find_real_sender(stream, jt);
|
||||
if (method == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
assert(method != nullptr, "invariant");
|
||||
assert(!method->is_native(), "invariant");
|
||||
bci = stream.bci();
|
||||
frame_type = stream.is_interpreted_frame() ? JfrStackFrame::FRAME_INTERPRETER : JfrStackFrame::FRAME_JIT;
|
||||
if (frame_type == JfrStackFrame::FRAME_JIT && !stream.at_end()) {
|
||||
const intptr_t* const id = stream.frame_id();
|
||||
stream.next();
|
||||
if (id == stream.frame_id()) {
|
||||
frame_type = JfrStackFrame::FRAME_INLINE;
|
||||
}
|
||||
}
|
||||
return method;
|
||||
}
|
||||
|
||||
static inline Method* ljf_sender_method(int& bci, u1& frame_type, JavaThread* jt) {
|
||||
assert(jt != nullptr, "invariant");
|
||||
if (!jt->has_last_Java_frame()) {
|
||||
return nullptr;
|
||||
}
|
||||
vframeStream stream(jt, true, false);
|
||||
return frame_context(stream, bci, frame_type, jt);
|
||||
}
|
||||
|
||||
static inline void on_runtime_deprecated(const Method* method, JavaThread* jt) {
|
||||
assert(jt != nullptr, "invariant");
|
||||
assert(method != nullptr, "invariant");
|
||||
assert(method->deprecated(), "invariant");
|
||||
if (jfr_is_started_on_command_line()) {
|
||||
int bci;
|
||||
u1 frame_type;
|
||||
Method* const sender = ljf_sender_method(bci, frame_type, jt);
|
||||
if (sender != nullptr) {
|
||||
JfrDeprecationManager::on_link(method, sender, bci, frame_type, jt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We can circumvent the need to hook into backpatching if ciMethod is made aware
|
||||
// of the deprecated annotation already as part of parsing bytecodes of the callee method.
|
||||
static void on_backpatching_deprecated(const Method* deprecated_method, JavaThread* jt) {
|
||||
assert(deprecated_method != nullptr, "invariant");
|
||||
assert(deprecated_method->deprecated(), "invariant");
|
||||
assert(jt->has_last_Java_frame(), "invariant");
|
||||
assert(jt->last_frame().is_runtime_frame(), "invariant");
|
||||
if (jfr_is_started_on_command_line()) {
|
||||
vframeStream stream(jt, true, false);
|
||||
assert(!stream.at_end(), "invariant");
|
||||
stream.next(); // now at caller
|
||||
int bci;
|
||||
u1 frame_type;
|
||||
Method* const sender = frame_context(stream, bci, frame_type, jt);
|
||||
if (sender != nullptr) {
|
||||
JfrDeprecationManager::on_link(deprecated_method, sender, bci, frame_type, jt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JfrResolution::on_backpatching(const Method* callee_method, JavaThread* jt) {
|
||||
assert(callee_method != nullptr, "invariant");
|
||||
assert(jt != nullptr, "invariant");
|
||||
if (callee_method->deprecated()) {
|
||||
on_backpatching_deprecated(callee_method, jt);
|
||||
}
|
||||
}
|
||||
|
||||
static inline const Method* ljf_sender_method(JavaThread* jt) {
|
||||
assert(jt != nullptr, "invariant");
|
||||
if (!jt->has_last_Java_frame()) {
|
||||
return nullptr;
|
||||
}
|
||||
const vframeStream ljf(jt, true, false);
|
||||
return ljf.method();
|
||||
}
|
||||
|
||||
static const char* const link_error_msg = "illegal access linking method 'jdk.jfr.internal.event.EventWriterFactory.getEventWriter(long)'";
|
||||
|
||||
void JfrResolution::on_runtime_resolution(const CallInfo & info, TRAPS) {
|
||||
assert(info.selected_method() != nullptr, "invariant");
|
||||
assert(info.resolved_klass() != nullptr, "invariant");
|
||||
static const Symbol* const event_writer_method_name = vmSymbols::getEventWriter_name();
|
||||
assert(event_writer_method_name != nullptr, "invariant");
|
||||
Method* const method = info.selected_method();
|
||||
assert(method != nullptr, "invariant");
|
||||
if (method->deprecated()) {
|
||||
on_runtime_deprecated(method, THREAD);
|
||||
return;
|
||||
}
|
||||
// Fast path
|
||||
if (method->name() != event_writer_method_name) {
|
||||
return;
|
||||
}
|
||||
static const Symbol* const event_writer_factory_klass_name = vmSymbols::jdk_jfr_internal_event_EventWriterFactory();
|
||||
assert(event_writer_factory_klass_name != nullptr, "invariant");
|
||||
if (info.resolved_klass()->name() != event_writer_factory_klass_name) {
|
||||
return;
|
||||
}
|
||||
// Attempting to link against jdk.jfr.internal.event.EventWriterFactory.getEventWriter().
|
||||
// The sender, i.e. the method attempting to link, is in the ljf (if one exists).
|
||||
const Method* const sender = ljf_sender_method(THREAD);
|
||||
if (sender == nullptr) {
|
||||
// A compiler thread is doing linktime resolution but there is no information about the sender available.
|
||||
// For the compiler threads, the sender is instead found as part of bytecode parsing.
|
||||
return;
|
||||
}
|
||||
// Is the sender method blessed for linkage?
|
||||
if (IS_METHOD_BLESSED(sender)) {
|
||||
return;
|
||||
}
|
||||
#if INCLUDE_JVMCI
|
||||
// JVMCI compiler is doing linktime resolution
|
||||
if (sender->method_holder()->name() == vmSymbols::jdk_vm_ci_hotspot_CompilerToVM()) {
|
||||
if (sender->name()->equals("lookupMethodInPool")) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
THROW_MSG(vmSymbols::java_lang_IllegalAccessError(), link_error_msg);
|
||||
}
|
||||
|
||||
static inline bool is_compiler_linking_event_writer(const Symbol* holder, const Symbol* name) {
|
||||
static const Symbol* const event_writer_factory_klass_name = vmSymbols::jdk_jfr_internal_event_EventWriterFactory();
|
||||
assert(event_writer_factory_klass_name != nullptr, "invariant");
|
||||
if (holder != event_writer_factory_klass_name) {
|
||||
return false;
|
||||
}
|
||||
static const Symbol* const event_writer_method_name = vmSymbols::getEventWriter_name();
|
||||
assert(event_writer_method_name != nullptr, "invariant");
|
||||
return name == event_writer_method_name;
|
||||
}
|
||||
|
||||
static inline bool is_compiler_linking_event_writer(const ciKlass * holder, const ciMethod * target) {
|
||||
assert(holder != nullptr, "invariant");
|
||||
assert(target != nullptr, "invariant");
|
||||
return is_compiler_linking_event_writer(holder->name()->get_symbol(), target->name()->get_symbol());
|
||||
}
|
||||
|
||||
static inline void on_compiler_resolve_deprecated(const ciMethod* target, int bci, Method* sender) {
|
||||
assert(target != nullptr, "invariant");
|
||||
assert(sender != nullptr, "invariant");
|
||||
if (jfr_is_started_on_command_line()) {
|
||||
const Method* const method = target->get_Method();
|
||||
assert(method != nullptr, "Invariant");
|
||||
assert(method->deprecated(), "invariant");
|
||||
JfrDeprecationManager::on_link(method, sender, bci, JfrStackFrame::FRAME_JIT, JavaThread::current());
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef COMPILER1
|
||||
// C1
|
||||
void JfrResolution::on_c1_resolution(const GraphBuilder * builder, const ciKlass * holder, const ciMethod * target) {
|
||||
Method* const sender = builder->method()->get_Method();
|
||||
if (is_compiler_linking_event_writer(holder, target) && !IS_METHOD_BLESSED(sender)) {
|
||||
builder->bailout(link_error_msg);
|
||||
return;
|
||||
}
|
||||
if (target->deprecated()) {
|
||||
on_compiler_resolve_deprecated(target, builder->bci(), sender);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef COMPILER2
|
||||
// C2
|
||||
void JfrResolution::on_c2_resolution(const Parse * parse, const ciKlass * holder, const ciMethod * target) {
|
||||
Method* const sender = parse->method()->get_Method();
|
||||
if (is_compiler_linking_event_writer(holder, target) && !IS_METHOD_BLESSED(sender)) {
|
||||
parse->C->record_failure(link_error_msg);
|
||||
return;
|
||||
}
|
||||
if (target->deprecated()) {
|
||||
on_compiler_resolve_deprecated(target, parse->bci(), sender);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if INCLUDE_JVMCI
|
||||
// JVMCI
|
||||
void JfrResolution::on_jvmci_resolution(const Method* caller, const Method* target, TRAPS) {
|
||||
if (is_compiler_linking_event_writer(target->method_holder()->name(), target->name())) {
|
||||
if (caller == nullptr || !IS_METHOD_BLESSED(caller)) {
|
||||
THROW_MSG(vmSymbols::java_lang_IllegalAccessError(), link_error_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, 2023, 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
|
||||
@ -22,8 +22,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHARE_JFR_INSTRUMENTATION_JFRRESOLUTION_HPP
|
||||
#define SHARE_JFR_INSTRUMENTATION_JFRRESOLUTION_HPP
|
||||
#ifndef SHARE_SUPPORT_JFRRESOLUTION_HPP
|
||||
#define SHARE_SUPPORT_JFRRESOLUTION_HPP
|
||||
|
||||
#include "memory/allocation.hpp"
|
||||
#include "utilities/exceptions.hpp"
|
||||
@ -32,6 +32,7 @@ class CallInfo;
|
||||
class ciKlass;
|
||||
class ciMethod;
|
||||
class GraphBuilder;
|
||||
class JavaThread;
|
||||
class Parse;
|
||||
|
||||
class JfrResolution : AllStatic {
|
||||
@ -40,7 +41,7 @@ class JfrResolution : AllStatic {
|
||||
static void on_c1_resolution(const GraphBuilder * builder, const ciKlass * holder, const ciMethod * target);
|
||||
static void on_c2_resolution(const Parse * parse, const ciKlass * holder, const ciMethod * target);
|
||||
static void on_jvmci_resolution(const Method* caller, const Method* target, TRAPS);
|
||||
static void on_backpatching(const Method* callee_method, JavaThread* jt);
|
||||
};
|
||||
|
||||
#endif // SHARE_JFR_INSTRUMENTATION_JFRRESOLUTION_HPP
|
||||
|
||||
#endif // SHARE_SUPPORT_JFRRESOLUTION_HPP
|
@ -57,13 +57,13 @@ static JfrSymbolTable& instance() {
|
||||
|
||||
JfrSymbolTable* JfrSymbolTable::create() {
|
||||
assert(_instance == nullptr, "invariant");
|
||||
assert_lock_strong(ClassLoaderDataGraph_lock);
|
||||
assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
|
||||
_instance = new JfrSymbolTable();
|
||||
return _instance;
|
||||
}
|
||||
|
||||
void JfrSymbolTable::destroy() {
|
||||
assert_lock_strong(ClassLoaderDataGraph_lock);
|
||||
assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
|
||||
if (_instance != nullptr) {
|
||||
delete _instance;
|
||||
_instance = nullptr;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2023, 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
|
||||
@ -66,6 +66,9 @@ class JfrBlob : public JfrCHeapObj {
|
||||
_next->exclusive_write(writer);
|
||||
}
|
||||
}
|
||||
size_t size() const {
|
||||
return _size;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // SHARE_JFR_UTILITIES_JFRBLOB_HPP
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 2023, 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
|
||||
@ -43,12 +43,15 @@ class JfrLinkedList : public AllocPolicy {
|
||||
bool is_empty() const;
|
||||
bool is_nonempty() const;
|
||||
void add(NodePtr node);
|
||||
void add_list(NodePtr first);
|
||||
NodePtr remove();
|
||||
template <typename Callback>
|
||||
void iterate(Callback& cb);
|
||||
NodePtr head() const;
|
||||
NodePtr excise(NodePtr prev, NodePtr node);
|
||||
bool in_list(const NodeType* node) const;
|
||||
NodePtr cut();
|
||||
void clear();
|
||||
private:
|
||||
NodePtr _head;
|
||||
};
|
||||
|
@ -118,4 +118,25 @@ bool JfrLinkedList<NodeType, AllocPolicy>::in_list(const NodeType* node) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename NodeType, typename AllocPolicy>
|
||||
NodeType* JfrLinkedList<NodeType, AllocPolicy>::cut() {
|
||||
NodePtr node;
|
||||
do {
|
||||
node = head();
|
||||
} while (Atomic::cmpxchg(&_head, node, (NodeType*)nullptr) != node);
|
||||
return node;
|
||||
}
|
||||
|
||||
template <typename NodeType, typename AllocPolicy>
|
||||
void JfrLinkedList<NodeType, AllocPolicy>::clear() {
|
||||
cut();
|
||||
}
|
||||
|
||||
|
||||
template <typename NodeType, typename AllocPolicy>
|
||||
inline void JfrLinkedList<NodeType, AllocPolicy>::add_list(NodeType* first) {
|
||||
assert(head() == nullptr, "invariant");
|
||||
Atomic::store(&_head, first);
|
||||
}
|
||||
|
||||
#endif // SHARE_JFR_UTILITIES_JFRLINKEDLIST_INLINE_HPP
|
||||
|
@ -58,6 +58,8 @@ class ConstMethodFlags {
|
||||
flag(is_scoped , 1 << 16) \
|
||||
flag(changes_current_thread , 1 << 17) \
|
||||
flag(jvmti_mount_transition , 1 << 18) \
|
||||
flag(deprecated , 1 << 19) \
|
||||
flag(deprecated_for_removal , 1 << 20) \
|
||||
/* end of list */
|
||||
|
||||
#define CM_FLAGS_ENUM_NAME(name, value) _misc_##name = value,
|
||||
|
@ -413,7 +413,7 @@ class ConstantPool : public Metadata {
|
||||
unresolved_klass_at_put(cp_index, name_index, CPKlassSlot::_temp_resolved_klass_index);
|
||||
}
|
||||
|
||||
jint int_at(int cp_index) {
|
||||
jint int_at(int cp_index) const {
|
||||
assert(tag_at(cp_index).is_int(), "Corrupted constant pool");
|
||||
return *int_at_addr(cp_index);
|
||||
}
|
||||
|
@ -249,6 +249,12 @@ class Method : public Metadata {
|
||||
u2 max_locals() const { return constMethod()->max_locals(); }
|
||||
void set_max_locals(int size) { constMethod()->set_max_locals(size); }
|
||||
|
||||
void set_deprecated() { constMethod()->set_deprecated(); }
|
||||
bool deprecated() const { return constMethod()->deprecated(); }
|
||||
|
||||
void set_deprecated_for_removal() { constMethod()->set_deprecated_for_removal(); }
|
||||
bool deprecated_for_removal() const { return constMethod()->deprecated_for_removal(); }
|
||||
|
||||
int highest_comp_level() const;
|
||||
void set_highest_comp_level(int level);
|
||||
int highest_osr_comp_level() const;
|
||||
|
@ -663,9 +663,6 @@ MethodData* MethodData::allocate(ClassLoaderData* loader_data, const methodHandl
|
||||
}
|
||||
|
||||
int MethodData::bytecode_cell_count(Bytecodes::Code code) {
|
||||
if (CompilerConfig::is_c1_simple_only() && !ProfileInterpreter) {
|
||||
return no_profile_data;
|
||||
}
|
||||
switch (code) {
|
||||
case Bytecodes::_checkcast:
|
||||
case Bytecodes::_instanceof:
|
||||
@ -986,9 +983,6 @@ int MethodData::compute_allocation_size_in_words(const methodHandle& method) {
|
||||
// the segment in bytes.
|
||||
int MethodData::initialize_data(BytecodeStream* stream,
|
||||
int data_index) {
|
||||
if (CompilerConfig::is_c1_simple_only() && !ProfileInterpreter) {
|
||||
return 0;
|
||||
}
|
||||
int cell_count = -1;
|
||||
u1 tag = DataLayout::no_tag;
|
||||
DataLayout* data_layout = data_layout_at(data_index);
|
||||
|
@ -183,7 +183,7 @@ public:
|
||||
}
|
||||
|
||||
u1 flags() const {
|
||||
return _header._struct._flags;
|
||||
return Atomic::load_acquire(&_header._struct._flags);
|
||||
}
|
||||
|
||||
u2 bci() const {
|
||||
@ -204,11 +204,36 @@ public:
|
||||
return _cells[index];
|
||||
}
|
||||
|
||||
void set_flag_at(u1 flag_number) {
|
||||
_header._struct._flags |= (u1)(0x1 << flag_number);
|
||||
bool set_flag_at(u1 flag_number) {
|
||||
const u1 bit = 1 << flag_number;
|
||||
u1 compare_value;
|
||||
do {
|
||||
compare_value = _header._struct._flags;
|
||||
if ((compare_value & bit) == bit) {
|
||||
// already set.
|
||||
return false;
|
||||
}
|
||||
} while (compare_value != Atomic::cmpxchg(&_header._struct._flags, compare_value, static_cast<u1>(compare_value | bit)));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool clear_flag_at(u1 flag_number) {
|
||||
const u1 bit = 1 << flag_number;
|
||||
u1 compare_value;
|
||||
u1 exchange_value;
|
||||
do {
|
||||
compare_value = _header._struct._flags;
|
||||
if ((compare_value & bit) == 0) {
|
||||
// already cleaed.
|
||||
return false;
|
||||
}
|
||||
exchange_value = compare_value & ~bit;
|
||||
} while (compare_value != Atomic::cmpxchg(&_header._struct._flags, compare_value, exchange_value));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool flag_at(u1 flag_number) const {
|
||||
return (_header._struct._flags & (0x1 << flag_number)) != 0;
|
||||
return (flags() & (1 << flag_number)) != 0;
|
||||
}
|
||||
|
||||
// Low-level support for code generation.
|
||||
@ -491,11 +516,12 @@ protected:
|
||||
enum : u1 {
|
||||
// null_seen:
|
||||
// saw a null operand (cast/aastore/instanceof)
|
||||
null_seen_flag = DataLayout::first_flag + 0,
|
||||
exception_handler_entered_flag = null_seen_flag + 1
|
||||
null_seen_flag = DataLayout::first_flag + 0,
|
||||
exception_handler_entered_flag = null_seen_flag + 1,
|
||||
deprecated_method_callsite_flag = exception_handler_entered_flag + 1
|
||||
#if INCLUDE_JVMCI
|
||||
// bytecode threw any exception
|
||||
, exception_seen_flag = exception_handler_entered_flag + 1
|
||||
, exception_seen_flag = deprecated_method_callsite_flag + 1
|
||||
#endif
|
||||
};
|
||||
enum { bit_cell_count = 0 }; // no additional data fields needed.
|
||||
@ -519,6 +545,9 @@ public:
|
||||
// Consulting it allows the compiler to avoid setting up null_check traps.
|
||||
bool null_seen() { return flag_at(null_seen_flag); }
|
||||
void set_null_seen() { set_flag_at(null_seen_flag); }
|
||||
bool deprecated_method_call_site() const { return flag_at(deprecated_method_callsite_flag); }
|
||||
bool set_deprecated_method_call_site() { return data()->set_flag_at(deprecated_method_callsite_flag); }
|
||||
bool clear_deprecated_method_call_site() { return data()->clear_flag_at(deprecated_method_callsite_flag); }
|
||||
|
||||
#if INCLUDE_JVMCI
|
||||
// true if an exception was thrown at the specific BCI
|
||||
|
@ -81,6 +81,9 @@
|
||||
#ifdef COMPILER1
|
||||
#include "c1/c1_Runtime1.hpp"
|
||||
#endif
|
||||
#if INCLUDE_JFR
|
||||
#include "jfr/jfr.hpp"
|
||||
#endif
|
||||
|
||||
// Shared stub locations
|
||||
RuntimeStub* SharedRuntime::_wrong_method_blob;
|
||||
@ -1342,6 +1345,7 @@ bool SharedRuntime::resolve_sub_helper_internal(methodHandle callee_method, cons
|
||||
CompiledStaticCall::compute_entry(callee_method, is_nmethod, static_call_info);
|
||||
}
|
||||
|
||||
JFR_ONLY(bool patched_caller = false;)
|
||||
// grab lock, check for deoptimization and potentially patch caller
|
||||
{
|
||||
CompiledICLocker ml(caller_nm);
|
||||
@ -1371,6 +1375,7 @@ bool SharedRuntime::resolve_sub_helper_internal(methodHandle callee_method, cons
|
||||
if (!inline_cache->set_to_monomorphic(virtual_call_info)) {
|
||||
return false;
|
||||
}
|
||||
JFR_ONLY(patched_caller = true;)
|
||||
}
|
||||
} else {
|
||||
if (VM_Version::supports_fast_class_init_checks() &&
|
||||
@ -1383,10 +1388,14 @@ bool SharedRuntime::resolve_sub_helper_internal(methodHandle callee_method, cons
|
||||
if (is_nmethod && caller_nm->method()->is_continuation_enter_intrinsic()) {
|
||||
ssc->compute_entry_for_continuation_entry(callee_method, static_call_info);
|
||||
}
|
||||
if (ssc->is_clean()) ssc->set(static_call_info);
|
||||
if (ssc->is_clean()) {
|
||||
ssc->set(static_call_info);
|
||||
JFR_ONLY(patched_caller = true;)
|
||||
}
|
||||
}
|
||||
}
|
||||
} // unlock CompiledICLocker
|
||||
JFR_ONLY(if (patched_caller) Jfr::on_backpatching(callee_method(), THREAD);)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,7 @@ import jdk.jfr.events.StackFilter;
|
||||
import jdk.jfr.internal.JVM;
|
||||
import jdk.jfr.internal.settings.CutoffSetting;
|
||||
import jdk.jfr.internal.settings.EnabledSetting;
|
||||
import jdk.jfr.internal.settings.LevelSetting;
|
||||
import jdk.jfr.internal.settings.PeriodSetting;
|
||||
import jdk.jfr.internal.settings.StackTraceSetting;
|
||||
import jdk.jfr.internal.settings.ThresholdSetting;
|
||||
@ -69,6 +70,7 @@ public final class EventControl {
|
||||
private static final Type TYPE_CUTOFF = TypeLibrary.createType(CutoffSetting.class);
|
||||
private static final Type TYPE_THROTTLE = TypeLibrary.createType(ThrottleSetting.class);
|
||||
private static final long STACK_FILTER_ID = Type.getTypeId(StackFilter.class);
|
||||
private static final Type TYPE_LEVEL = TypeLibrary.createType(LevelSetting.class);
|
||||
|
||||
private final ArrayList<SettingControl> settingControls = new ArrayList<>();
|
||||
private final ArrayList<NamedControl> namedControls = new ArrayList<>(5);
|
||||
@ -91,6 +93,9 @@ public final class EventControl {
|
||||
if (eventType.hasThrottle()) {
|
||||
addControl(Throttle.NAME, defineThrottle(eventType));
|
||||
}
|
||||
if (eventType.hasLevel()) {
|
||||
addControl(Level.NAME, defineLevel(eventType));
|
||||
}
|
||||
addControl(Enabled.NAME, defineEnabled(eventType));
|
||||
|
||||
addStackFilters(eventType);
|
||||
@ -337,6 +342,14 @@ public final class EventControl {
|
||||
return new Control(new ThrottleSetting(type), def);
|
||||
}
|
||||
|
||||
private static Control defineLevel(PlatformEventType type) {
|
||||
Level level = type.getAnnotation(Level.class);
|
||||
String[] values = level.value();
|
||||
String def = values[0];
|
||||
type.add(PrivateAccess.getInstance().newSettingDescriptor(TYPE_LEVEL, Level.NAME, def, Collections.emptyList()));
|
||||
return new Control(new LevelSetting(type, values), def);
|
||||
}
|
||||
|
||||
private static Control definePeriod(PlatformEventType type) {
|
||||
Period period = type.getAnnotation(Period.class);
|
||||
String def = "everyChunk";
|
||||
|
@ -659,4 +659,12 @@ public final class JVM {
|
||||
* @param stackFilterId the stack filter ID to unregister
|
||||
*/
|
||||
public static native void unregisterStackFilter(long stackFilterId);
|
||||
|
||||
/**
|
||||
* Sets bits used for event settings, like cutoff(ticks) and level
|
||||
*
|
||||
* @param eventTypeId the id of the event type
|
||||
* @param value
|
||||
*/
|
||||
public static native void setMiscellaneous(long eventTypeId, long value);
|
||||
}
|
||||
|
59
src/jdk.jfr/share/classes/jdk/jfr/internal/Level.java
Normal file
59
src/jdk.jfr/share/classes/jdk/jfr/internal/Level.java
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.jfr.internal;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import jdk.jfr.MetadataDefinition;
|
||||
|
||||
/**
|
||||
* Event annotation, determines filtering level.
|
||||
*
|
||||
* This settings is only supported for JVM events.
|
||||
*
|
||||
* @since 22
|
||||
*/
|
||||
@MetadataDefinition
|
||||
@Target({ ElementType.TYPE })
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Level {
|
||||
/**
|
||||
* Settings name {@code "level"} for configuring filtering level.
|
||||
*/
|
||||
public static final String NAME = "level";
|
||||
|
||||
/**
|
||||
* Levels, for example {@code "forRemoval", "all"}.
|
||||
*
|
||||
* @return the levels, default { "forRemoval", "all" }, not {@code null}
|
||||
*/
|
||||
String[] value() default {};
|
||||
}
|
@ -81,6 +81,7 @@ public final class MetadataLoader {
|
||||
private final boolean stackTrace;
|
||||
private final boolean cutoff;
|
||||
private final boolean throttle;
|
||||
private final String level;
|
||||
private final boolean isEvent;
|
||||
private final boolean isRelation;
|
||||
private final boolean experimental;
|
||||
@ -103,6 +104,7 @@ public final class MetadataLoader {
|
||||
period = dis.readUTF();
|
||||
cutoff = dis.readBoolean();
|
||||
throttle = dis.readBoolean();
|
||||
level = dis.readUTF();
|
||||
experimental = dis.readBoolean();
|
||||
internal = dis.readBoolean();
|
||||
id = dis.readLong();
|
||||
@ -307,6 +309,13 @@ public final class MetadataLoader {
|
||||
aes.add(STACK_TRACE);
|
||||
}
|
||||
}
|
||||
if (!t.level.isEmpty()) {
|
||||
String[] levels = t.level.split(",");
|
||||
for (int i = 0; i < levels.length; i++) {
|
||||
levels[i] = levels[i].strip();
|
||||
}
|
||||
aes.add(new AnnotationElement(Level.class, levels));
|
||||
}
|
||||
if (t.cutoff) {
|
||||
aes.add(new AnnotationElement(Cutoff.class, Cutoff.INFINITY));
|
||||
}
|
||||
|
@ -73,6 +73,7 @@ public final class MetadataRepository {
|
||||
EventType eventType = PrivateAccess.getInstance().newEventType(pEventType);
|
||||
pEventType.setHasCutoff(eventType.getAnnotation(Cutoff.class) != null);
|
||||
pEventType.setHasThrottle(eventType.getAnnotation(Throttle.class) != null);
|
||||
pEventType.setHasLevel(eventType.getAnnotation(Level.class) != null);
|
||||
pEventType.setHasPeriod(eventType.getAnnotation(Period.class) != null);
|
||||
// Must add hook before EventControl is created as it removes
|
||||
// annotations, such as Period and Threshold.
|
||||
|
@ -65,6 +65,7 @@ public final class PlatformEventType extends Type {
|
||||
private boolean markForInstrumentation;
|
||||
private boolean registered = true;
|
||||
private boolean committable = enabled && registered;
|
||||
private boolean hasLevel = false;
|
||||
|
||||
// package private
|
||||
PlatformEventType(String name, long id, boolean isJDK, boolean dynamicSettings) {
|
||||
@ -142,7 +143,17 @@ public final class PlatformEventType extends Type {
|
||||
public void setCutoff(long cutoffNanos) {
|
||||
if (isJVM) {
|
||||
long cutoffTicks = JVMSupport.nanosToTicks(cutoffNanos);
|
||||
JVM.setCutoff(getId(), cutoffTicks);
|
||||
JVM.setMiscellaneous(getId(), cutoffTicks);
|
||||
}
|
||||
}
|
||||
|
||||
public void setLevel(long level) {
|
||||
setMiscellaneous(level);
|
||||
}
|
||||
|
||||
private void setMiscellaneous(long value) {
|
||||
if (isJVM) {
|
||||
JVM.setMiscellaneous(getId(), value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,6 +167,14 @@ public final class PlatformEventType extends Type {
|
||||
this.hasPeriod = hasPeriod;
|
||||
}
|
||||
|
||||
public void setHasLevel(boolean hasLevel) {
|
||||
this.hasLevel = hasLevel;
|
||||
}
|
||||
|
||||
public boolean hasLevel() {
|
||||
return this.hasLevel;
|
||||
}
|
||||
|
||||
public boolean hasStackTrace() {
|
||||
return getField(ImplicitFields.STACK_TRACE) != null;
|
||||
}
|
||||
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.jfr.internal.settings;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.jfr.Label;
|
||||
import jdk.jfr.MetadataDefinition;
|
||||
import jdk.jfr.Name;
|
||||
import jdk.jfr.internal.PlatformEventType;
|
||||
import jdk.jfr.internal.Type;
|
||||
|
||||
@MetadataDefinition
|
||||
@Label("Level")
|
||||
@Name(Type.SETTINGS_PREFIX + "Level")
|
||||
public class LevelSetting extends JDKSettingControl {
|
||||
private final PlatformEventType eventType;
|
||||
private final List<String> levels;
|
||||
private String value;
|
||||
|
||||
public LevelSetting(PlatformEventType eventType, String[] levels) {
|
||||
this.eventType = Objects.requireNonNull(eventType);
|
||||
this.levels = Arrays.asList(Objects.requireNonNull(levels));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String combine(Set<String> values) {
|
||||
int maxIndex = 0;
|
||||
for (String value : values) {
|
||||
maxIndex = Math.max(maxIndex, indexOf(value));
|
||||
}
|
||||
return levels.get(maxIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
this.eventType.setLevel(indexOf(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
private int indexOf(String value) {
|
||||
int index = levels.indexOf(value);
|
||||
return index < 0 ? 0 : index;
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2023, 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 jdk.jfr.internal.test;
|
||||
|
||||
public class DeprecatedMethods {
|
||||
public static int counter;
|
||||
|
||||
@Deprecated
|
||||
public static void deprecated() {
|
||||
counter++;
|
||||
}
|
||||
|
||||
@Deprecated(since = "0")
|
||||
public static void deprecatedSince() {
|
||||
counter++;
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public static void deprecatedForRemoval() {
|
||||
counter++;
|
||||
}
|
||||
|
||||
@Deprecated(since = "0", forRemoval = true)
|
||||
public static void deprecatedSinceForRemoval() {
|
||||
counter++;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static void reflectionDeprecated() {
|
||||
counter++;
|
||||
}
|
||||
|
||||
@Deprecated(since = "0")
|
||||
public static void reflectionDeprecatedSince() {
|
||||
counter++;
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public static void reflectionDeprecatedForRemoval() {
|
||||
counter++;
|
||||
}
|
||||
|
||||
@Deprecated(since = "0", forRemoval = true)
|
||||
public static void reflectionDeprecatedSinceForRemoval() {
|
||||
counter++;
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2023, 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 jdk.jfr.internal.test;
|
||||
|
||||
@Deprecated(since = "0")
|
||||
public class DeprecatedThing {
|
||||
public int counter;
|
||||
|
||||
public void foo() {
|
||||
bar();
|
||||
}
|
||||
|
||||
public void zoo() {
|
||||
System.out.println("Zoo invoked");
|
||||
for (int i = 0; i < 1_000_000; i++) {
|
||||
bar();
|
||||
}
|
||||
}
|
||||
|
||||
private void bar() {
|
||||
baz();
|
||||
}
|
||||
|
||||
public void baz() {
|
||||
inc();
|
||||
}
|
||||
|
||||
private void inc() {
|
||||
counter++;
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public void instanceDeprecatedForRemoval() {
|
||||
for (int i = 0; i < 1_000_000; i++) {
|
||||
inc();
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated(since = "0", forRemoval = true)
|
||||
public void instanceDeprecatedSinceForRemoval() {
|
||||
counter++;
|
||||
}
|
||||
}
|
@ -915,6 +915,12 @@
|
||||
<setting name="period">endChunk</setting>
|
||||
</event>
|
||||
|
||||
<event name="jdk.DeprecatedInvocation">
|
||||
<setting name="enabled">true</setting>
|
||||
<setting name="stackTrace">true</setting>
|
||||
<setting name="level">forRemoval</setting>
|
||||
</event>
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -915,6 +915,11 @@
|
||||
<setting name="period">endChunk</setting>
|
||||
</event>
|
||||
|
||||
<event name="jdk.DeprecatedInvocation">
|
||||
<setting name="enabled">true</setting>
|
||||
<setting name="stackTrace">true</setting>
|
||||
<setting name="level">forRemoval</setting>
|
||||
</event>
|
||||
|
||||
|
||||
|
||||
|
240
test/jdk/jdk/jfr/event/runtime/TestDeprecatedEvent.java
Normal file
240
test/jdk/jdk/jfr/event/runtime/TestDeprecatedEvent.java
Normal file
@ -0,0 +1,240 @@
|
||||
/*
|
||||
* Copyright (c) 2023, 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 jdk.jfr.event.runtime;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import jdk.jfr.consumer.RecordedMethod;
|
||||
import jdk.jfr.consumer.RecordedEvent;
|
||||
import jdk.jfr.consumer.RecordedStackTrace;
|
||||
import jdk.jfr.consumer.RecordedFrame;
|
||||
import jdk.jfr.internal.test.DeprecatedMethods;
|
||||
import jdk.jfr.internal.test.DeprecatedThing;
|
||||
import jdk.jfr.Recording;
|
||||
import jdk.test.lib.jfr.EventNames;
|
||||
import jdk.test.lib.jfr.Events;
|
||||
import static jdk.test.lib.Asserts.assertTrue;
|
||||
import static jdk.test.lib.Asserts.assertNull;
|
||||
import static jdk.test.lib.Asserts.assertNotNull;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @key jfr
|
||||
* @requires vm.hasJFR
|
||||
* @modules jdk.jfr/jdk.jfr.internal.test
|
||||
* @library /test/lib
|
||||
*
|
||||
* @run main/othervm/timeout=300 -XX:StartFlightRecording:settings=none,+jdk.DeprecatedInvocation#enabled=true
|
||||
* jdk.jfr.event.runtime.TestDeprecatedEvent Default
|
||||
|
||||
* @run main/othervm/timeout=300 -Xint -XX:+UseInterpreter -XX:StartFlightRecording:settings=none,+jdk.DeprecatedInvocation#enabled=true
|
||||
* jdk.jfr.event.runtime.TestDeprecatedEvent Interpreter
|
||||
*
|
||||
* @run main/othervm/timeout=300 -Xcomp -XX:-UseInterpreter -XX:StartFlightRecording:settings=none,+jdk.DeprecatedInvocation#enabled=true
|
||||
* jdk.jfr.event.runtime.TestDeprecatedEvent Compiler
|
||||
*
|
||||
* @run main/othervm/timeout=300 -Xcomp -XX:TieredStopAtLevel=1 -XX:-UseInterpreter -XX:StartFlightRecording:settings=none,+jdk.DeprecatedInvocation#enabled=true
|
||||
* jdk.jfr.event.runtime.TestDeprecatedEvent C1
|
||||
*
|
||||
* @run main/othervm/timeout=300 -Xcomp -XX:TieredStopAtLevel=4 -XX:-TieredCompilation -XX:-UseInterpreter -XX:StartFlightRecording:settings=none,+jdk.DeprecatedInvocation#enabled=true
|
||||
* jdk.jfr.event.runtime.TestDeprecatedEvent C2
|
||||
*
|
||||
*/
|
||||
public class TestDeprecatedEvent {
|
||||
/*
|
||||
*
|
||||
* @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI
|
||||
* jdk.jfr.event.runtime.TestDeprecatedEvent JVMCI
|
||||
*
|
||||
*/
|
||||
public static String EVENT_NAME = EventNames.DeprecatedInvocation;
|
||||
private static String mode;
|
||||
public static int counter;
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
mode = args[0];
|
||||
testDeprecatedLevelAll();
|
||||
testDeprecatedLevelAllRetained();
|
||||
testReflectionAll();
|
||||
testDeprecatedLevelForRemovalRetained();
|
||||
}
|
||||
|
||||
private static void testDeprecatedLevelAll() throws Exception {
|
||||
try (Recording r = new Recording()) {
|
||||
r.enable(EVENT_NAME).with("level", "all");
|
||||
r.start();
|
||||
testLevelAll();
|
||||
r.stop();
|
||||
validateLevelAll(r);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public static void userDeprecatedForRemoval() {
|
||||
counter++;
|
||||
}
|
||||
|
||||
private static void testLevelAll() throws Exception {
|
||||
// Methods individually decorated.
|
||||
DeprecatedMethods.deprecated();
|
||||
DeprecatedMethods.deprecatedSince();
|
||||
DeprecatedMethods.deprecatedForRemoval();
|
||||
DeprecatedMethods.deprecatedSinceForRemoval();
|
||||
// Class level @deprecated annotation
|
||||
// @Deprecated(since = "0")
|
||||
DeprecatedThing t = new DeprecatedThing();
|
||||
t.instanceDeprecatedForRemoval();
|
||||
t.instanceDeprecatedSinceForRemoval();
|
||||
t.foo();
|
||||
t.zoo();
|
||||
// Invoke a deprecated method in the users code
|
||||
// to verify the negative case, i.e. that this
|
||||
// invocation is not reported.
|
||||
userDeprecatedForRemoval();
|
||||
}
|
||||
|
||||
private static void validateLevelAll(Recording r) throws Exception {
|
||||
List<RecordedEvent> events = Events.fromRecording(r);
|
||||
printInvocations(events, "all");
|
||||
assertMethod(events, "testLevelAll", "deprecated");
|
||||
assertMethod(events, "testLevelAll", "deprecatedSince");
|
||||
assertMethod(events, "testLevelAll", "deprecatedForRemoval");
|
||||
assertMethod(events, "testLevelAll", "deprecatedSinceForRemoval");
|
||||
assertMethod(events, "testLevelAll", "instanceDeprecatedForRemoval");
|
||||
assertMethod(events, "testLevelAll", "instanceDeprecatedSinceForRemoval");
|
||||
assertMethod(events, "testLevelAll", "foo");
|
||||
assertMethod(events, "testLevelAll", "zoo");
|
||||
// Negative case
|
||||
try {
|
||||
assertMethod(events, "testLevelAll", "userDeprecatedForRemoval");
|
||||
throw new RuntimeException("Invocation of a deprecated method in user code should not be reported");
|
||||
} catch (Exception e) {
|
||||
// Expected
|
||||
}
|
||||
}
|
||||
|
||||
// Does not invoke any deprecated methods. We only verify
|
||||
// that all previously invoked methods are still retained
|
||||
// when starting and stopping a subsequent recording.
|
||||
private static void testDeprecatedLevelAllRetained() throws Exception {
|
||||
try (Recording r = new Recording()) {
|
||||
r.enable(EVENT_NAME).with("level", "all");
|
||||
r.start();
|
||||
r.stop();
|
||||
validateLevelAll(r);
|
||||
}
|
||||
}
|
||||
|
||||
private static void testReflectionAll() throws Exception {
|
||||
try (Recording r = new Recording()) {
|
||||
r.enable(EVENT_NAME).with("level", "all");
|
||||
r.start();
|
||||
DeprecatedMethods.class.getMethod("reflectionDeprecated").invoke(null);
|
||||
DeprecatedMethods.class.getMethod("reflectionDeprecatedSince").invoke(null);
|
||||
DeprecatedMethods.class.getMethod("reflectionDeprecatedForRemoval").invoke(null);
|
||||
DeprecatedMethods.class.getMethod("reflectionDeprecatedSinceForRemoval").invoke(null);
|
||||
r.stop();
|
||||
validateReflectionLevelAll(r);
|
||||
}
|
||||
}
|
||||
|
||||
private static void validateReflectionLevelAll(Recording r) throws Exception {
|
||||
List<RecordedEvent> events = Events.fromRecording(r);
|
||||
printInvocations(events, "reflectionAll");
|
||||
assertMethod(events, "testReflectionAll", "reflectionDeprecated");
|
||||
assertMethod(events, "testReflectionAll", "reflectionDeprecatedSince");
|
||||
assertMethod(events, "testReflectionAll", "reflectionDeprecatedForRemoval");
|
||||
assertMethod(events, "testReflectionAll", "reflectionDeprecatedSinceForRemoval");
|
||||
}
|
||||
|
||||
// Does not invoke any deprecated methods. We only verify
|
||||
// that all previously invoked methods are still retained
|
||||
// when starting and stopping a subsequent recording.
|
||||
private static void testDeprecatedLevelForRemovalRetained() throws Exception {
|
||||
try (Recording r = new Recording()) {
|
||||
r.enable(EVENT_NAME).with("level", "forRemoval");
|
||||
r.start();
|
||||
r.stop();
|
||||
validateLevelForRemoval(r);
|
||||
}
|
||||
}
|
||||
|
||||
private static void validateLevelForRemoval(Recording r) throws Exception {
|
||||
List<RecordedEvent> events = Events.fromRecording(r);
|
||||
printInvocations(events, "forRemoval");
|
||||
assertMethod(events, "testLevelAll", "deprecatedForRemoval");
|
||||
assertMethod(events, "testLevelAll", "deprecatedSinceForRemoval");
|
||||
assertMethod(events, "testLevelAll", "instanceDeprecatedForRemoval");
|
||||
assertMethod(events, "testLevelAll", "instanceDeprecatedSinceForRemoval");
|
||||
assertMethod(events, "testReflectionAll", "reflectionDeprecatedForRemoval");
|
||||
assertMethod(events, "testReflectionAll", "reflectionDeprecatedSinceForRemoval");
|
||||
}
|
||||
|
||||
private static void assertMethod(List<RecordedEvent> events, String caller, String method) throws Exception {
|
||||
for (RecordedEvent e : events) {
|
||||
RecordedMethod deprecatedMethod = e.getValue("method");
|
||||
boolean forRemoval = e.getValue("forRemoval");
|
||||
RecordedStackTrace stacktrace = e.getStackTrace();
|
||||
assertNotNull(stacktrace, "should have a stacktrace");
|
||||
assertTrue(stacktrace.isTruncated(), "invariant");
|
||||
List<RecordedFrame> frames = stacktrace.getFrames();
|
||||
assertTrue(frames.size() == 1, "invariant");
|
||||
assertTrue(frames.getFirst().isJavaFrame(), "invariant");
|
||||
RecordedFrame frame = frames.getFirst();
|
||||
assertTrue(frame.isJavaFrame(), "invariant");
|
||||
RecordedMethod callerMethod = frame.getMethod();
|
||||
assertNull(e.getThread(), "should not have a thread");
|
||||
if (forRemoval) {
|
||||
assertTrue(deprecatedMethod.getName().endsWith("ForRemoval"), "wrong filtering?");
|
||||
}
|
||||
if (deprecatedMethod.getName().equals(method) && callerMethod.getName().equals(caller)){
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new Exception("Could not find invocation: " + caller + " -> " + method);
|
||||
}
|
||||
|
||||
|
||||
private static void printInvocations(List<RecordedEvent> events, String all) {
|
||||
System.out.println("*** METHOD INVOCATION *** (" + mode + ") level = " + all + " count: " + events.size() + " ***\n");
|
||||
for (RecordedEvent e : events) {
|
||||
RecordedMethod deprecatedMethod = e.getValue("method");
|
||||
boolean forRemoval = e.getValue("forRemoval");
|
||||
RecordedStackTrace stacktrace = e.getStackTrace();
|
||||
assertNotNull(stacktrace, "should have a stacktrace");
|
||||
assertTrue(stacktrace.isTruncated(), "invariant");
|
||||
List<RecordedFrame> frames = stacktrace.getFrames();
|
||||
assertTrue(frames.size() == 1, "invariant");
|
||||
RecordedFrame frame = frames.getFirst();
|
||||
assertTrue(frame.isJavaFrame(), "invariant");
|
||||
RecordedMethod callerMethod = frame.getMethod();
|
||||
int bci = frame.getBytecodeIndex();
|
||||
int lineNumber = frame.getLineNumber();
|
||||
assertNull(e.getThread(), "should not have a thread");
|
||||
System.out.println(callerMethod.getName() + " at bci: " + bci + " line: " + lineNumber + " -> " + deprecatedMethod.getName());
|
||||
System.out.println(e);
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
}
|
@ -88,6 +88,7 @@ public class EventNames {
|
||||
public static final String NativeMemoryUsageTotal = PREFIX + "NativeMemoryUsageTotal";
|
||||
public static final String JavaAgent = PREFIX + "JavaAgent";
|
||||
public static final String NativeAgent = PREFIX + "NativeAgent";
|
||||
public static final String DeprecatedInvocation = PREFIX + "DeprecatedInvocation";
|
||||
|
||||
// This event is hard to test
|
||||
public static final String ReservedStackActivation = PREFIX + "ReservedStackActivation";
|
||||
|
Loading…
Reference in New Issue
Block a user