]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lsfd: print the detail of the timer associated with a timerfd
authorMasatake YAMATO <yamato@redhat.com>
Fri, 2 Jun 2023 23:42:27 +0000 (08:42 +0900)
committerMasatake YAMATO <yamato@redhat.com>
Sun, 4 Jun 2023 08:18:38 +0000 (17:18 +0900)
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
<thomas@t-8ch.de>.

Signed-off-by: Masatake YAMATO <yamato@redhat.com>
misc-utils/lsfd-unkn.c
misc-utils/lsfd.c
misc-utils/lsfd.h

index 20579d2dd7143a05248d9917585b592f0c931099..9af0816f11697634c134e1d9be65424be128a992 100644 (file)
@@ -25,6 +25,9 @@
 
 #include "lsfd.h"
 
+#include <sys/timerfd.h>
+#include <time.h>
+
 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)
index 9c56460e1321ce09f6fb2a1348081938cd9d909f..93e2304d9f86cd8f7e23975ae8873138da9e942e 100644 (file)
@@ -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)") },
index 5b653658b57a5140c9fd285b24b4476dd8f914e8..7dcb553bf1737dd97e055358289196d701cc0ba5 100644 (file)
@@ -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,