aboutsummaryrefslogtreecommitdiff
path: root/libbb/procps.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--libbb/procps.c177
1 files changed, 86 insertions, 91 deletions
diff --git a/libbb/procps.c b/libbb/procps.c
index f56b71b21..c751100bc 100644
--- a/libbb/procps.c
+++ b/libbb/procps.c
@@ -63,6 +63,7 @@ const char* FAST_FUNC get_cached_groupname(gid_t gid)
63 return get_cached(1, gid, gid2group_utoa); 63 return get_cached(1, gid, gid2group_utoa);
64} 64}
65 65
66#if !ENABLE_PLATFORM_MINGW32
66 67
67#define PROCPS_BUFSIZE 1024 68#define PROCPS_BUFSIZE 1024
68 69
@@ -109,7 +110,7 @@ void FAST_FUNC free_procps_scan(procps_status_t* sp)
109} 110}
110 111
111#if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP 112#if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP
112static unsigned long long fast_strtoull_16(char **endptr) 113unsigned long long FAST_FUNC fast_strtoull_16(char **endptr)
113{ 114{
114 unsigned char c; 115 unsigned char c;
115 char *str = *endptr; 116 char *str = *endptr;
@@ -130,7 +131,7 @@ static unsigned long long fast_strtoull_16(char **endptr)
130 131
131#if ENABLE_FEATURE_FAST_TOP || ENABLE_FEATURE_TOPMEM || ENABLE_PMAP 132#if ENABLE_FEATURE_FAST_TOP || ENABLE_FEATURE_TOPMEM || ENABLE_PMAP
132/* We cut a lot of corners here for speed */ 133/* We cut a lot of corners here for speed */
133static unsigned long fast_strtoul_10(char **endptr) 134unsigned long FAST_FUNC fast_strtoul_10(char **endptr)
134{ 135{
135 unsigned char c; 136 unsigned char c;
136 char *str = *endptr; 137 char *str = *endptr;
@@ -143,6 +144,24 @@ static unsigned long fast_strtoul_10(char **endptr)
143 *endptr = str + 1; /* We skip trailing space! */ 144 *endptr = str + 1; /* We skip trailing space! */
144 return n; 145 return n;
145} 146}
147# if LONG_MAX < LLONG_MAX
148/* For VSZ, which can be very large */
149static unsigned long long fast_strtoull_10(char **endptr)
150{
151 unsigned char c;
152 char *str = *endptr;
153 unsigned long long n = *str - '0';
154
155 /* Need to stop on both ' ' and '\n' */
156 while ((c = *++str) > ' ')
157 n = n*10 + (c - '0');
158
159 *endptr = str + 1; /* We skip trailing space! */
160 return n;
161}
162# else
163# define fast_strtoull_10(endptr) fast_strtoul_10(endptr)
164# endif
146 165
147# if ENABLE_FEATURE_FAST_TOP 166# if ENABLE_FEATURE_FAST_TOP
148static long fast_strtol_10(char **endptr) 167static long fast_strtol_10(char **endptr)
@@ -155,7 +174,7 @@ static long fast_strtol_10(char **endptr)
155} 174}
156# endif 175# endif
157 176
158static char *skip_fields(char *str, int count) 177char* FAST_FUNC skip_fields(char *str, int count)
159{ 178{
160 do { 179 do {
161 while (*str++ != ' ') 180 while (*str++ != ' ')
@@ -166,35 +185,25 @@ static char *skip_fields(char *str, int count)
166} 185}
167#endif 186#endif
168 187
169#if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP 188#if ENABLE_FEATURE_TOPMEM
170static char* skip_whitespace_if_prefixed_with(char *buf, const char *prefix) 189static NOINLINE void procps_read_smaps(pid_t pid, procps_status_t *sp)
171{ 190{
172 char *tp = is_prefixed_with(buf, prefix); 191 // There is A LOT of /proc/PID/smaps data on a big system.
173 if (tp) { 192 // Optimize this for speed, makes "top -m" faster.
174 tp = skip_whitespace(tp); 193//TODO large speedup:
175 } 194//read /proc/PID/smaps_rollup (cumulative stats of all mappings, much faster)
176 return tp; 195//and /proc/PID/maps to get mapped_ro and mapped_rw (IOW: VSZ,VSZRW)
177}
178 196
179int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total,
180 void (*cb)(struct smaprec *, void *), void *data)
181{
182 FILE *file; 197 FILE *file;
183 struct smaprec currec;
184 char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3]; 198 char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3];
185 char buf[PROCPS_BUFSIZE]; 199 char buf[PROCPS_BUFSIZE];
186#if !ENABLE_PMAP
187 void (*cb)(struct smaprec *, void *) = NULL;
188 void *data = NULL;
189#endif
190 200
191 sprintf(filename, "/proc/%u/smaps", (int)pid); 201 sprintf(filename, "/proc/%u/smaps", (int)pid);
192 202
193 file = fopen_for_read(filename); 203 file = fopen_for_read(filename);
194 if (!file) 204 if (!file)
195 return 1; 205 return;
196 206
197 memset(&currec, 0, sizeof(currec));
198 while (fgets(buf, PROCPS_BUFSIZE, file)) { 207 while (fgets(buf, PROCPS_BUFSIZE, file)) {
199 // Each mapping datum has this form: 208 // Each mapping datum has this form:
200 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME 209 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME
@@ -202,80 +211,53 @@ int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total,
202 // Rss: nnn kB 211 // Rss: nnn kB
203 // ..... 212 // .....
204 213
205 char *tp, *p; 214 char *tp;
206 215
216 if (buf[0] == 'S' || buf[0] == 'P') {
207#define SCAN(S, X) \ 217#define SCAN(S, X) \
208 if ((tp = skip_whitespace_if_prefixed_with(buf, S)) != NULL) { \ 218 if (memcmp(buf, S, sizeof(S)-1) == 0) { \
209 total->X += currec.X = fast_strtoul_10(&tp); \ 219 tp = skip_whitespace(buf + sizeof(S)-1); \
210 continue; \ 220 sp->X += fast_strtoul_10(&tp); \
211 } 221 continue; \
212 if (cb) { 222 }
213 SCAN("Pss:" , smap_pss ); 223 SCAN("Private_Dirty:", private_dirty)
214 SCAN("Swap:" , smap_swap ); 224 SCAN("Private_Clean:", private_clean)
215 } 225 SCAN("Shared_Dirty:" , shared_dirty )
216 SCAN("Private_Dirty:", private_dirty); 226 SCAN("Shared_Clean:" , shared_clean )
217 SCAN("Private_Clean:", private_clean);
218 SCAN("Shared_Dirty:" , shared_dirty );
219 SCAN("Shared_Clean:" , shared_clean );
220#undef SCAN 227#undef SCAN
228 }
221 tp = strchr(buf, '-'); 229 tp = strchr(buf, '-');
222 if (tp) { 230 if (tp) {
223 // We reached next mapping - the line of this form: 231 // We reached next mapping - the line of this form:
224 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME 232 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME
225 233
226 if (cb) { 234 char *rwx;
227 /* If we have a previous record, there's nothing more 235 unsigned long sz;
228 * for it, call the callback and clear currec
229 */
230 if (currec.smap_size)
231 cb(&currec, data);
232 free(currec.smap_name);
233 }
234 memset(&currec, 0, sizeof(currec));
235 236
236 *tp = ' '; 237 *tp = ' ';
237 tp = buf; 238 tp = buf;
238 currec.smap_start = fast_strtoull_16(&tp); 239 sz = fast_strtoull_16(&tp); // start
239 currec.smap_size = (fast_strtoull_16(&tp) - currec.smap_start) >> 10; 240 sz = (fast_strtoull_16(&tp) - sz) >> 10; // end - start
240 241 // tp -> "rw-s" string
241 strncpy(currec.smap_mode, tp, sizeof(currec.smap_mode)-1); 242 rwx = tp;
242
243 // skipping "rw-s FILEOFS M:m INODE " 243 // skipping "rw-s FILEOFS M:m INODE "
244 tp = skip_whitespace(skip_fields(tp, 4)); 244 tp = skip_whitespace(skip_fields(tp, 4));
245 // filter out /dev/something (something != zero) 245 // if not a device memory mapped...
246 if (!is_prefixed_with(tp, "/dev/") || strcmp(tp, "/dev/zero\n") == 0) { 246 if (memcmp(tp, "/dev/", 5) != 0 // not "/dev/something"
247 if (currec.smap_mode[1] == 'w') { 247 || strcmp(tp + 5, "zero\n") == 0 // or is "/dev/zero" (which isn't a device)
248 currec.mapped_rw = currec.smap_size; 248 ) {
249 total->mapped_rw += currec.smap_size; 249 if (rwx[1] == 'w')
250 } else if (currec.smap_mode[1] == '-') { 250 sp->mapped_rw += sz;
251 currec.mapped_ro = currec.smap_size; 251 else if (rwx[0] == 'r' || rwx[2] == 'x')
252 total->mapped_ro += currec.smap_size; 252 sp->mapped_ro += sz;
253 } 253 // else: seen "---p" mappings (mmap guard gaps?),
254 // do NOT account these as VSZ, they aren't really
254 } 255 }
255
256 if (strcmp(tp, "[stack]\n") == 0) 256 if (strcmp(tp, "[stack]\n") == 0)
257 total->stack += currec.smap_size; 257 sp->stack += sz;
258 if (cb) {
259 p = skip_non_whitespace(tp);
260 if (p == tp) {
261 currec.smap_name = xstrdup(" [ anon ]");
262 } else {
263 *p = '\0';
264 currec.smap_name = xstrdup(tp);
265 }
266 }
267 total->smap_size += currec.smap_size;
268 } 258 }
269 } 259 }
270 fclose(file); 260 fclose(file);
271
272 if (cb) {
273 if (currec.smap_size)
274 cb(&currec, data);
275 free(currec.smap_name);
276 }
277
278 return 0;
279} 261}
280#endif 262#endif
281 263
@@ -370,7 +352,8 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
370 char *cp, *comm1; 352 char *cp, *comm1;
371 int tty; 353 int tty;
372#if !ENABLE_FEATURE_FAST_TOP 354#if !ENABLE_FEATURE_FAST_TOP
373 unsigned long vsz, rss; 355 unsigned long long vsz;
356 unsigned long rss;
374#endif 357#endif
375 /* see proc(5) for some details on this */ 358 /* see proc(5) for some details on this */
376 strcpy(filename_tail, "stat"); 359 strcpy(filename_tail, "stat");
@@ -396,7 +379,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
396 "%ld " /* nice */ 379 "%ld " /* nice */
397 "%*s %*s " /* timeout, it_real_value */ 380 "%*s %*s " /* timeout, it_real_value */
398 "%lu " /* start_time */ 381 "%lu " /* start_time */
399 "%lu " /* vsize */ 382 "%llu " /* vsize - can be very large */
400 "%lu " /* rss */ 383 "%lu " /* rss */
401# if ENABLE_FEATURE_TOP_SMP_PROCESS 384# if ENABLE_FEATURE_TOP_SMP_PROCESS
402 "%*s %*s %*s %*s %*s %*s " /*rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */ 385 "%*s %*s %*s %*s %*s %*s " /*rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */
@@ -449,7 +432,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
449 cp = skip_fields(cp, 2); /* timeout, it_real_value */ 432 cp = skip_fields(cp, 2); /* timeout, it_real_value */
450 sp->start_time = fast_strtoul_10(&cp); 433 sp->start_time = fast_strtoul_10(&cp);
451 /* vsz is in bytes and we want kb */ 434 /* vsz is in bytes and we want kb */
452 sp->vsz = fast_strtoul_10(&cp) >> 10; 435 sp->vsz = fast_strtoull_10(&cp) >> 10;
453 /* vsz is in bytes but rss is in *PAGES*! Can you believe that? */ 436 /* vsz is in bytes but rss is in *PAGES*! Can you believe that? */
454 sp->rss = fast_strtoul_10(&cp) << sp->shift_pages_to_kb; 437 sp->rss = fast_strtoul_10(&cp) << sp->shift_pages_to_kb;
455# if ENABLE_FEATURE_TOP_SMP_PROCESS 438# if ENABLE_FEATURE_TOP_SMP_PROCESS
@@ -483,7 +466,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
483 466
484#if ENABLE_FEATURE_TOPMEM 467#if ENABLE_FEATURE_TOPMEM
485 if (flags & PSSCAN_SMAPS) 468 if (flags & PSSCAN_SMAPS)
486 procps_read_smaps(pid, &sp->smaps, NULL, NULL); 469 procps_read_smaps(pid, sp);
487#endif /* TOPMEM */ 470#endif /* TOPMEM */
488#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS 471#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
489 if (flags & PSSCAN_RUIDGID) { 472 if (flags & PSSCAN_RUIDGID) {
@@ -566,36 +549,45 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
566 return sp; 549 return sp;
567} 550}
568 551
569void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) 552int FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
570{ 553{
571 int sz; 554 int sz;
572 char filename[sizeof("/proc/%u/cmdline") + sizeof(int)*3]; 555 char filename[sizeof("/proc/%u/cmdline") + sizeof(int)*3];
573 556
574 sprintf(filename, "/proc/%u/cmdline", pid); 557 sprintf(filename, "/proc/%u/cmdline", pid);
575 sz = open_read_close(filename, buf, col - 1); 558 sz = open_read_close(filename, buf, col - 1);
559 if (sz < 0)
560 return sz;
576 if (sz > 0) { 561 if (sz > 0) {
577 const char *base; 562 const char *program_basename;
578 int comm_len; 563 int comm_len;
579 564
580 buf[sz] = '\0'; 565 buf[sz] = '\0';
581 while (--sz >= 0 && buf[sz] == '\0') 566 while (--sz >= 0 && buf[sz] == '\0')
582 continue; 567 continue;
583 /* Prevent basename("process foo/bar") = "bar" */ 568
584 strchrnul(buf, ' ')[0] = '\0'; 569 /* Find "program" in "[-][/PATH/TO/]program" */
585 base = bb_basename(buf); /* before we replace argv0's NUL with space */ 570 strchrnul(buf, ' ')[0] = '\0'; /* prevent basename("program foo/bar") = "bar" */
571 program_basename = bb_basename(buf[0] == '-' ? buf + 1 : buf);
572 /* ^^^ note: must do it *before* replacing argv0's NUL with space */
573
574 /* Prevent stuff like this:
575 * echo 'sleep 999; exit' >`printf '\ec'`; sh ?c
576 * messing up top and ps output (or worse).
577 * This also replaces NULs with spaces, converting
578 * list of NUL-strings into one string.
579 */
586 while (sz >= 0) { 580 while (sz >= 0) {
587 if ((unsigned char)(buf[sz]) < ' ') 581 if ((unsigned char)(buf[sz]) < ' ')
588 buf[sz] = ' '; 582 buf[sz] = ' ';
589 sz--; 583 sz--;
590 } 584 }
591 if (base[0] == '-') /* "-sh" (login shell)? */
592 base++;
593 585
594 /* If comm differs from argv0, prepend "{comm} ". 586 /* If comm differs from argv0, prepend "{comm} ".
595 * It allows to see thread names set by prctl(PR_SET_NAME). 587 * It allows to see thread names set by prctl(PR_SET_NAME).
596 */ 588 */
597 if (!comm) 589 if (!comm)
598 return; 590 return 0;
599 comm_len = strlen(comm); 591 comm_len = strlen(comm);
600 /* Why compare up to comm_len, not COMM_LEN-1? 592 /* Why compare up to comm_len, not COMM_LEN-1?
601 * Well, some processes rewrite argv, and use _spaces_ there 593 * Well, some processes rewrite argv, and use _spaces_ there
@@ -603,21 +595,24 @@ void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
603 * I prefer to still treat argv0 "process foo bar" 595 * I prefer to still treat argv0 "process foo bar"
604 * as 'equal' to comm "process". 596 * as 'equal' to comm "process".
605 */ 597 */
606 if (strncmp(base, comm, comm_len) != 0) { 598 if (strncmp(program_basename, comm, comm_len) != 0) {
607 comm_len += 3; 599 comm_len += 3;
608 if (col > comm_len) 600 if (col > comm_len)
609 memmove(buf + comm_len, buf, col - comm_len); 601 memmove(buf + comm_len, buf, col - comm_len);
610 snprintf(buf, col, "{%s}", comm); 602 snprintf(buf, col, "{%s}", comm);
611 if (col <= comm_len) 603 if (col <= comm_len)
612 return; 604 return 0;
613 buf[comm_len - 1] = ' '; 605 buf[comm_len - 1] = ' ';
614 buf[col - 1] = '\0'; 606 buf[col - 1] = '\0';
615 } 607 }
616 } else { 608 } else {
617 snprintf(buf, col, "[%s]", comm ? comm : "?"); 609 snprintf(buf, col, "[%s]", comm ? comm : "?");
618 } 610 }
611 return 0;
619} 612}
620 613
614#endif /* ENABLE_PLATFORM_MINGW32 */
615
621/* from kernel: 616/* from kernel:
622 // pid comm S ppid pgid sid tty_nr tty_pgrp flg 617 // pid comm S ppid pgid sid tty_nr tty_pgrp flg
623 sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \ 618 sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \