Commit fbc0f876 authored by Steve Fink's avatar Steve Fink Committed by Stephen Hemminger

ss -p is much too slow

> On closer inspection, it appears that ss -p does a quadratic scan. It
> rescans every entry in /proc/*/fd/* repeatedly (once per listening
> port? per process? I don't remember what I figured out.)
>
> I humbly suggest that this is not a good idea.

Yep, this is junk.  Please give this patch a try:

ss: Avoid quadradic complexity with '-p'

Scan the process list of open sockets once, and store in a hash
table to be used by subsequent find_user() calls.
Reported-by: default avatarSteve Fink <sphink@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 1a7943bc
...@@ -195,90 +195,147 @@ static FILE *ephemeral_ports_open(void) ...@@ -195,90 +195,147 @@ static FILE *ephemeral_ports_open(void)
return generic_proc_open("PROC_IP_LOCAL_PORT_RANGE", "sys/net/ipv4/ip_local_port_range"); return generic_proc_open("PROC_IP_LOCAL_PORT_RANGE", "sys/net/ipv4/ip_local_port_range");
} }
int find_users(unsigned ino, char *buf, int buflen) struct user_ent {
struct user_ent *next;
unsigned int ino;
int pid;
int fd;
char process[0];
};
#define USER_ENT_HASH_SIZE 256
struct user_ent *user_ent_hash[USER_ENT_HASH_SIZE];
static int user_ent_hashfn(unsigned int ino)
{ {
char pattern[64]; int val = (ino >> 24) ^ (ino >> 16) ^ (ino >> 8) ^ ino;
int pattern_len;
char *ptr = buf;
char name[1024];
DIR *dir;
struct dirent *d;
int cnt = 0;
int nameoff;
if (!ino) return val & (USER_ENT_HASH_SIZE - 1);
return 0; }
static void user_ent_add(unsigned int ino, const char *process, int pid, int fd)
{
struct user_ent *p, **pp;
int str_len;
sprintf(pattern, "socket:[%u]", ino); str_len = strlen(process) + 1;
pattern_len = strlen(pattern); p = malloc(sizeof(struct user_ent) + str_len);
if (!p)
abort();
p->next = NULL;
p->ino = ino;
p->pid = pid;
p->fd = fd;
strcpy(p->process, process);
pp = &user_ent_hash[user_ent_hashfn(ino)];
p->next = *pp;
*pp = p;
}
strncpy(name, getenv("PROC_ROOT") ? : "/proc/", sizeof(name)/2); static void user_ent_hash_build(void)
name[sizeof(name)/2] = 0; {
if (strlen(name) == 0 || const char *root = getenv("PROC_ROOT") ? : "/proc/";
name[strlen(name)-1] != '/') struct dirent *d;
char name[1024];
int nameoff;
DIR *dir;
strcpy(name, root);
if (strlen(name) == 0 || name[strlen(name)-1] != '/')
strcat(name, "/"); strcat(name, "/");
nameoff = strlen(name); nameoff = strlen(name);
if ((dir = opendir(name)) == NULL)
return 0; dir = opendir(name);
if (!dir)
return;
while ((d = readdir(dir)) != NULL) { while ((d = readdir(dir)) != NULL) {
DIR *dir1;
struct dirent *d1; struct dirent *d1;
int pid;
int pos;
char crap;
char process[16]; char process[16];
int pid, pos;
DIR *dir1;
char crap;
if (sscanf(d->d_name, "%d%c", &pid, &crap) != 1) if (sscanf(d->d_name, "%d%c", &pid, &crap) != 1)
continue; continue;
sprintf(name+nameoff, "%d/fd/", pid); sprintf(name + nameoff, "%d/fd/", pid);
pos = strlen(name); pos = strlen(name);
if ((dir1 = opendir(name)) == NULL) if ((dir1 = opendir(name)) == NULL)
continue; continue;
process[0] = 0; process[0] = '\0';
while ((d1 = readdir(dir1)) != NULL) { while ((d1 = readdir(dir1)) != NULL) {
int fd, n; const char *pattern = "socket:[";
unsigned int ino;
char lnk[64]; char lnk[64];
int fd, n;
if (sscanf(d1->d_name, "%d%c", &fd, &crap) != 1) if (sscanf(d1->d_name, "%d%c", &fd, &crap) != 1)
continue; continue;
sprintf(name+pos, "%d", fd); sprintf(name+pos, "%d", fd);
n = readlink(name, lnk, sizeof(lnk)-1); n = readlink(name, lnk, sizeof(lnk)-1);
if (n != pattern_len || if (strncmp(lnk, pattern, strlen(pattern)))
memcmp(lnk, pattern, n))
continue; continue;
if (ptr-buf >= buflen-1) sscanf(lnk, "socket:[%u]", &ino);
break;
if (process[0] == 0) { if (process[0] == '\0') {
char tmp[1024]; char tmp[1024];
FILE *fp; FILE *fp;
snprintf(tmp, sizeof(tmp), "%s/%d/stat",
getenv("PROC_ROOT") ? : "/proc", pid); snprintf(tmp, sizeof(tmp), "%s/%d/stat", root, pid);
if ((fp = fopen(tmp, "r")) != NULL) { if ((fp = fopen(tmp, "r")) != NULL) {
fscanf(fp, "%*d (%[^)])", process); fscanf(fp, "%*d (%[^)])", process);
fclose(fp); fclose(fp);
} }
} }
snprintf(ptr, buflen-(ptr-buf), "(\"%s\",%d,%d),", process, pid, fd); user_ent_add(ino, process, pid, fd);
ptr += strlen(ptr);
cnt++;
} }
closedir(dir1); closedir(dir1);
} }
closedir(dir); closedir(dir);
}
int find_users(unsigned ino, char *buf, int buflen)
{
struct user_ent *p;
int cnt = 0;
char *ptr;
if (!ino)
return 0;
p = user_ent_hash[user_ent_hashfn(ino)];
ptr = buf;
while (p) {
if (p->ino != ino)
goto next;
if (ptr - buf >= buflen - 1)
break;
snprintf(ptr, buflen - (ptr - buf),
"(\"%s\",%d,%d),",
p->process, p->pid, p->fd);
ptr += strlen(ptr);
cnt++;
next:
p = p->next;
}
if (ptr != buf) if (ptr != buf)
ptr[-1] = 0; ptr[-1] = '\0';
return cnt; return cnt;
} }
/* Get stats from slab */ /* Get stats from slab */
struct slabstat struct slabstat
...@@ -2476,6 +2533,7 @@ int main(int argc, char *argv[]) ...@@ -2476,6 +2533,7 @@ int main(int argc, char *argv[])
break; break;
case 'p': case 'p':
show_users++; show_users++;
user_ent_hash_build();
break; break;
case 'd': case 'd':
current_filter.dbs |= (1<<DCCP_DB); current_filter.dbs |= (1<<DCCP_DB);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment