| /* |
| * Copyright (c) 2024, 2025, Red Hat, Inc. and/or its affiliates. |
| * Copyright (c) 2024, 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. |
| * |
| */ |
| |
| #ifndef SHARE_COMPILER_COMPILATIONMEMSTATINTERNALS_HPP |
| #define SHARE_COMPILER_COMPILATIONMEMSTATINTERNALS_HPP |
| |
| #include "compiler/compilationMemoryStatistic.hpp" |
| #include "compiler/compilerDefinitions.hpp" |
| #include "memory/allocation.hpp" |
| #include "memory/arena.hpp" |
| #include "utilities/globalDefinitions.hpp" |
| |
| #ifdef COMPILER2 |
| #include "opto/phase.hpp" |
| #endif |
| |
| class CompileTask; |
| class Method; |
| class Symbol; |
| class outputStream; |
| |
| #ifdef COMPILER2 |
| constexpr int phase_trc_id_max = (int)Phase::PhaseTraceId::max_phase_timers; |
| constexpr int phase_trc_id_none = (int)Phase::PhaseTraceId::_t_none; |
| #else |
| // In minimal builds, the ArenaCounterTable is just a single-dimension vector of arena tags (see below) |
| constexpr int phase_trc_id_max = 1; |
| constexpr int phase_trc_id_none = 0; |
| #endif |
| inline void check_phase_trace_id(int v) { assert(v >= 0 && v < phase_trc_id_max, "OOB (%d)", v); } |
| |
| constexpr int arena_tag_max = (int)Arena::Tag::tag_count; |
| inline void check_arena_tag(int v) { assert(v >= 0 && v < arena_tag_max, "OOB (%d)", v); } |
| |
| // A two-dimensional table, containing byte counters per arena type and |
| // per compilation phase. |
| class ArenaCounterTable { |
| size_t _v[phase_trc_id_max][arena_tag_max]; |
| public: |
| ArenaCounterTable(); |
| void copy_from(const ArenaCounterTable& other); |
| inline size_t at(int phase_trc_id, int arena_tag) const; |
| inline void add(size_t size, int phase_trc_id, int arena_tag); |
| inline void sub(size_t size, int phase_trc_id, int arena_tag); |
| void print_on(outputStream* ss) const; |
| void summarize(size_t out[arena_tag_max]) const; |
| }; |
| |
| struct PhaseInfo { |
| int id, num; |
| const char* text; |
| }; |
| |
| // A stack keeping track of the current compilation phase. Fixed-width for simplicity |
| // (we should never go beyond 5 or so in depth). |
| class PhaseInfoStack { |
| static constexpr int max_depth = 16; |
| int _depth; |
| PhaseInfo _stack[max_depth]; |
| public: |
| inline PhaseInfoStack(); |
| inline bool empty() const { return _depth == 0; } |
| inline void push(PhaseInfo info); |
| inline void pop(); |
| inline const PhaseInfo& top() const; |
| inline int depth() const { return _depth; } |
| }; |
| |
| // A very simple fixed-width FIFO buffer, used for the phase timeline |
| template <typename T, int size> |
| class SimpleFifo { |
| STATIC_ASSERT((size * 2) < INT_MAX); |
| T _v[size]; |
| int _pos; |
| int _oldest; |
| uint64_t _lost; |
| |
| int current_pos() const { return _pos; } |
| static int pos_to_index(int pos) { return pos % size; } |
| T& at(int pos) { return *(_v + pos_to_index(pos)); } |
| |
| public: |
| SimpleFifo() : _pos(0), _oldest(0), _lost(0UL) {} |
| T& current() { return at(current_pos()); } |
| T& last() { assert(!empty(), "sanity"); return at(current_pos() - 1); } |
| bool empty() const { return _pos == _oldest; } |
| uint64_t lost() const { return _lost; } |
| |
| void advance() { |
| _pos ++; |
| if (_pos >= size) { |
| _oldest ++; |
| _lost ++; |
| } |
| if (_pos == INT_MAX) { |
| _pos -= size; |
| _oldest -= size; |
| } |
| } |
| |
| void revert() { |
| assert(!empty(), "sanity"); |
| _pos--; |
| } |
| |
| template<typename F> |
| void iterate_all(F f) const { |
| for (int i = _oldest; i < _pos; i++) { |
| const int index = pos_to_index(i); |
| f(_v[index]); |
| } |
| } |
| |
| void copy_from(const SimpleFifo& other) { |
| memcpy(_v, other._v, sizeof(_v)); |
| _pos = other._pos; |
| _lost = other._lost; |
| _oldest = other._oldest; |
| } |
| }; |
| |
| // Holds a table of n entries; each entry keeping start->end footprints when |
| // a phase started and ended; each entry also keeping the phase-local peak (if |
| // a phase caused a temporary spike in footprint that vanished before the phase |
| // ended). |
| // Handling nested phases: for this structure, there is always a phase active; |
| // if a phase ends, we "restart" the parent phase (which often is the |
| // "outside any phase" phase). |
| class FootprintTimeline { |
| public: |
| static constexpr unsigned max_num_phases = 256; // beyond that we wrap, keeping just the last n phases |
| private: |
| template <typename T, typename dT> |
| struct C { |
| T start, peak, cur; |
| void init(T v) { start = cur = peak = v; } |
| void update(T v) { cur = v; if (v > peak) peak = v; } |
| dT end_delta() const { return (dT)cur - (dT)start; } |
| // Returns the peak size during this phase: how high usage rose above either |
| // start or end of phase. The background is that we want to know the max. memory |
| // consumption during this phase, but that may not be reflected by the start or the |
| // end counters if an Arena was created during the phase and only lived temporarily. |
| size_t temporary_peak_size() const { return MIN2(peak - cur, peak - start); } |
| }; |
| struct Entry { |
| PhaseInfo info; |
| int level; |
| C<size_t, ssize_t> _bytes; |
| C<unsigned, signed int> _live_nodes; |
| }; |
| SimpleFifo<Entry, max_num_phases> _fifo; |
| DEBUG_ONLY(bool _inbetween_phases;) |
| public: |
| FootprintTimeline(); |
| void copy_from(const FootprintTimeline& other); |
| inline void on_footprint_change(size_t cur_abs, unsigned cur_nodes); |
| void on_phase_end(size_t cur_abs, unsigned cur_nodes); |
| void on_phase_start(PhaseInfo info, size_t cur_abs, unsigned cur_nodes, int level); |
| void print_on(outputStream* st) const; |
| }; |
| |
| // We keep the name of the involved symbols in Symbol (made permanent) instead of resolving them to string and |
| // storing those. That significantly reduces footprint for the result store and delays resolving until printing |
| // time, which may be never. |
| class FullMethodName { |
| Symbol* _k; |
| Symbol* _m; |
| Symbol* _s; |
| public: |
| FullMethodName(); |
| FullMethodName(const Method* m); |
| FullMethodName(const FullMethodName& o); |
| FullMethodName& operator=(const FullMethodName& o); |
| void make_permanent(); |
| void print_on(outputStream* st) const; |
| char* as_C_string(char* buf, size_t len) const; |
| bool operator== (const FullMethodName& b) const; |
| DEBUG_ONLY(bool is_test_class() const;) |
| }; |
| |
| // ArenaState is the central data structure holding all statistics and temp data during |
| // a single compilation. It is created on demand (if memstat is active) and tied to the |
| // CompilerThread. |
| class ArenaStatCounter : public CHeapObj<mtCompiler> { |
| |
| FullMethodName _fmn; |
| |
| // from directives |
| const bool _should_print_memstat; |
| const bool _should_crash_on_memlimit; |
| |
| // Bytes total now |
| size_t _current; |
| // Bytes total at last global peak |
| size_t _peak; |
| // Bytes per arena/phase, now |
| ArenaCounterTable _counters_current; |
| // Bytes per arena/phase when we last reached the global peak |
| ArenaCounterTable _counters_at_global_peak; |
| |
| // Number of live nodes now (C2 only) |
| unsigned _live_nodes_current; |
| // Number of live nodes at global peak (C2 only) |
| unsigned _live_nodes_at_global_peak; |
| |
| // MemLimit handling |
| const size_t _limit; |
| bool _hit_limit; |
| bool _limit_in_process; |
| |
| // Keep track of current C2 phase |
| int _phase_counter; |
| PhaseInfoStack _phase_info_stack; |
| |
| // Keep track of C2 phase allocations over time |
| FootprintTimeline _timeline; |
| |
| const CompilerType _comp_type; |
| const int _comp_id; |
| |
| DEBUG_ONLY(bool _is_test_class;) |
| |
| int retrieve_live_node_count() const; |
| |
| public: |
| ArenaStatCounter(const CompileTask* task, size_t limit); |
| |
| void on_phase_start(PhaseInfo info); |
| void on_phase_end(); |
| |
| // Account an arena allocation. Returns true if new peak reached. |
| bool on_arena_chunk_allocation(size_t size, int arena_tag, uint64_t* stamp); |
| |
| // Account an arena deallocation. |
| void on_arena_chunk_deallocation(size_t size, uint64_t stamp); |
| |
| void print_peak_state_on(outputStream* st) const; |
| void print_error_state_on(outputStream* st) const; |
| |
| size_t limit() const { return _limit; } |
| bool hit_limit() const { return _hit_limit; } |
| bool limit_in_process() const { return _limit_in_process; } |
| void set_limit_in_process(bool v) { _limit_in_process = v; } |
| |
| const FullMethodName& fmn() const { return _fmn; } |
| bool should_print_memstat() { return _should_print_memstat; }; |
| bool should_crash_on_memlimit() const { return _should_crash_on_memlimit; }; |
| |
| CompilerType comp_type() const { return _comp_type; } |
| int comp_id() const { return _comp_id; } |
| DEBUG_ONLY(bool is_test_class() const { return _is_test_class; }) |
| |
| // Bytes total at last global peak |
| size_t peak() const { return _peak; } |
| |
| // Bytes per arena/phase when we last reached the global peak |
| const ArenaCounterTable& counters_at_global_peak() const { return _counters_at_global_peak; } |
| const FootprintTimeline& timeline() const { return _timeline; } |
| // Number of live nodes at global peak (C2 only) |
| unsigned live_nodes_at_global_peak() const { return _live_nodes_at_global_peak; } |
| |
| int advance_phase_counter() { return ++_phase_counter; } |
| }; |
| |
| #endif // SHARE_COMPILER_COMPILATIONMEMSTATINTERNALS_HPP |