jdk/src/hotspot/share/jfr/support/methodtracer/jfrMethodProcessor.cpp
Markus Grönlund 9fe2aa59ff 8356587: Missing object ID X in pool jdk.types.Method
Reviewed-by: egahlin
Backport-of: a34994476e8f4783c9f5a83a9c3db63ad605b323
2025-07-29 11:40:55 +00:00

144 lines
5.6 KiB
C++

/*
* Copyright (c) 2025, 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 "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp"
#include "jfr/support/methodtracer/jfrFilter.hpp"
#include "jfr/support/methodtracer/jfrFilterManager.hpp"
#include "jfr/support/methodtracer/jfrMethodProcessor.hpp"
#include "jfr/utilities/jfrTypes.hpp"
#include "logging/log.hpp"
#include "oops/instanceKlass.hpp"
#include "runtime/thread.inline.hpp"
#include "utilities/growableArray.hpp"
JfrMethodProcessor::JfrMethodProcessor(const InstanceKlass* ik, Thread* thread) :
_klass(ik),
_methods(nullptr),
_thread(thread),
_has_timing(false),
_log(log_is_enabled(Debug, jfr, methodtrace)) {
assert(ik != nullptr, "invariant");
assert(Thread::current() == thread, "invariant");
process();
}
JfrMethodProcessor::~JfrMethodProcessor() {
assert(_thread != nullptr, "invariant");
if (_methods != nullptr) {
// Removal of pushed metadata keep-alive entries.
for (int i = 0; i < _methods->length(); ++i) {
Method* const method = const_cast<Method*>(_methods->at(i).method());
if (method != nullptr) {
const int idx = _thread->metadata_handles()->find_from_end(method);
assert(idx >= 0, "invariant");
_thread->metadata_handles()->remove_at(idx);
}
}
}
}
void JfrMethodProcessor::update_methods(const InstanceKlass* ik) {
assert(ik != nullptr, "invariant");
assert(_methods != nullptr, "invariant");
const Array<Method*>* const ik_methods = ik->methods();
assert(ik_methods != nullptr, "invariant");
for (int i = 0; i < _methods->length(); ++i) {
const uint32_t idx = _methods->at(i).methods_array_index();
Method* const method = ik_methods->at(idx);
assert(method != nullptr, "invariant");
assert(method->name() == _methods->at(i).name(), "invariant");
assert(method->signature() == _methods->at(i).signature(), "invariant");
_methods->at(i).set_method(method);
// This is to keep the method from being unloaded during redefine / retransform.
// Equivalent functionality to that provided by the methodHandle. Unfortunately,
// we cannot use that directly because our handles would reside not on the stack
// but in an Arena managed by a thread-local ResourceArea, which is not allowed.
// Removal of pushed metadata entries happens in the destructor.
_thread->metadata_handles()->push(method);
}
}
void JfrMethodProcessor::set_timing(int modification) {
if (_has_timing) {
return;
}
if (modification > 0 && (modification & 1)) {
_has_timing = true;
}
}
static void log(const Method* method, traceid id, int new_modification) {
assert(method != nullptr, "invariant");
const char* timing = JfrFilter::is_timing(new_modification) ? "+timing" : "-timing";
const char* tracing = JfrFilter::is_tracing(new_modification) ? "+tracing" : "-tracing";
stringStream param_stream;
method->signature()->print_as_signature_external_parameters(&param_stream);
const char* param_string = param_stream.as_string();
stringStream ss;
ss.print("%s", method->method_holder()->external_name());
ss.print("::");
ss.print("%s", method->name()->as_C_string());
ss.print("(");
if (strlen(param_string) < 30) {
ss.print("%s", param_string);
} else {
ss.print("...");
}
ss.print(")");
log_debug(jfr, methodtrace)("Modify bytecode for %s %s %s (Method ID: " UINT64_FORMAT_X ")", ss.as_string(), timing, tracing, id);
}
void JfrMethodProcessor::process() {
const JfrFilter* const filter = JfrFilterManager::current();
assert(filter != nullptr, "invariant");
if (!filter->can_instrument_class(_klass)) {
return;
}
const int class_modifications = filter->class_modifications(_klass, false);
const Array<Method*>* const methods = _klass->methods();
const int method_count = methods->length();
for (int i = 0; i < method_count; i++) {
const Method* const m = methods->at(i);
assert(m != nullptr, "invariant");
if (filter->can_instrument_method(m)) {
const int new_modification = JfrFilter::combine_bits(class_modifications, filter->method_modifications(m));
if (new_modification != JfrFilter::NONE || JfrTraceId::has_sticky_bit(m)) {
// Allocate lazy, most classes will not match a filter
if (_methods == nullptr) {
_methods = new GrowableArray<JfrTracedMethod>();
}
set_timing(new_modification);
const int modification = new_modification == JfrFilter::NONE ? 0 : new_modification;
JfrTracedMethod traced_method(_klass, m, modification, i);
_methods->append(traced_method);
if (_log) {
log(m, traced_method.id(), modification);
}
}
}
}
}