mirror of
https://github.com/openjdk/jdk.git
synced 2026-05-03 10:20:57 +00:00
234 lines
6.7 KiB
C++
234 lines
6.7 KiB
C++
/*
|
|
* Copyright (c) 2023 SAP SE. All rights reserved.
|
|
* Copyright (c) 2023, 2024, 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 "precompiled.hpp"
|
|
#include "nmt/mallocLimit.hpp"
|
|
#include "nmt/memTag.hpp"
|
|
#include "nmt/nmtCommon.hpp"
|
|
#include "runtime/java.hpp"
|
|
#include "runtime/globals.hpp"
|
|
#include "utilities/globalDefinitions.hpp"
|
|
#include "utilities/parseInteger.hpp"
|
|
#include "utilities/ostream.hpp"
|
|
|
|
MallocLimitSet MallocLimitHandler::_limits;
|
|
bool MallocLimitHandler::_have_limit = false;
|
|
|
|
static const char* const MODE_OOM = "oom";
|
|
static const char* const MODE_FATAL = "fatal";
|
|
|
|
static const char* mode_to_name(MallocLimitMode m) {
|
|
switch (m) {
|
|
case MallocLimitMode::trigger_fatal: return MODE_FATAL;
|
|
case MallocLimitMode::trigger_oom: return MODE_OOM;
|
|
default: ShouldNotReachHere();
|
|
};
|
|
return nullptr;
|
|
}
|
|
|
|
class ParserHelper {
|
|
// Start, end of parsed string.
|
|
const char* const _s;
|
|
const char* const _end;
|
|
// Current parse position.
|
|
const char* _p;
|
|
|
|
public:
|
|
ParserHelper(const char* s) : _s(s), _end(s + strlen(s)), _p(s) {}
|
|
|
|
bool eof() const { return _p >= _end; }
|
|
|
|
// Check if string at position matches a malloclimit_mode_t.
|
|
// Advance position on match.
|
|
bool match_mode_flag(MallocLimitMode* out) {
|
|
if (eof()) {
|
|
return false;
|
|
}
|
|
if (strncasecmp(_p, MODE_OOM, strlen(MODE_OOM)) == 0) {
|
|
*out = MallocLimitMode::trigger_oom;
|
|
_p += 3;
|
|
return true;
|
|
} else if (strncasecmp(_p, MODE_FATAL, strlen(MODE_FATAL)) == 0) {
|
|
*out = MallocLimitMode::trigger_fatal;
|
|
_p += 5;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Check if string at position matches a category name.
|
|
// Advances position on match.
|
|
bool match_category(MemTag* out) {
|
|
if (eof()) {
|
|
return false;
|
|
}
|
|
const char* end = strchr(_p, ':');
|
|
if (end == nullptr) {
|
|
end = _end;
|
|
}
|
|
stringStream ss;
|
|
ss.print("%.*s", (int)(end - _p), _p);
|
|
MemTag mem_tag = NMTUtil::string_to_mem_tag(ss.base());
|
|
if (mem_tag != mtNone) {
|
|
*out = mem_tag;
|
|
_p = end;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Check if string at position matches a memory size (e.g. "100", "100g" etc).
|
|
// Advances position on match.
|
|
bool match_size(size_t* out) {
|
|
if (!eof()) {
|
|
char* remainder = nullptr;
|
|
if (parse_integer<size_t>(_p, &remainder, out)) {
|
|
assert(remainder > _p && remainder <= _end, "sanity");
|
|
_p = remainder;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Check if char at pos matches c; return true and advance pos if so.
|
|
bool match_char(char c) {
|
|
if (!eof() && (*_p) == c) {
|
|
_p ++;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
MallocLimitSet::MallocLimitSet() {
|
|
reset();
|
|
}
|
|
|
|
void MallocLimitSet::set_global_limit(size_t s, MallocLimitMode flag) {
|
|
_glob.sz = s; _glob.mode = flag;
|
|
}
|
|
|
|
void MallocLimitSet::set_category_limit(MemTag mem_tag, size_t s, MallocLimitMode flag) {
|
|
const int i = NMTUtil::tag_to_index(mem_tag);
|
|
_cat[i].sz = s; _cat[i].mode = flag;
|
|
}
|
|
|
|
void MallocLimitSet::reset() {
|
|
set_global_limit(0, MallocLimitMode::trigger_fatal);
|
|
_glob.sz = 0; _glob.mode = MallocLimitMode::trigger_fatal;
|
|
for (int i = 0; i < mt_number_of_tags; i++) {
|
|
set_category_limit(NMTUtil::index_to_tag(i), 0, MallocLimitMode::trigger_fatal);
|
|
}
|
|
}
|
|
|
|
void MallocLimitSet::print_on(outputStream* st) const {
|
|
static const char* flagnames[] = { MODE_FATAL, MODE_OOM };
|
|
if (_glob.sz > 0) {
|
|
st->print_cr("MallocLimit: total limit: " PROPERFMT " (%s)", PROPERFMTARGS(_glob.sz),
|
|
mode_to_name(_glob.mode));
|
|
} else {
|
|
for (int i = 0; i < mt_number_of_tags; i++) {
|
|
if (_cat[i].sz > 0) {
|
|
st->print_cr("MallocLimit: category \"%s\" limit: " PROPERFMT " (%s)",
|
|
NMTUtil::tag_to_enum_name(NMTUtil::index_to_tag(i)),
|
|
PROPERFMTARGS(_cat[i].sz), mode_to_name(_cat[i].mode));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool MallocLimitSet::parse_malloclimit_option(const char* v, const char** err) {
|
|
|
|
#define BAIL_UNLESS(condition, errormessage) if (!(condition)) { *err = errormessage; return false; }
|
|
|
|
// Global form:
|
|
// MallocLimit=<size>[:flag]
|
|
|
|
// Category-specific form:
|
|
// MallocLimit=<category>:<size>[:flag][,<category>:<size>[:flag]...]
|
|
|
|
reset();
|
|
|
|
ParserHelper sst(v);
|
|
|
|
BAIL_UNLESS(!sst.eof(), "Empty string");
|
|
|
|
// Global form?
|
|
if (sst.match_size(&_glob.sz)) {
|
|
// Match optional mode flag (e.g. 1g:oom)
|
|
if (!sst.eof()) {
|
|
BAIL_UNLESS(sst.match_char(':'), "Expected colon");
|
|
BAIL_UNLESS(sst.match_mode_flag(&_glob.mode), "Expected flag");
|
|
}
|
|
}
|
|
// Category-specific form?
|
|
else {
|
|
while (!sst.eof()) {
|
|
MemTag mem_tag;
|
|
|
|
// Match category, followed by :
|
|
BAIL_UNLESS(sst.match_category(&mem_tag), "Expected category name");
|
|
BAIL_UNLESS(sst.match_char(':'), "Expected colon following category");
|
|
|
|
malloclimit* const modified_limit = &_cat[NMTUtil::tag_to_index(mem_tag)];
|
|
|
|
// Match size
|
|
BAIL_UNLESS(sst.match_size(&modified_limit->sz), "Expected size");
|
|
|
|
// Match optional flag
|
|
if (!sst.eof() && sst.match_char(':')) {
|
|
BAIL_UNLESS(sst.match_mode_flag(&modified_limit->mode), "Expected flag");
|
|
}
|
|
|
|
// More to come?
|
|
if (!sst.eof()) {
|
|
BAIL_UNLESS(sst.match_char(','), "Expected comma");
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void MallocLimitHandler::initialize(const char* options) {
|
|
_have_limit = false;
|
|
if (options != nullptr && options[0] != '\0') {
|
|
const char* err = nullptr;
|
|
if (!_limits.parse_malloclimit_option(options, &err)) {
|
|
vm_exit_during_initialization("Failed to parse MallocLimit", err);
|
|
}
|
|
_have_limit = true;
|
|
}
|
|
}
|
|
|
|
void MallocLimitHandler::print_on(outputStream* st) {
|
|
if (have_limit()) {
|
|
_limits.print_on(st);
|
|
} else {
|
|
st->print_cr("MallocLimit: unset");
|
|
}
|
|
}
|
|
|