mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-05 13:40:18 +00:00
144 lines
5.6 KiB
C++
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(¶m_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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|