shared: helpers to read pressure stats from cgroups
This commit is contained in:
parent
510ca79cf2
commit
42b83e8e33
|
@ -187,6 +187,8 @@ shared_sources = files('''
|
|||
pkcs11-util.h
|
||||
pretty-print.c
|
||||
pretty-print.h
|
||||
psi-util.c
|
||||
psi-util.h
|
||||
ptyfwd.c
|
||||
ptyfwd.h
|
||||
pwquality-util.c
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "extract-word.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "parse-util.h"
|
||||
#include "psi-util.h"
|
||||
#include "string-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "strv.h"
|
||||
|
||||
int read_resource_pressure(const char *path, PressureType type, ResourcePressure *ret) {
|
||||
_cleanup_free_ char *line = NULL;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
unsigned field_filled = 0;
|
||||
ResourcePressure rp = {};
|
||||
const char *t, *cline;
|
||||
char *word;
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
assert(IN_SET(type, PRESSURE_TYPE_SOME, PRESSURE_TYPE_FULL));
|
||||
assert(ret);
|
||||
|
||||
if (type == PRESSURE_TYPE_SOME)
|
||||
t = "some";
|
||||
else if (type == PRESSURE_TYPE_FULL)
|
||||
t = "full";
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
r = fopen_unlocked(path, "re", &f);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *l = NULL;
|
||||
char *w;
|
||||
|
||||
r = read_line(f, LONG_LINE_MAX, &l);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
w = first_word(l, t);
|
||||
if (w) {
|
||||
line = TAKE_PTR(l);
|
||||
cline = w;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!line)
|
||||
return -ENODATA;
|
||||
|
||||
/* extracts either avgX=Y.Z or total=X */
|
||||
while ((r = extract_first_word(&cline, &word, NULL, 0)) > 0) {
|
||||
_cleanup_free_ char *w = word;
|
||||
const char *v;
|
||||
|
||||
if ((v = startswith(w, "avg10="))) {
|
||||
if (field_filled & (1U << 0))
|
||||
return -EINVAL;
|
||||
|
||||
field_filled |= 1U << 0;
|
||||
r = parse_loadavg_fixed_point(v, &rp.avg10);
|
||||
} else if ((v = startswith(w, "avg60="))) {
|
||||
if (field_filled & (1U << 1))
|
||||
return -EINVAL;
|
||||
|
||||
field_filled |= 1U << 1;
|
||||
r = parse_loadavg_fixed_point(v, &rp.avg60);
|
||||
} else if ((v = startswith(w, "avg300="))) {
|
||||
if (field_filled & (1U << 2))
|
||||
return -EINVAL;
|
||||
|
||||
field_filled |= 1U << 2;
|
||||
r = parse_loadavg_fixed_point(v, &rp.avg300);
|
||||
} else if ((v = startswith(w, "total="))) {
|
||||
if (field_filled & (1U << 3))
|
||||
return -EINVAL;
|
||||
|
||||
field_filled |= 1U << 3;
|
||||
r = safe_atou64(v, &rp.total);
|
||||
} else
|
||||
continue;
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (field_filled != 15U)
|
||||
return -EINVAL;
|
||||
|
||||
*ret = rp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int is_pressure_supported(void) {
|
||||
const char *p;
|
||||
|
||||
FOREACH_STRING(p, "/proc/pressure/cpu", "/proc/pressure/io", "/proc/pressure/memory")
|
||||
if (access(p, F_OK) < 0) {
|
||||
if (errno == ENOENT)
|
||||
return 0;
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "parse-util.h"
|
||||
#include "time-util.h"
|
||||
|
||||
typedef enum PressureType {
|
||||
PRESSURE_TYPE_SOME,
|
||||
PRESSURE_TYPE_FULL,
|
||||
} PressureType;
|
||||
|
||||
/* Averages are stored in fixed-point with 11 bit fractions */
|
||||
typedef struct ResourcePressure {
|
||||
loadavg_t avg10;
|
||||
loadavg_t avg60;
|
||||
loadavg_t avg300;
|
||||
usec_t total;
|
||||
} ResourcePressure;
|
||||
|
||||
/** Upstream 4.20+ format
|
||||
*
|
||||
* some avg10=0.22 avg60=0.17 avg300=1.11 total=58761459
|
||||
* full avg10=0.23 avg60=0.16 avg300=1.08 total=58464525
|
||||
*/
|
||||
int read_resource_pressure(const char *path, PressureType type, ResourcePressure *ret);
|
||||
|
||||
/* Was the kernel compiled with CONFIG_PSI=y? 1 if yes, 0 if not, negative on error. */
|
||||
int is_pressure_supported(void);
|
|
@ -804,6 +804,10 @@ tests += [
|
|||
[['src/test/test-local-addresses.c'],
|
||||
[],
|
||||
[]],
|
||||
|
||||
[['src/test/test-psi-util.c'],
|
||||
[],
|
||||
[]],
|
||||
]
|
||||
|
||||
############################################################
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include <linux/loadavg.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "psi-util.h"
|
||||
#include "tests.h"
|
||||
|
||||
static void test_read_mem_pressure(void) {
|
||||
_cleanup_(unlink_tempfilep) char path[] = "/tmp/pressurereadtestXXXXXX";
|
||||
ResourcePressure rp;
|
||||
|
||||
if (geteuid() != 0)
|
||||
return (void) log_tests_skipped("not root");
|
||||
|
||||
assert_se(mkstemp(path));
|
||||
|
||||
assert_se(read_resource_pressure("/verylikelynonexistentpath", PRESSURE_TYPE_SOME, &rp) < 0);
|
||||
assert_se(read_resource_pressure(path, PRESSURE_TYPE_SOME, &rp) < 0);
|
||||
|
||||
assert_se(write_string_file(path, "herpdederp\n", WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(read_resource_pressure(path, PRESSURE_TYPE_SOME, &rp) < 0);
|
||||
|
||||
/* Pressure file with some invalid values*/
|
||||
assert_se(write_string_file(path, "some avg10=0.22=55 avg60=0.17=8 avg300=1.11=00 total=58761459\n"
|
||||
"full avg10=0.23=55 avg60=0.16=8 avg300=1.08=00 total=58464525", WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(read_resource_pressure(path, PRESSURE_TYPE_SOME, &rp) < 0);
|
||||
|
||||
/* Same pressure valid values as below but with duplicate avg60 field */
|
||||
assert_se(write_string_file(path, "some avg10=0.22 avg60=0.17 avg60=0.18 avg300=1.11 total=58761459\n"
|
||||
"full avg10=0.23 avg60=0.16 avg300=1.08 total=58464525", WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(read_resource_pressure(path, PRESSURE_TYPE_SOME, &rp) < 0);
|
||||
|
||||
assert_se(write_string_file(path, "some avg10=0.22 avg60=0.17 avg300=1.11 total=58761459\n"
|
||||
"full avg10=0.23 avg60=0.16 avg300=1.08 total=58464525", WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(read_resource_pressure(path, PRESSURE_TYPE_SOME, &rp) == 0);
|
||||
assert_se(LOAD_INT(rp.avg10) == 0);
|
||||
assert_se(LOAD_FRAC(rp.avg10) == 22);
|
||||
assert_se(LOAD_INT(rp.avg60) == 0);
|
||||
assert_se(LOAD_FRAC(rp.avg60) == 17);
|
||||
assert_se(LOAD_INT(rp.avg300) == 1);
|
||||
assert_se(LOAD_FRAC(rp.avg300) == 11);
|
||||
assert_se(rp.total == 58761459);
|
||||
assert(read_resource_pressure(path, PRESSURE_TYPE_FULL, &rp) == 0);
|
||||
assert_se(LOAD_INT(rp.avg10) == 0);
|
||||
assert_se(LOAD_FRAC(rp.avg10) == 23);
|
||||
assert_se(LOAD_INT(rp.avg60) == 0);
|
||||
assert_se(LOAD_FRAC(rp.avg60) == 16);
|
||||
assert_se(LOAD_INT(rp.avg300) == 1);
|
||||
assert_se(LOAD_FRAC(rp.avg300) == 8);
|
||||
assert_se(rp.total == 58464525);
|
||||
|
||||
/* Pressure file with extra unsupported fields */
|
||||
assert_se(write_string_file(path, "some avg5=0.55 avg10=0.22 avg60=0.17 avg300=1.11 total=58761459\n"
|
||||
"full avg10=0.23 avg60=0.16 avg300=1.08 avg600=2.00 total=58464525", WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(read_resource_pressure(path, PRESSURE_TYPE_SOME, &rp) == 0);
|
||||
assert_se(LOAD_INT(rp.avg10) == 0);
|
||||
assert_se(LOAD_FRAC(rp.avg10) == 22);
|
||||
assert_se(LOAD_INT(rp.avg60) == 0);
|
||||
assert_se(LOAD_FRAC(rp.avg60) == 17);
|
||||
assert_se(LOAD_INT(rp.avg300) == 1);
|
||||
assert_se(LOAD_FRAC(rp.avg300) == 11);
|
||||
assert_se(rp.total == 58761459);
|
||||
assert(read_resource_pressure(path, PRESSURE_TYPE_FULL, &rp) == 0);
|
||||
assert_se(LOAD_INT(rp.avg10) == 0);
|
||||
assert_se(LOAD_FRAC(rp.avg10) == 23);
|
||||
assert_se(LOAD_INT(rp.avg60) == 0);
|
||||
assert_se(LOAD_FRAC(rp.avg60) == 16);
|
||||
assert_se(LOAD_INT(rp.avg300) == 1);
|
||||
assert_se(LOAD_FRAC(rp.avg300) == 8);
|
||||
assert_se(rp.total == 58464525);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
test_setup_logging(LOG_DEBUG);
|
||||
test_read_mem_pressure();
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue