Skip to content

Commit 07f5b76

Browse files
egahlinMarkus Grönlund
and
Markus Grönlund
committed
8352738: Implement JEP 520: JFR Method Timing and Tracing
Co-authored-by: Markus Grönlund <[email protected]> Reviewed-by: shade, mgronlun
1 parent d43f588 commit 07f5b76

File tree

107 files changed

+6712
-454
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

107 files changed

+6712
-454
lines changed

src/hotspot/share/classfile/modules.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "runtime/handles.hpp"
3030

3131
class ModuleEntryTable;
32+
class SerializeClosure;
3233
class Symbol;
3334

3435
class Modules : AllStatic {
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
/*
2+
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*
23+
*/
24+
25+
#include "classfile/classFileParser.hpp"
26+
#include "classfile/classFileStream.hpp"
27+
#include "classfile/classLoadInfo.hpp"
28+
#include "classfile/javaClasses.inline.hpp"
29+
#include "classfile/symbolTable.hpp"
30+
#include "jfr/instrumentation/jfrClassTransformer.hpp"
31+
#include "jfr/recorder/service/jfrOptionSet.hpp"
32+
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp"
33+
#include "logging/log.hpp"
34+
#include "memory/allocation.inline.hpp"
35+
#include "memory/resourceArea.hpp"
36+
#include "oops/instanceKlass.hpp"
37+
#include "oops/klass.inline.hpp"
38+
#include "prims/jvmtiRedefineClasses.hpp"
39+
#include "prims/jvmtiThreadState.hpp"
40+
#include "runtime/handles.inline.hpp"
41+
#include "runtime/javaThread.hpp"
42+
#include "utilities/exceptions.hpp"
43+
#include "utilities/globalDefinitions.hpp"
44+
#include "utilities/growableArray.hpp"
45+
#include "utilities/macros.hpp"
46+
47+
static void log_pending_exception(oop throwable) {
48+
assert(throwable != nullptr, "invariant");
49+
oop msg = java_lang_Throwable::message(throwable);
50+
if (msg != nullptr) {
51+
char* text = java_lang_String::as_utf8_string(msg);
52+
if (text != nullptr) {
53+
log_error(jfr, system) ("%s", text);
54+
}
55+
}
56+
}
57+
58+
// On initial class load.
59+
void JfrClassTransformer::cache_class_file_data(InstanceKlass* new_ik, const ClassFileStream* new_stream, const JavaThread* thread) {
60+
assert(new_ik != nullptr, "invariant");
61+
assert(new_stream != nullptr, "invariant");
62+
assert(thread != nullptr, "invariant");
63+
assert(!thread->has_pending_exception(), "invariant");
64+
if (!JfrOptionSet::allow_retransforms()) {
65+
return;
66+
}
67+
const jint stream_len = new_stream->length();
68+
JvmtiCachedClassFileData* p =
69+
(JvmtiCachedClassFileData*)NEW_C_HEAP_ARRAY_RETURN_NULL(u1, offset_of(JvmtiCachedClassFileData, data) + stream_len, mtInternal);
70+
if (p == nullptr) {
71+
log_error(jfr, system)("Allocation using C_HEAP_ARRAY for %zu bytes failed in JfrEventClassTransformer::cache_class_file_data",
72+
static_cast<size_t>(offset_of(JvmtiCachedClassFileData, data) + stream_len));
73+
return;
74+
}
75+
p->length = stream_len;
76+
memcpy(p->data, new_stream->buffer(), stream_len);
77+
new_ik->set_cached_class_file(p);
78+
}
79+
80+
InstanceKlass* JfrClassTransformer::create_instance_klass(InstanceKlass*& ik, ClassFileStream* stream, bool is_initial_load, JavaThread* thread) {
81+
if (stream == nullptr) {
82+
if (is_initial_load) {
83+
log_error(jfr, system)("JfrClassTransformer: unable to create ClassFileStream for %s", ik->external_name());
84+
}
85+
return nullptr;
86+
}
87+
InstanceKlass* const new_ik = create_new_instance_klass(ik, stream, thread);
88+
if (new_ik == nullptr) {
89+
if (is_initial_load) {
90+
log_error(jfr, system)("JfrClassTransformer: unable to create InstanceKlass for %s", ik->external_name());
91+
}
92+
}
93+
return new_ik;
94+
}
95+
96+
void JfrClassTransformer::copy_traceid(const InstanceKlass* ik, const InstanceKlass* new_ik) {
97+
assert(ik != nullptr, "invariant");
98+
assert(new_ik != nullptr, "invariant");
99+
new_ik->set_trace_id(ik->trace_id());
100+
assert(TRACE_ID(ik) == TRACE_ID(new_ik), "invariant");
101+
}
102+
103+
InstanceKlass* JfrClassTransformer::create_new_instance_klass(InstanceKlass* ik, ClassFileStream* stream, TRAPS) {
104+
assert(stream != nullptr, "invariant");
105+
ResourceMark rm(THREAD);
106+
ClassLoaderData* const cld = ik->class_loader_data();
107+
Handle pd(THREAD, ik->protection_domain());
108+
Symbol* const class_name = ik->name();
109+
ClassLoadInfo cl_info(pd);
110+
ClassFileParser new_parser(stream,
111+
class_name,
112+
cld,
113+
&cl_info,
114+
ClassFileParser::INTERNAL, // internal visibility
115+
THREAD);
116+
if (HAS_PENDING_EXCEPTION) {
117+
log_pending_exception(PENDING_EXCEPTION);
118+
CLEAR_PENDING_EXCEPTION;
119+
return nullptr;
120+
}
121+
const ClassInstanceInfo* cl_inst_info = cl_info.class_hidden_info_ptr();
122+
InstanceKlass* const new_ik = new_parser.create_instance_klass(false, *cl_inst_info, THREAD);
123+
if (HAS_PENDING_EXCEPTION) {
124+
log_pending_exception(PENDING_EXCEPTION);
125+
CLEAR_PENDING_EXCEPTION;
126+
return nullptr;
127+
}
128+
assert(new_ik != nullptr, "invariant");
129+
assert(new_ik->name() != nullptr, "invariant");
130+
assert(ik->name() == new_ik->name(), "invariant");
131+
return new_ik;
132+
}
133+
134+
// Redefining / retransforming?
135+
const Klass* JfrClassTransformer::find_existing_klass(const InstanceKlass* ik, JavaThread* thread) {
136+
assert(ik != nullptr, "invariant");
137+
assert(thread != nullptr, "invariant");
138+
JvmtiThreadState* const state = thread->jvmti_thread_state();
139+
return state != nullptr ? klass_being_redefined(ik, state) : nullptr;
140+
}
141+
142+
const Klass* JfrClassTransformer::klass_being_redefined(const InstanceKlass* ik, JvmtiThreadState* state) {
143+
assert(ik != nullptr, "invariant");
144+
assert(state != nullptr, "invariant");
145+
const GrowableArray<Klass*>* const redef_klasses = state->get_classes_being_redefined();
146+
if (redef_klasses == nullptr || redef_klasses->is_empty()) {
147+
return nullptr;
148+
}
149+
for (int i = 0; i < redef_klasses->length(); ++i) {
150+
const Klass* const existing_klass = redef_klasses->at(i);
151+
assert(existing_klass != nullptr, "invariant");
152+
if (ik->name() == existing_klass->name() && ik->class_loader_data() == existing_klass->class_loader_data()) {
153+
// 'ik' is a scratch klass. Return the klass being redefined.
154+
return existing_klass;
155+
}
156+
}
157+
return nullptr;
158+
}
159+
160+
// On redefine / retransform, in case an agent modified the class, the original bytes are cached onto the scratch klass.
161+
void JfrClassTransformer::transfer_cached_class_file_data(InstanceKlass* ik, InstanceKlass* new_ik, const ClassFileParser& parser, JavaThread* thread) {
162+
assert(ik != nullptr, "invariant");
163+
assert(new_ik != nullptr, "invariant");
164+
JvmtiCachedClassFileData* const p = ik->get_cached_class_file();
165+
if (p != nullptr) {
166+
new_ik->set_cached_class_file(p);
167+
ik->set_cached_class_file(nullptr);
168+
return;
169+
}
170+
// No cached classfile indicates that no agent modified the klass.
171+
// This means that the parser is holding the original bytes. Hence, we cache it onto the scratch klass.
172+
const ClassFileStream* const stream = parser.clone_stream();
173+
cache_class_file_data(new_ik, stream, thread);
174+
}
175+
176+
void JfrClassTransformer::rewrite_klass_pointer(InstanceKlass*& ik, InstanceKlass* new_ik, ClassFileParser& parser, const JavaThread* thread) {
177+
assert(ik != nullptr, "invariant");
178+
assert(new_ik != nullptr, "invariant");
179+
assert(thread != nullptr, "invariant");
180+
assert(TRACE_ID(ik) == TRACE_ID(new_ik), "invariant");
181+
assert(!thread->has_pending_exception(), "invariant");
182+
// Assign original InstanceKlass* back onto "its" parser object for proper destruction.
183+
parser.set_klass_to_deallocate(ik);
184+
// Finally rewrite the original pointer to the newly created InstanceKlass.
185+
ik = new_ik;
186+
}
187+
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*
23+
*/
24+
25+
#ifndef SHARE_JFR_INSTRUMENTATION_JFRCLASSTRANSFORMER_HPP
26+
#define SHARE_JFR_INSTRUMENTATION_JFRCLASSTRANSFORMER_HPP
27+
28+
#include "memory/allStatic.hpp"
29+
#include "utilities/exceptions.hpp"
30+
31+
class ClassFileParser;
32+
class ClassFileStream;
33+
class InstanceKlass;
34+
35+
/*
36+
* Contains common functionality used by method and event instrumentation.
37+
*/
38+
class JfrClassTransformer : AllStatic {
39+
private:
40+
static InstanceKlass* create_new_instance_klass(InstanceKlass* ik, ClassFileStream* stream, TRAPS);
41+
static const Klass* klass_being_redefined(const InstanceKlass* ik, JvmtiThreadState* state);
42+
43+
public:
44+
static const Klass* find_existing_klass(const InstanceKlass* ik, JavaThread* thread);
45+
static InstanceKlass* create_instance_klass(InstanceKlass*& ik, ClassFileStream* stream, bool is_initial_load, JavaThread* thread);
46+
static void copy_traceid(const InstanceKlass* ik, const InstanceKlass* new_ik);
47+
static void transfer_cached_class_file_data(InstanceKlass* ik, InstanceKlass* new_ik, const ClassFileParser& parser, JavaThread* thread);
48+
static void rewrite_klass_pointer(InstanceKlass*& ik, InstanceKlass* new_ik, ClassFileParser& parser, const JavaThread* thread);
49+
static void cache_class_file_data(InstanceKlass* new_ik, const ClassFileStream* new_stream, const JavaThread* thread);
50+
};
51+
52+
#endif // SHARE_JFR_INSTRUMENTATION_JFRCLASSTRANSFORMER_HPP

0 commit comments

Comments
 (0)