From: Masatake YAMATO Date: Fri, 2 Jun 2023 23:42:27 +0000 (+0900) Subject: lsfd: print the detail of the timer associated with a timerfd X-Git-Tag: v2.40-rc1~403^2~5 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=188b7d64bb60071ef9c221d10c69c9fe8a1bdc11;p=thirdparty%2Futil-linux.git lsfd: print the detail of the timer associated with a timerfd An example output: # ./lsfd -p 1 -Q '(TYPE == "timerfd")' -oCOMMAND,PID,ASSOC,TYPE,INODE,NAME COMMAND PID ASSOC TYPE INODE NAME systemd 1 22 timerfd 1060 clockid=monotonic remaining=4625.661834645 systemd 1 25 timerfd 1060 clockid=realtime remaining=398.164618943 systemd 1 112 timerfd 1060 clockid=realtime remaining=7537606384.202715161 Added three new columns, TIMERFD.{CLOCKID,TIMERFD.REMAINING,INTERVAL}. The filter engine works well with them. An example listing timerfd files expiring within 1.2 seconds. # ./lsfd -Q '(TIMERFD.REMAINING > 0.0) && (TIMERFD.REMAINING < 1.2)' COMMAND PID USER ASSOC MODE TYPE SOURCE MNTID INODE NAME Xorg 17069 jet 26 rw- timerfd anon_inodefs 15 1060 clockid=monotonic remaining=0.061075544 systemd-oomd 2382701 systemd-oom 8 rw- timerfd anon_inodefs 15 1060 clockid=monotonic remaining=0.178126915 Using SCOLS_JSON_NUMBER as the data type for TIMERFD.REMAINING and TIMERFD.INTERVAL columns is suggested by Thomas Weißschuh . Signed-off-by: Masatake YAMATO --- diff --git a/misc-utils/lsfd-unkn.c b/misc-utils/lsfd-unkn.c index 20579d2dd7..9af0816f11 100644 --- a/misc-utils/lsfd-unkn.c +++ b/misc-utils/lsfd-unkn.c @@ -25,6 +25,9 @@ #include "lsfd.h" +#include +#include + struct unkn { struct file file; const struct anon_ops *anon_ops; @@ -549,6 +552,169 @@ static const struct anon_ops anon_eventpoll_ops = { .attach_xinfo = anon_eventpoll_attach_xinfo, }; +/* + * timerfd + */ +struct anon_timerfd_data { + int clockid; + struct itimerspec itimerspec; +}; + +static bool anon_timerfd_probe(const char *str) +{ + return strncmp(str, "[timerfd]", 9) == 0; +} + +static void anon_timerfd_init(struct unkn *unkn) +{ + unkn->anon_data = xcalloc(1, sizeof(struct anon_timerfd_data)); +} + +static void anon_timerfd_free(struct unkn *unkn) +{ + struct anon_timerfd_data *data = unkn->anon_data; + free(data); +} + +static int anon_timerfd_handle_fdinfo(struct unkn *unkn, const char *key, const char *value) +{ + struct anon_timerfd_data *data = (struct anon_timerfd_data *)unkn->anon_data; + + if (strcmp(key, "clockid") == 0) { + unsigned long clockid; + char *end = NULL; + + errno = 0; + clockid = strtoul(value, &end, 0); + if (errno != 0) + return 0; /* ignore -- parse failed */ + if (*end != '\0') + return 0; /* ignore -- garbage remains. */ + + data->clockid = clockid; + return 1; + } else { + struct timespec *t; + uint64_t tv_sec; + uint64_t tv_nsec; + + if (strcmp(key, "it_value") == 0) + t = &data->itimerspec.it_value; + else if (strcmp(key, "it_interval") == 0) + t = &data->itimerspec.it_interval; + else + return 0; + + if (sscanf(value, "(%"SCNu64", %"SCNu64")", + &tv_sec, &tv_nsec) == 2) { + t->tv_sec = (time_t)tv_sec; + t->tv_nsec = (long)tv_nsec; + return 1; + } + + return 0; + } +} + +static bool is_zero_timespec(const struct timespec *t) +{ + return !t->tv_sec && !t->tv_nsec; +} + +static const char *anon_timerfd_decode_clockid(int clockid) +{ + switch (clockid) { + case CLOCK_REALTIME: + return "realtime"; + case CLOCK_MONOTONIC: + return "monotonic"; + case CLOCK_BOOTTIME: + return "boottime"; + case CLOCK_REALTIME_ALARM: + return "realtime-alarm"; + case CLOCK_BOOTTIME_ALARM: + return "boottime-alarm"; + default: + return "unknown"; + } +} + +static void anon_timerfd_render_timespec_string(char *buf, size_t size, + const char *prefix, + const struct timespec *t) +{ + snprintf(buf, size, "%s%llu.%09ld", + prefix? prefix: "", + (unsigned long long)t->tv_sec, t->tv_nsec); +} + +static char *anon_timerfd_get_name(struct unkn *unkn) +{ + char *str = NULL; + + struct anon_timerfd_data *data = (struct anon_timerfd_data *)unkn->anon_data; + const struct timespec *exp; + const struct timespec *ival; + + const char *clockid_name; + char exp_buf[BUFSIZ] = {'\0'}; + char ival_buf[BUFSIZ] = {'\0'}; + + clockid_name = anon_timerfd_decode_clockid(data->clockid); + + exp = &data->itimerspec.it_value; + if (!is_zero_timespec(exp)) + anon_timerfd_render_timespec_string(exp_buf, sizeof(exp_buf), + " remaining=", exp); + + ival = &data->itimerspec.it_interval; + if (!is_zero_timespec(ival)) + anon_timerfd_render_timespec_string(ival_buf, sizeof(ival_buf), + " interval=", ival); + + xasprintf(&str, "clockid=%s%s%s", clockid_name, exp_buf, ival_buf); + return str; +} + +static bool anon_timerfd_fill_column(struct proc *proc __attribute__((__unused__)), + struct unkn *unkn, + struct libscols_line *ln __attribute__((__unused__)), + int column_id, + size_t column_index __attribute__((__unused__)), + char **str) +{ + struct anon_timerfd_data *data = (struct anon_timerfd_data *)unkn->anon_data; + char buf[BUFSIZ] = {'\0'}; + + switch(column_id) { + case COL_TIMERFD_CLOCKID: + *str = xstrdup(anon_timerfd_decode_clockid(data->clockid)); + return true; + case COL_TIMERFD_INTERVAL: + anon_timerfd_render_timespec_string(buf, sizeof(buf), NULL, + &data->itimerspec.it_interval); + *str = xstrdup(buf); + return true; + case COL_TIMERFD_REMAINING: + anon_timerfd_render_timespec_string(buf, sizeof(buf), NULL, + &data->itimerspec.it_value); + *str = xstrdup(buf); + return true; + } + + return false; +} + +static const struct anon_ops anon_timerfd_ops = { + .class = "timerfd", + .probe = anon_timerfd_probe, + .get_name = anon_timerfd_get_name, + .fill_column = anon_timerfd_fill_column, + .init = anon_timerfd_init, + .free = anon_timerfd_free, + .handle_fdinfo = anon_timerfd_handle_fdinfo, +}; + /* * generic (fallback implementation) */ @@ -565,6 +731,7 @@ static const struct anon_ops *anon_ops[] = { &anon_pidfd_ops, &anon_eventfd_ops, &anon_eventpoll_ops, + &anon_timerfd_ops, }; static const struct anon_ops *anon_probe(const char *str) diff --git a/misc-utils/lsfd.c b/misc-utils/lsfd.c index 9c56460e13..93e2304d9f 100644 --- a/misc-utils/lsfd.c +++ b/misc-utils/lsfd.c @@ -294,6 +294,15 @@ static const struct colinfo infos[] = { [COL_TID] = { "TID", 5, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER, N_("thread ID of the process opening the file") }, + [COL_TIMERFD_CLOCKID] = { "TIMERFD.CLOCKID", + 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING, + N_("clockid") }, + [COL_TIMERFD_INTERVAL] = { "TIMERFD.INTERVAL", + 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER, + N_("interval") }, + [COL_TIMERFD_REMAINING]= { "TIMERFD.REMAINING", + 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER, + N_("remaining time") }, [COL_TYPE] = { "TYPE", 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING, N_("file type (cooked)") }, diff --git a/misc-utils/lsfd.h b/misc-utils/lsfd.h index 5b653658b5..7dcb553bf1 100644 --- a/misc-utils/lsfd.h +++ b/misc-utils/lsfd.h @@ -95,6 +95,9 @@ enum { COL_TCP_LPORT, COL_TCP_RPORT, COL_TID, + COL_TIMERFD_CLOCKID, + COL_TIMERFD_INTERVAL, + COL_TIMERFD_REMAINING, COL_TYPE, COL_UDP_LADDR, COL_UDP_RADDR,