| /* |
| * 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; |
| } |
| |