Skip to content

Commit

Permalink
Store vitals samples at extremas for some sampled values
Browse files Browse the repository at this point in the history
  • Loading branch information
schmelter-sap committed Jul 7, 2023
1 parent 560d6bf commit f42b72c
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 39 deletions.
6 changes: 3 additions & 3 deletions src/hotspot/os/linux/vitals_linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ bool platform_columns_initialize() {
// syst-avail depends on kernel version.
g_show_system_memavail = OSWrapper::syst_avail() != INVALID_VALUE;
g_col_system_memavail =
define_column<MemorySizeColumn>(system_cat, NULL, "avail", "Memory available without swapping [host] [krn]", g_show_system_memavail);
define_column<MemorySizeColumn>(system_cat, NULL, "avail", "Memory available without swapping [host] [krn]", g_show_system_memavail, MIN);
g_col_system_memcommitted =
define_column<MemorySizeColumn>(system_cat, NULL, "comm", "Committed memory [host]", true);
g_col_system_memcommitted_ratio =
Expand Down Expand Up @@ -197,9 +197,9 @@ bool platform_columns_initialize() {
// (which should come out as the same, but you never know
g_show_cgroup_info = OSContainer::is_containerized() || (OSWrapper::syst_cgro_lim() != INVALID_VALUE || OSWrapper::syst_cgro_limsw() != INVALID_VALUE);
g_col_system_cgrp_limit_in_bytes =
define_column<MemorySizeColumn>(system_cat, "cgroup", "lim", "cgroup memory limit [cgrp]", g_show_cgroup_info);
define_column<MemorySizeColumn>(system_cat, "cgroup", "lim", "cgroup memory limit [cgrp]", g_show_cgroup_info, MIN);
g_col_system_cgrp_soft_limit_in_bytes =
define_column<MemorySizeColumn>(system_cat, "cgroup", "slim", "cgroup memory soft limit [cgrp]", g_show_cgroup_info);
define_column<MemorySizeColumn>(system_cat, "cgroup", "slim", "cgroup memory soft limit [cgrp]", g_show_cgroup_info, MIN);
g_col_system_cgrp_usage_in_bytes =
define_column<MemorySizeColumn>(system_cat, "cgroup", "usg", "cgroup memory usage [cgrp]", g_show_cgroup_info);
g_col_system_cgrp_kmem_usage_in_bytes =
Expand Down
4 changes: 2 additions & 2 deletions src/hotspot/os/windows/vitals_windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ static Column* g_col_process_commit_charge = NULL;

bool platform_columns_initialize() {
g_col_system_memoryload =
define_column<PlainValueColumn>("system", NULL, "mload", "Approximate percentage of physical memory that is in use.", true);
define_column<PlainValueColumn>("system", NULL, "mload", "Approximate percentage of physical memory that is in use.", true, MAX);

// MEMORYSTATUSEX ullAvailPhys
g_col_system_avail_phys =
define_column<MemorySizeColumn>("system", NULL, "avail-phys", "Amount of physical memory currently available.", true);
define_column<MemorySizeColumn>("system", NULL, "avail-phys", "Amount of physical memory currently available.", true, MIN);
// PROCESS_MEMORY_COUNTERS_EX WorkingSetSize
g_col_process_working_set_size =
define_column<MemorySizeColumn>("system", NULL, "wset", "Working set size", true);
Expand Down
4 changes: 4 additions & 0 deletions src/hotspot/share/runtime/globals.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,10 @@ const int ObjectAlignmentInBytes = 8;
product(bool, PrintVitalsAtExit, false, \
"Prints vitals at VM exit to tty.") \
\
product(bool, StorePeakSamples, true, \
"If enabled we store the samples for peak values of selected " \
"types.") \
\
product(ccstr, VitalsFile, NULL, \
"When DumpVitalsAtExit is set, the file name prefix for the " \
"output files (default is sapmachine_vitals_<pid>).") \
Expand Down
91 changes: 70 additions & 21 deletions src/hotspot/share/vitals/vitals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ Legend::Legend() : _last_added_cat(NULL) {}
void Legend::add_column_info(const char* const category, const char* const header,
const char* const name, const char* const description) {
// Print category label if this column opens a new category
if (_last_added_cat != category) {
if ((_last_added_cat == NULL) || (::strcmp(_last_added_cat, category) != 0)) {
print_text_with_dashes(&_legend, category, 30);
_legend.cr();
}
Expand Down Expand Up @@ -317,7 +317,7 @@ void ColumnList::add_column(Column* c) {

////////////////////

static void print_category_line(outputStream* st, const ColumnWidths* widths, const print_info_t* pi) {
static void print_category_line(outputStream* st, const ColumnWidths* widths, const print_info_t* pi, int add_width) {

assert(pi->csv == false, "Not in csv mode");
ostream_put_n(st, ' ', TIMESTAMP_LEN + TIMESTAMP_DIVIDER_LEN);
Expand All @@ -336,7 +336,7 @@ static void print_category_line(outputStream* st, const ColumnWidths* widths, co
}
width = 0;
}
width += widths->at(c->index());
width += widths->at(c->index()) + add_width;
width += 1; // divider between columns
last_category_text = c->category();
c = c->next();
Expand All @@ -345,7 +345,7 @@ static void print_category_line(outputStream* st, const ColumnWidths* widths, co
st->cr();
}

static void print_header_line(outputStream* st, const ColumnWidths* widths, const print_info_t* pi) {
static void print_header_line(outputStream* st, const ColumnWidths* widths, const print_info_t* pi, int add_width) {

assert(pi->csv == false, "Not in csv mode");
ostream_put_n(st, ' ', TIMESTAMP_LEN + TIMESTAMP_DIVIDER_LEN);
Expand All @@ -369,7 +369,7 @@ static void print_header_line(outputStream* st, const ColumnWidths* widths, cons
}
width = 0;
}
width += widths->at(c->index());
width += widths->at(c->index()) + add_width;
width += 1; // divider between columns
last_header_text = c->header();
c = c->next();
Expand All @@ -380,7 +380,7 @@ static void print_header_line(outputStream* st, const ColumnWidths* widths, cons
st->cr();
}

static void print_column_names(outputStream* st, const ColumnWidths* widths, const print_info_t* pi) {
static void print_column_names(outputStream* st, const ColumnWidths* widths, const print_info_t* pi, int add_width) {

// Leave space for timestamp column
if (pi->csv == false) {
Expand All @@ -393,7 +393,7 @@ static void print_column_names(outputStream* st, const ColumnWidths* widths, con
const Column* previous = NULL;
while (c != NULL) {
if (pi->csv == false) {
st->print("%-*s ", widths->at(c->index()), c->name());
st->print("%-*s ", widths->at(c->index()) + add_width, c->name());
} else { // csv mode
// csv: use comma as delimiter, don't pad, and precede name with category/header
// (limited to 4 chars).
Expand Down Expand Up @@ -487,11 +487,12 @@ static int print_memory_size(outputStream* st, size_t byte_size, size_t scale)

///////// class Column and childs ///////////

Column::Column(const char* category, const char* header, const char* name, const char* description)
Column::Column(const char* category, const char* header, const char* name, const char* description, Extremum extremum)
: _category(category),
_header(header), // may be NULL
_name(name),
_description(description),
_extremum(extremum),
_next(NULL), _idx(-1),
_idx_cat(-1), _idx_hdr(-1)
{}
Expand Down Expand Up @@ -578,7 +579,7 @@ int DeltaMemorySizeColumn::do_print0(outputStream* st, value_t value,

// Print one sample.
static void print_one_sample(outputStream* st, const Sample* sample,
const Sample* last_sample, const ColumnWidths* widths, const print_info_t* pi) {
const Sample* last_sample, const ColumnWidths* widths, const print_info_t* pi, int marked_index = -1, char mark = '*') {

// Print timestamp and divider
if (pi->csv) {
Expand Down Expand Up @@ -607,6 +608,9 @@ static void print_one_sample(outputStream* st, const Sample* sample,
}
const int min_width = widths->at(idx);
c->print_value(st, v, v2, age, min_width, pi);
if (marked_index >= 0) {
st->put(marked_index == idx ? mark : ' ');
}
st->put(pi->csv ? ',' : ' ');
c = c->next();
}
Expand Down Expand Up @@ -754,7 +758,7 @@ class PrintSamplesClosure : public SampleTable::Closure {

// sampleTables is a combination of two tables: a short term table and a long term table.
// It takes care to feed new samples into these tables at the appropriate intervals.
class SampleTables: public CHeapObj<mtInternal> {
class SampleTables : public CHeapObj<mtInternal> {

// Short term table: cover one hour, one sample per VitalsSampleInterval (default 10 seconds)
static const int short_term_span_seconds = 3600;
Expand All @@ -763,11 +767,12 @@ class SampleTables: public CHeapObj<mtInternal> {
static const int long_term_span_seconds = short_term_span_seconds * 24 * 14;
static const int long_term_sample_interval = short_term_span_seconds;

static int short_term_tablesize() { return (short_term_span_seconds / VitalsSampleInterval) + 1; }
static int short_term_tablesize() { return (short_term_span_seconds / VitalsSampleInterval) + 1; }
static const int long_term_tablesize = (long_term_span_seconds / long_term_sample_interval) + 1;

SampleTable _short_term_table;
SampleTable _long_term_table;
Sample* _extremum_samples;

int _count;

Expand All @@ -776,7 +781,7 @@ class SampleTables: public CHeapObj<mtInternal> {
char _temp_buffer[196 * K];

static void print_table(const SampleTable* table, outputStream* st,
const ColumnWidths* widths, const print_info_t* pi) {
const ColumnWidths* widths, const print_info_t* pi) {
if (table->is_empty()) {
st->print_cr("(no samples)");
return;
Expand All @@ -785,12 +790,12 @@ class SampleTables: public CHeapObj<mtInternal> {
table->walk_table_locked(&prclos, !pi->reverse_ordering);
}

static void print_headers(outputStream* st, const ColumnWidths* widths, const print_info_t* pi) {
static void print_headers(outputStream* st, const ColumnWidths* widths, const print_info_t* pi, int add_width = 0) {
if (pi->csv == false) {
print_category_line(st, widths, pi);
print_header_line(st, widths, pi);
print_category_line(st, widths, pi, add_width);
print_header_line(st, widths, pi, add_width);
}
print_column_names(st, widths, pi);
print_column_names(st, widths, pi, add_width);
}

// Helper, print a time span given in seconds-
Expand All @@ -800,11 +805,14 @@ class SampleTables: public CHeapObj<mtInternal> {
const int days = secs / (60 * 60 * 24);
if (days > 1) {
st->print_cr("Last %d days:", days);
} else if (hrs > 1) {
}
else if (hrs > 1) {
st->print_cr("Last %d hours:", hrs);
} else if (mins > 1) {
}
else if (mins > 1) {
st->print_cr("Last %d minutes:", mins);
} else {
}
else {
st->print_cr("Last %d seconds:", secs);
}
}
Expand All @@ -813,8 +821,9 @@ class SampleTables: public CHeapObj<mtInternal> {

SampleTables()
: _short_term_table(short_term_tablesize()),
_long_term_table(long_term_tablesize),
_count(0)
_long_term_table(long_term_tablesize),
_extremum_samples(NULL),
_count(0)
{}

void add_sample(const Sample* sample) {
Expand All @@ -829,6 +838,34 @@ class SampleTables: public CHeapObj<mtInternal> {
if ((_count % (long_term_sample_interval / VitalsSampleInterval)) == 0) {
_long_term_table.add_sample(sample);
}

// Update peak samples if needed.
if (StorePeakSamples) {
if (_extremum_samples == NULL) {
// For the first sample just initialize with the sample for all values we store peaks for.
_extremum_samples = (Sample*)NEW_C_HEAP_ARRAY(char, Sample::size_in_bytes() * Sample::num_values(), mtInternal);

for (int i = 0; i < Sample::num_values(); ++i) {
::memcpy(i * Sample::size_in_bytes() + (char*) _extremum_samples, sample, Sample::size_in_bytes());
}
}
else {
// Otherwise iterate columns and update if needed.
for (Column const* column = ColumnList::the_list()->first(); column != NULL; column = column->next()) {
if (column->extremum() != NONE) {
int idx = column->index();
Sample* extremum_sample = (Sample*)(idx * Sample::size_in_bytes() + (char*) _extremum_samples);

bool should_log = (column->extremum() == MAX) && (sample->value(idx) > extremum_sample->value(idx));
should_log |= (column->extremum() == MIN) && (sample->value(idx) < extremum_sample->value(idx));

if (should_log) {
::memcpy(extremum_sample, sample, Sample::size_in_bytes());
}
}
}
}
}
}

void print_all(outputStream* external_stream, const print_info_t* pi, const Sample* sample_now) {
Expand Down Expand Up @@ -874,6 +911,18 @@ class SampleTables: public CHeapObj<mtInternal> {
st->cr();
}

if (StorePeakSamples) {
st->print_cr("Samples at extremes (+ marks a maximum, - marks a minimum)");
print_headers(st, &widths, pi, 1); // Need more space for the mark to display.

for (Column const* column = ColumnList::the_list()->first(); column != NULL; column = column->next()) {
if (column->extremum() != NONE) {
Sample* peak_sample = (Sample*) (column->index() * Sample::size_in_bytes() + (char*) _extremum_samples);
print_one_sample(st, peak_sample, NULL, &widths, pi, column->index(), column->extremum() == MIN ? '-' : '+');
}
}
}

st->cr();

} // lock end
Expand Down
36 changes: 23 additions & 13 deletions src/hotspot/share/vitals/vitals_internals.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,20 @@ namespace sapmachine_vitals {

class ColumnList;

enum Extremum {
NONE,
MAX,
MIN
};

class Column: public CHeapObj<mtInternal> {
friend class ColumnList;

const char* const _category;
const char* const _header; // optional. May be NULL.
const char* const _name;
const char* const _description;
const Extremum _extremum;

// The following members are fixed by ColumnList when the Column is added to it.
Column* _next; // next column in table
Expand All @@ -80,7 +87,7 @@ namespace sapmachine_vitals {

protected:

Column(const char* category, const char* header, const char* name, const char* description);
Column(const char* category, const char* header, const char* name, const char* description, Extremum extremum);

// Child classes implement this.
// output stream can be NULL; in that case, method shall return number of characters it would have printed.
Expand All @@ -93,6 +100,7 @@ namespace sapmachine_vitals {
const char* header() const { return _header; }
const char* name() const { return _name; }
const char* description() const { return _description; }
Extremum extremum() const { return _extremum; }

void print_value(outputStream* os, value_t value, value_t last_value,
int last_value_age, int min_width, const print_info_t* pi) const;
Expand All @@ -110,6 +118,7 @@ namespace sapmachine_vitals {

virtual bool is_memory_size() const { return false; }

static Extremum extremum_default() { return NONE; }
};

// Some standard column types
Expand All @@ -118,8 +127,8 @@ namespace sapmachine_vitals {
int do_print0(outputStream* os, value_t value, value_t last_value,
int last_value_age, const print_info_t* pi) const;
public:
PlainValueColumn(const char* category, const char* header, const char* name, const char* description)
: Column(category, header, name, description)
PlainValueColumn(const char* category, const char* header, const char* name, const char* description, Extremum extremum)
: Column(category, header, name, description, extremum)
{}
};

Expand All @@ -128,36 +137,37 @@ namespace sapmachine_vitals {
int last_value_age, const print_info_t* pi) const;
public:
// only_positive: only positive deltas are shown, negative deltas are supressed
DeltaValueColumn(const char* category, const char* header, const char* name, const char* description)
: Column(category, header, name, description)
DeltaValueColumn(const char* category, const char* header, const char* name, const char* description, Extremum extremum)
: Column(category, header, name, description, extremum)
{}
};

class MemorySizeColumn: public Column {
int do_print0(outputStream* os, value_t value, value_t last_value,
int last_value_age, const print_info_t* pi) const;
public:
MemorySizeColumn(const char* category, const char* header, const char* name, const char* description)
: Column(category, header, name, description)
MemorySizeColumn(const char* category, const char* header, const char* name, const char* description, Extremum extremum)
: Column(category, header, name, description, extremum)
{}
bool is_memory_size() const { return true; }
static Extremum extremum_default() { return MAX; }
};

class DeltaMemorySizeColumn: public Column {
int do_print0(outputStream* os, value_t value, value_t last_value,
int last_value_age, const print_info_t* pi) const;
public:
DeltaMemorySizeColumn(const char* category, const char* header, const char* name, const char* description)
: Column(category, header, name, description)
DeltaMemorySizeColumn(const char* category, const char* header, const char* name, const char* description, Extremum extremum)
: Column(category, header, name, description, extremum)
{}
};

class TimeStampColumn: public Column {
int do_print0(outputStream* os, value_t value, value_t last_value,
int last_value_age, const print_info_t* pi) const;
public:
TimeStampColumn(const char* category, const char* header, const char* name, const char* description)
: Column(category, header, name, description)
TimeStampColumn(const char* category, const char* header, const char* name, const char* description, Extremum extremum)
: Column(category, header, name, description, extremum)
{}
};

Expand Down Expand Up @@ -218,11 +228,11 @@ namespace sapmachine_vitals {
Column* define_column (
const char* const category, const char* const header,
const char* const name, const char* const description,
bool is_active)
bool is_active, Extremum extremum = ColumnType::extremum_default())
{
Column* c = NULL;
if (is_active) {
c = new ColumnType(category, header, name, description);
c = new ColumnType(category, header, name, description, extremum);
ColumnList::the_list()->add_column(c);
}
Legend::the_legend()->add_column_info(category, header, name, description);
Expand Down

0 comments on commit f42b72c

Please sign in to comment.