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: Steve Fink <sphink@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
1a7943bcf3
commit
fbc0f876fa
136
misc/ss.c
136
misc/ss.c
|
|
@ -195,89 +195,146 @@ 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;
|
return val & (USER_ENT_HASH_SIZE - 1);
|
||||||
char name[1024];
|
}
|
||||||
DIR *dir;
|
|
||||||
|
static void user_ent_add(unsigned int ino, const char *process, int pid, int fd)
|
||||||
|
{
|
||||||
|
struct user_ent *p, **pp;
|
||||||
|
int str_len;
|
||||||
|
|
||||||
|
str_len = strlen(process) + 1;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void user_ent_hash_build(void)
|
||||||
|
{
|
||||||
|
const char *root = getenv("PROC_ROOT") ? : "/proc/";
|
||||||
struct dirent *d;
|
struct dirent *d;
|
||||||
int cnt = 0;
|
char name[1024];
|
||||||
int nameoff;
|
int nameoff;
|
||||||
|
DIR *dir;
|
||||||
|
|
||||||
if (!ino)
|
strcpy(name, root);
|
||||||
return 0;
|
if (strlen(name) == 0 || name[strlen(name)-1] != '/')
|
||||||
|
|
||||||
sprintf(pattern, "socket:[%u]", ino);
|
|
||||||
pattern_len = strlen(pattern);
|
|
||||||
|
|
||||||
strncpy(name, getenv("PROC_ROOT") ? : "/proc/", sizeof(name)/2);
|
|
||||||
name[sizeof(name)/2] = 0;
|
|
||||||
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);
|
||||||
if (ptr != buf)
|
|
||||||
ptr[-1] = 0;
|
|
||||||
return cnt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
ptr[-1] = '\0';
|
||||||
|
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
|
||||||
/* Get stats from slab */
|
/* Get stats from slab */
|
||||||
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue