]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lsfd: optimize maps use
authorKarel Zak <kzak@redhat.com>
Thu, 30 Sep 2021 08:42:02 +0000 (10:42 +0200)
committerKarel Zak <kzak@redhat.com>
Wed, 6 Oct 2021 09:01:54 +0000 (11:01 +0200)
The /proc/#/maps file usually contains the same file more than once.
This patch tries to reuse previous file to reduce number of stat()
calls, see "strace --summary-only" output below, the syscall is reduce
in 50%.

old version:

% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 55.57    0.118708           2     50756      5414 newfstatat

new version:

% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 39.17    0.058501           2     24436      5382 newfstatat

Signed-off-by: Karel Zak <kzak@redhat.com>
misc-utils/lsfd.c

index 35abd7d8c633df07259b78918bbca1251160b358..5c4cb17c4c720a6db0301d21a66fdc93cd3b4bb3 100644 (file)
@@ -250,6 +250,22 @@ static struct file *new_file(struct proc *proc, const struct file_class *class)
        return file;
 }
 
+static struct file *copy_file(struct file *old)
+{
+       struct file *file = xcalloc(1, old->class->size);
+
+       INIT_LIST_HEAD(&file->files);
+       file->proc = old->proc;
+       list_add_tail(&file->files, &old->proc->files);
+
+       file->class = old->class;
+       file->association = old->association;
+       file->name = xstrdup(old->name);
+       file->stat = old->stat;
+
+       return file;
+}
+
 static void file_set_path(struct file *file, struct stat *sb, const char *name, int association)
 {
        const struct file_class *class = stat2class(sb);
@@ -392,8 +408,9 @@ static void parse_maps_line(char *buf, struct proc *proc)
        unsigned long major, minor;
        enum association assoc = ASSOC_MEM;
        struct stat sb;
-       struct file *f;
+       struct file *f, *prev;
        char *path, modestr[5];
+       dev_t devno;
 
        /* ignore non-path entries */
        path = strchr(buf, '/');
@@ -401,10 +418,6 @@ static void parse_maps_line(char *buf, struct proc *proc)
                return;
        rtrim_whitespace((unsigned char *) path);
 
-       /* first try the path */
-       if (stat(path, &sb) < 0)
-               return;
-
        /* read rest of the map */
        if (sscanf(buf, "%"SCNx64               /* start */
                        "-%"SCNx64              /* end */
@@ -417,11 +430,28 @@ static void parse_maps_line(char *buf, struct proc *proc)
                        &major, &minor, &ino) != 7)
                return;
 
-       f = new_file(proc, stat2class(&sb));
-       if (!f)
-               return;
+       devno = makedev(major, minor);
+
+       if (modestr[3] == 's')
+               assoc = ASSOC_SHM;
 
-       file_set_path(f, &sb, path, -assoc);
+       /* The map usually contains the same file more than once, try to reuse
+        * the previous file (if devno and ino are the same) to save stat() call.
+        */
+       prev = list_last_entry(&proc->files, struct file, files);
+
+       if (prev && prev->stat.st_dev == devno && prev->stat.st_ino == ino) {
+               f = copy_file(prev);
+               f->association = -assoc;
+       } else {
+               if (stat(path, &sb) < 0)
+                       return;
+               f = new_file(proc, stat2class(&sb));
+               if (!f)
+                       return;
+
+               file_set_path(f, &sb, path, -assoc);
+       }
 
        if (modestr[0] == 'r')
                f->mode |= S_IRUSR;
@@ -429,8 +459,6 @@ static void parse_maps_line(char *buf, struct proc *proc)
                f->mode |= S_IWUSR;
        if (modestr[2] == 'x')
                f->mode |= S_IXUSR;
-       if (modestr[3] == 's')
-               assoc = ASSOC_SHM;
 
        f->map_start = start;
        f->map_end = end;