blob: 49b5e20e49a8b39466f93e829951120a072fda28 [file] [edit]
/*
* Copyright (c) 2023, 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 "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, JfrCheckpointWriter& tsw, bool stacktrace) :
_now(JfrTicks::now()),_cw(cw), _tsw(tsw), _for_removal(only_for_removal()), _stacktrace(stacktrace) {}
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());
}
static void write_type_set(const JfrDeprecatedEdge* edge, JfrCheckpointWriter& tsw) {
if (!edge->has_type_set()) {
return;
}
edge->type_set()->exclusive_write(tsw);
}
bool JfrDeprecatedEventWriter::process(const JfrDeprecatedEdge* edge) {
assert(edge != nullptr, "invariant");
if (_for_removal && !edge->for_removal()) {
return true;
}
write_event(edge, _cw, _now, _stacktrace);
write_type_set(edge, _tsw);
return true;
}
JfrDeprecatedEventClear::JfrDeprecatedEventClear() {}
bool JfrDeprecatedEventClear::process(const JfrDeprecatedEdge* edge) {
assert(edge != nullptr, "invariant");
if (!edge->has_type_set()) {
return true;
}
edge->type_set()->reset_write_state();
return true;
}