parse-util: add parse_loadavg_fixed_point

This commit is contained in:
Anita Zhang 2020-06-04 02:13:29 -07:00
parent e30bbc90c9
commit 510ca79cf2
5 changed files with 145 additions and 4 deletions

48
src/basic/linux/loadavg.h Normal file
View File

@ -0,0 +1,48 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _LINUX_SCHED_LOADAVG_H
#define _LINUX_SCHED_LOADAVG_H
/*
* These are the constant used to fake the fixed-point load-average
* counting. Some notes:
* - 11 bit fractions expand to 22 bits by the multiplies: this gives
* a load-average precision of 10 bits integer + 11 bits fractional
* - if you want to count load-averages more often, you need more
* precision, or rounding will get you. With 2-second counting freq,
* the EXP_n values would be 1981, 2034 and 2043 if still using only
* 11 bit fractions.
*/
extern unsigned long avenrun[]; /* Load averages */
extern void get_avenrun(unsigned long *loads, unsigned long offset, int shift);
#define FSHIFT 11 /* nr of bits of precision */
#define FIXED_1 (1<<FSHIFT) /* 1.0 as fixed-point */
#define LOAD_FREQ (5*HZ+1) /* 5 sec intervals */
#define EXP_1 1884 /* 1/exp(5sec/1min) as fixed-point */
#define EXP_5 2014 /* 1/exp(5sec/5min) */
#define EXP_15 2037 /* 1/exp(5sec/15min) */
/*
* a1 = a0 * e + a * (1 - e)
*/
static inline unsigned long
calc_load(unsigned long load, unsigned long exp, unsigned long active)
{
unsigned long newload;
newload = load * exp + active * (FIXED_1 - exp);
if (active >= load)
newload += FIXED_1-1;
return newload / FIXED_1;
}
extern unsigned long calc_load_n(unsigned long load, unsigned long exp,
unsigned long active, unsigned int n);
#define LOAD_INT(x) ((x) >> FSHIFT)
#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)
extern void calc_global_load(unsigned long ticks);
#endif /* _LINUX_SCHED_LOADAVG_H */

View File

@ -108,6 +108,7 @@ basic_sources = files('''
linux/in6.h
linux/l2tp.h
linux/libc-compat.h
linux/loadavg.h
linux/netdevice.h
linux/netlink.h
linux/rtnetlink.h

View File

@ -862,3 +862,45 @@ int parse_oom_score_adjust(const char *s, int *ret) {
*ret = v;
return 0;
}
int store_loadavg_fixed_point(unsigned long i, unsigned long f, loadavg_t *ret) {
assert(ret);
if (i >= (~0UL << FSHIFT))
return -ERANGE;
i = i << FSHIFT;
f = DIV_ROUND_UP((f << FSHIFT), 100);
if (f >= FIXED_1)
return -ERANGE;
*ret = i | f;
return 0;
}
int parse_loadavg_fixed_point(const char *s, loadavg_t *ret) {
const char *d, *f_str, *i_str;
unsigned long i, f;
int r;
assert(s);
assert(ret);
d = strchr(s, '.');
if (!d)
return -EINVAL;
i_str = strndupa(s, d - s);
f_str = d + 1;
r = safe_atolu_full(i_str, 10, &i);
if (r < 0)
return r;
r = safe_atolu_full(f_str, 10, &f);
if (r < 0)
return r;
return store_loadavg_fixed_point(i, f, ret);
}

View File

@ -3,12 +3,15 @@
#include <inttypes.h>
#include <limits.h>
#include <linux/loadavg.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#include "macro.h"
typedef unsigned long loadavg_t;
int parse_boolean(const char *v) _pure_;
int parse_dev(const char *s, dev_t *ret);
int parse_pid(const char *s, pid_t* ret_pid);
@ -88,18 +91,18 @@ static inline int safe_atoux64(const char *s, uint64_t *ret) {
}
#if LONG_MAX == INT_MAX
static inline int safe_atolu(const char *s, unsigned long *ret_u) {
static inline int safe_atolu_full(const char *s, unsigned base, long unsigned *ret_u) {
assert_cc(sizeof(unsigned long) == sizeof(unsigned));
return safe_atou(s, (unsigned*) ret_u);
return safe_atou_full(s, base, (unsigned*) ret_u);
}
static inline int safe_atoli(const char *s, long int *ret_u) {
assert_cc(sizeof(long int) == sizeof(int));
return safe_atoi(s, (int*) ret_u);
}
#else
static inline int safe_atolu(const char *s, unsigned long *ret_u) {
static inline int safe_atolu_full(const char *s, unsigned base, unsigned long *ret_u) {
assert_cc(sizeof(unsigned long) == sizeof(unsigned long long));
return safe_atollu(s, (unsigned long long*) ret_u);
return safe_atollu_full(s, base, (unsigned long long*) ret_u);
}
static inline int safe_atoli(const char *s, long int *ret_u) {
assert_cc(sizeof(long int) == sizeof(long long int));
@ -107,6 +110,10 @@ static inline int safe_atoli(const char *s, long int *ret_u) {
}
#endif
static inline int safe_atolu(const char *s, unsigned long *ret_u) {
return safe_atolu_full(s, 0, ret_u);
}
#if SIZE_MAX == UINT_MAX
static inline int safe_atozu(const char *s, size_t *ret_u) {
assert_cc(sizeof(size_t) == sizeof(unsigned));
@ -137,3 +144,8 @@ int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high);
int parse_ip_prefix_length(const char *s, int *ret);
int parse_oom_score_adjust(const char *s, int *ret);
/* Given a Linux load average (e.g. decimal number 34.89 where 34 is passed as i and 89 is passed as f), convert it
* to a loadavg_t. */
int store_loadavg_fixed_point(unsigned long i, unsigned long f, loadavg_t *ret);
int parse_loadavg_fixed_point(const char *s, loadavg_t *ret);

View File

@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include <errno.h>
#include <linux/loadavg.h>
#include <locale.h>
#include <math.h>
#include <sys/socket.h>
@ -929,6 +930,42 @@ static void test_parse_mtu(void) {
assert_se(parse_mtu(AF_UNSPEC, "", &mtu) == -EINVAL);
}
static void test_parse_loadavg_fixed_point(void) {
loadavg_t fp;
assert_se(parse_loadavg_fixed_point("1.23", &fp) == 0);
assert_se(LOAD_INT(fp) == 1);
assert_se(LOAD_FRAC(fp) == 23);
assert_se(parse_loadavg_fixed_point("1.80", &fp) == 0);
assert_se(LOAD_INT(fp) == 1);
assert_se(LOAD_FRAC(fp) == 80);
assert_se(parse_loadavg_fixed_point("0.07", &fp) == 0);
assert_se(LOAD_INT(fp) == 0);
assert_se(LOAD_FRAC(fp) == 7);
assert_se(parse_loadavg_fixed_point("0.00", &fp) == 0);
assert_se(LOAD_INT(fp) == 0);
assert_se(LOAD_FRAC(fp) == 0);
assert_se(parse_loadavg_fixed_point("4096.57", &fp) == 0);
assert_se(LOAD_INT(fp) == 4096);
assert_se(LOAD_FRAC(fp) == 57);
/* Caps out at 2 digit fracs */
assert_se(parse_loadavg_fixed_point("1.100", &fp) == -ERANGE);
assert_se(parse_loadavg_fixed_point("4096.4096", &fp) == -ERANGE);
assert_se(parse_loadavg_fixed_point("-4000.5", &fp) == -ERANGE);
assert_se(parse_loadavg_fixed_point("18446744073709551615.5", &fp) == -ERANGE);
assert_se(parse_loadavg_fixed_point("foobar", &fp) == -EINVAL);
assert_se(parse_loadavg_fixed_point("3333", &fp) == -EINVAL);
assert_se(parse_loadavg_fixed_point("1.2.3", &fp) == -EINVAL);
assert_se(parse_loadavg_fixed_point(".", &fp) == -EINVAL);
assert_se(parse_loadavg_fixed_point("", &fp) == -EINVAL);
}
int main(int argc, char *argv[]) {
log_parse_environment();
log_open();
@ -955,6 +992,7 @@ int main(int argc, char *argv[]) {
test_parse_errno();
test_parse_syscall_and_errno();
test_parse_mtu();
test_parse_loadavg_fixed_point();
return 0;
}