Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/hotspot/share/c1/c1_Compilation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,8 @@ int Compilation::compile_java_method() {
env()->dump_replay_data(env()->compile_id());
}

DEBUG_ONLY(CompilationMemoryStatistic::do_test_allocations();)

{
PhaseTraceTime timeit(_t_codeemit);
return emit_code_body();
Expand Down
4 changes: 2 additions & 2 deletions src/hotspot/share/ci/ciEnv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ static bool firstEnv = true;
// ------------------------------------------------------------------
// ciEnv::ciEnv
ciEnv::ciEnv(CompileTask* task)
: _ciEnv_arena(mtCompiler) {
: _ciEnv_arena(mtCompiler, Arena::Tag::tag_cienv) {
VM_ENTRY_MARK;

// Set up ciEnv::current immediately, for the sake of ciObjectFactory, etc.
Expand Down Expand Up @@ -238,7 +238,7 @@ class RecordLocation {
}
};

ciEnv::ciEnv(Arena* arena) : _ciEnv_arena(mtCompiler) {
ciEnv::ciEnv(Arena* arena) : _ciEnv_arena(mtCompiler, Arena::Tag::tag_cienv) {
ASSERT_IN_VM;

// Set up ciEnv::current immediately, for the sake of ciObjectFactory, etc.
Expand Down
2 changes: 1 addition & 1 deletion src/hotspot/share/ci/ciObjectFactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ void ciObjectFactory::initialize() {
// This Arena is long lived and exists in the resource mark of the
// compiler thread that initializes the initial ciObjectFactory which
// creates the shared ciObjects that all later ciObjectFactories use.
Arena* arena = new (mtCompiler) Arena(mtCompiler);
Arena* arena = new (mtCompiler) Arena(mtCompiler, Arena::Tag::tag_cienv);
ciEnv initial(arena);
ciEnv* env = ciEnv::current();
env->_factory->init_shared_objects();
Expand Down
287 changes: 287 additions & 0 deletions src/hotspot/share/compiler/compilationMemStatInternals.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,287 @@
/*
* 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
Loading