>From 5244c0e3fec2af28bf3922b4f0e45b209880f27e Mon Sep 17 00:00:00 2001 From: Joakim Sindholt Date: Tue, 12 Jul 2016 11:12:24 +0200 Subject: [PATCH 2/2] optimize nftw for stack usage Before this we would see 296 bytes of stack used per frame on amd64 using clang, and after it's all the way down to 88 bytes per frame. Less than a third of what it was and much more reasonable. --- src/misc/nftw.c | 91 ++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 58 insertions(+), 33 deletions(-) diff --git a/src/misc/nftw.c b/src/misc/nftw.c index 7df7226..6057d89 100644 --- a/src/misc/nftw.c +++ b/src/misc/nftw.c @@ -8,6 +8,18 @@ #include #include "libc.h" +struct context +{ + char *path; + int (*fn)(const char *, const struct stat *, int, struct FTW *); + int fd_limit; + int flags; + + size_t l; + struct stat st; + struct FTW lev; +}; + struct history { struct history *chain; @@ -20,68 +32,70 @@ struct history #undef dirfd #define dirfd(d) (*(int *)d) -static int do_nftw(char *path, int (*fn)(const char *, const struct stat *, int, struct FTW *), int fd_limit, int flags, struct history *h) +static int do_nftw(struct context *c, struct history *h) { - size_t l = strlen(path), j = l && path[l-1]=='/' ? l-1 : l; - struct stat st; struct history new; int type; int r; - struct FTW lev; - char *name; - if ((flags & FTW_PHYS) ? lstat(path, &st) : stat(path, &st) < 0) { - if (!(flags & FTW_PHYS) && errno==ENOENT && !lstat(path, &st)) + if ((c->flags & FTW_PHYS) ? lstat(c->path, &c->st) : stat(c->path, &c->st) < 0) { + if (!(c->flags & FTW_PHYS) && errno==ENOENT && !lstat(c->path, &c->st)) type = FTW_SLN; else if (errno != EACCES) return -1; else type = FTW_NS; - } else if (S_ISDIR(st.st_mode)) { - if (access(path, R_OK) < 0) type = FTW_DNR; - else if (flags & FTW_DEPTH) type = FTW_DP; + } else if (S_ISDIR(c->st.st_mode)) { + if (access(c->path, R_OK) < 0) type = FTW_DNR; + else if (c->flags & FTW_DEPTH) type = FTW_DP; else type = FTW_D; - } else if (S_ISLNK(st.st_mode)) { - if (flags & FTW_PHYS) type = FTW_SL; + } else if (S_ISLNK(c->st.st_mode)) { + if (c->flags & FTW_PHYS) type = FTW_SL; else type = FTW_SLN; } else { type = FTW_F; } - if ((flags & FTW_MOUNT) && st.st_dev != h->dev) + if ((c->flags & FTW_MOUNT) && c->st.st_dev != h->dev) return 0; - + new.chain = h; - new.dev = st.st_dev; - new.ino = st.st_ino; + new.dev = c->st.st_dev; + new.ino = c->st.st_ino; new.level = h->level+1; - new.base = j+1; + new.base = c->l+1; - lev.level = new.level; - lev.base = h->base; - - if (!(flags & FTW_DEPTH) && (r=fn(path, &st, type, &lev))) - return r; + if (!(c->flags & FTW_DEPTH)) { + c->lev.level = new.level; + c->lev.base = h->base; + if ((r=c->fn(c->path, &c->st, type, &c->lev))) + return r; + } for (; h; h = h->chain) - if (h->dev == st.st_dev && h->ino == st.st_ino) + if (h->dev == c->st.st_dev && h->ino == c->st.st_ino) return 0; - if ((type == FTW_D || type == FTW_DP) && fd_limit) { - DIR *d = opendir(path); + if ((type == FTW_D || type == FTW_DP) && c->fd_limit) { + DIR *d = opendir(c->path); if (d) { struct dirent *de; while ((de = readdir(d))) { + size_t dl; if (de->d_name[0] == '.' && (!de->d_name[1] || (de->d_name[1]=='.' && !de->d_name[2]))) continue; - if (strlen(de->d_name) >= PATH_MAX-l) { + if ((dl=strlen(de->d_name)) > PATH_MAX-new.base) { errno = ENAMETOOLONG; closedir(d); return -1; } - path[j]='/'; - strcpy(path+j+1, de->d_name); - if ((r=do_nftw(path, fn, fd_limit-1, flags, &new))) { + c->l = new.base+dl; + c->path[new.base-1]='/'; + memcpy(c->path+new.base, de->d_name, dl+1); + --c->fd_limit; + r=do_nftw(c, &new); + ++c->fd_limit; + if (r) { closedir(d); return r; } @@ -91,16 +105,21 @@ static int do_nftw(char *path, int (*fn)(const char *, const struct stat *, int, return -1; } } + c->path[new.base-1]=0; - path[l] = 0; - if ((flags & FTW_DEPTH) && (r=fn(path, &st, type, &lev))) - return r; + if (c->flags & FTW_DEPTH) { + c->lev.level = new.level; + c->lev.base = h->base; + if ((r=c->fn(c->path, &c->st, type, &c->lev))) + return r; + } return 0; } int nftw(const char *path, int (*fn)(const char *, const struct stat *, int, struct FTW *), int fd_limit, int flags) { + struct context c; struct history h; int r, cs; size_t l; @@ -126,8 +145,14 @@ int nftw(const char *path, int (*fn)(const char *, const struct stat *, int, str while (h.base && pathbuf[h.base-1]!='/') --h.base; + c.path = pathbuf; + c.fn = fn; + c.fd_limit = fd_limit; + c.flags = flags; + c.l = l; + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); - r = do_nftw(pathbuf, fn, fd_limit, flags, &h); + r = do_nftw(&c, &h); pthread_setcancelstate(cs, 0); return r; } -- 2.7.3