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