Files
JetBrainsRuntime/src/hotspot/os/linux/procMapsParser.cpp
Albert Mingkun Yang eecba58c68 8371587: Final mapping lost in ProcSmapsParser::parse_next
Reviewed-by: jsjolen, fandreuzzi
2025-12-02 13:05:46 +00:00

135 lines
4.0 KiB
C++

/*
* Copyright (c) 2024, 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.
*
*/
#include "procMapsParser.hpp"
#include "runtime/os.hpp"
#include "utilities/globalDefinitions.hpp"
static bool is_lowercase_hex(char c) {
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f');
}
static size_t max_mapping_line_len() {
return 100 + // everything but the file name
os::vm_page_size() // the file name (kernel limits /proc/pid/cmdline to one page
;
}
ProcSmapsParser::ProcSmapsParser(FILE* f) :
_f(f), _linelen(max_mapping_line_len()), _line(nullptr) {
assert(_f != nullptr, "Invalid file handle given");
_line = NEW_C_HEAP_ARRAY(char, max_mapping_line_len(), mtInternal);
_line[0] = '\0';
}
ProcSmapsParser::~ProcSmapsParser() {
FREE_C_HEAP_ARRAY(char, _line);
}
bool ProcSmapsParser::read_line() {
_line[0] = '\0';
if (::fgets(_line, _linelen, _f) == nullptr) {
// On error or EOF, ensure deterministic empty buffer
_line[0] = '\0';
return false;
} else {
return true;
}
}
bool ProcSmapsParser::is_header_line() {
// e.g. ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]
return is_lowercase_hex(_line[0]); // All non-header lines in /proc/pid/smaps start with upper-case letters.
}
void ProcSmapsParser::scan_header_line(ProcSmapsInfo& out) {
const int items_read = ::sscanf(_line, "%p-%p %20s %*s %*s %*s %1024s",
&out.from, &out.to, out.prot, out.filename);
assert(items_read >= 2, "Expected header_line");
}
void ProcSmapsParser::scan_additional_line(ProcSmapsInfo& out) {
#define SCAN(key, var) \
if (::sscanf(_line, key ": %zu kB", &var) == 1) { \
var *= K; \
return; \
}
SCAN("KernelPageSize", out.kernelpagesize);
SCAN("Rss", out.rss);
SCAN("AnonHugePages", out.anonhugepages);
SCAN("Private_Hugetlb", out.private_hugetlb);
SCAN("Shared_Hugetlb", out.shared_hugetlb);
SCAN("Swap", out.swap);
#undef SCAN
// scan THPeligible into a bool
int thpel = 0;
if (::sscanf(_line, "THPeligible: %d", &thpel) == 1) {
assert(thpel == 1 || thpel == 0, "Unexpected value %d", thpel);
out.thpeligible = (thpel == 1);
return;
}
// scan some flags too
if (strncmp(_line, "VmFlags:", 8) == 0) {
#define SCAN(flag) { out.flag = (::strstr(_line + 8, " " #flag) != nullptr); }
SCAN(rd);
SCAN(wr);
SCAN(ex);
SCAN(nr);
SCAN(sh);
SCAN(hg);
SCAN(ht);
SCAN(nh);
#undef SCAN
}
}
bool ProcSmapsParser::parse_next(ProcSmapsInfo& out) {
// Information about a single mapping reaches across several lines.
out.reset();
// Read header line, unless we already read it
if (_line[0] == '\0') {
if (!read_line()) {
return false;
}
}
assert(is_header_line(), "Not a header line: \"%s\".", _line);
scan_header_line(out);
while (true) {
bool ok = read_line();
if (!ok || is_header_line()) {
break; // EOF or next header
}
scan_additional_line(out);
}
return true; // always return true if a mapping was parsed
}