mirror of
https://git.busybox.net/busybox
synced 2025-12-13 05:21:05 +00:00
function old new delta procps_read_smaps 515 529 +14 procps_get_maps 685 665 -20 .rodata 105847 105820 -27 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/2 up/down: 14/-47) Total: -33 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
226 lines
5.6 KiB
C
226 lines
5.6 KiB
C
/*
|
|
* pmap implementation for busybox
|
|
*
|
|
* Copyright (C) 2010 Nokia Corporation. All rights reserved.
|
|
* Written by Alexander Shishkin <virtuoso@slind.org>
|
|
*
|
|
* Licensed under GPLv2 or later, see the LICENSE file in this source tree
|
|
* for details.
|
|
*/
|
|
//config:config PMAP
|
|
//config: bool "pmap (6.2 kb)"
|
|
//config: default y
|
|
//config: help
|
|
//config: Display processes' memory mappings.
|
|
|
|
//applet:IF_PMAP(APPLET(pmap, BB_DIR_USR_BIN, BB_SUID_DROP))
|
|
|
|
//kbuild:lib-$(CONFIG_PMAP) += pmap.o
|
|
|
|
//usage:#define pmap_trivial_usage
|
|
//usage: "[-xq] PID..."
|
|
//usage:#define pmap_full_usage "\n\n"
|
|
//usage: "Display process memory usage"
|
|
//usage: "\n"
|
|
//usage: "\n -x Show details"
|
|
//usage: "\n -q Quiet"
|
|
|
|
#include "libbb.h"
|
|
|
|
#if ULLONG_MAX == 0xffffffff
|
|
# define TABS "\t"
|
|
# define SIZEWIDTHx "7"
|
|
# define SIZEWIDTH "9"
|
|
# define AFMTLL "8"
|
|
# define DASHES ""
|
|
#else
|
|
# define TABS "\t\t"
|
|
# define SIZEWIDTHx "15"
|
|
# define SIZEWIDTH "17"
|
|
# define AFMTLL "16"
|
|
# define DASHES "--------"
|
|
#endif
|
|
|
|
enum {
|
|
OPT_x = 1 << 0,
|
|
OPT_q = 1 << 1,
|
|
};
|
|
|
|
struct smaprec {
|
|
// For mixed 32/64 userspace, 32-bit pmap still needs
|
|
// 64-bit field here to correctly show 64-bit processes:
|
|
unsigned long long smap_start;
|
|
// Make size wider too:
|
|
// I've seen 1203765248 kb large "---p" mapping in a browser,
|
|
// this cuts close to 4 terabytes.
|
|
unsigned long long smap_size;
|
|
// (strictly speaking, other fields need to be wider too,
|
|
// but they are in kbytes, not bytes, and they hold sizes,
|
|
// not start addresses, sizes tend to be less than 4 terabytes)
|
|
unsigned long private_dirty;
|
|
unsigned long smap_pss, smap_swap;
|
|
char smap_mode[5];
|
|
char *smap_name;
|
|
};
|
|
|
|
// How long the filenames and command lines we want to handle?
|
|
#define PMAP_BUFSZ 4096
|
|
|
|
static void print_smaprec(struct smaprec *currec)
|
|
{
|
|
printf("%0" AFMTLL "llx ", currec->smap_start);
|
|
|
|
if (option_mask32 & OPT_x)
|
|
printf("%7llu %7lu %7lu %7lu ",
|
|
currec->smap_size,
|
|
currec->smap_pss,
|
|
currec->private_dirty,
|
|
currec->smap_swap);
|
|
else
|
|
printf("%7lluK", currec->smap_size);
|
|
|
|
printf(" %.4s %s\n", currec->smap_mode, currec->smap_name ? : " [ anon ]");
|
|
}
|
|
|
|
/* libbb's procps_read_smaps() looks somewhat similar,
|
|
* but the collected information is sufficiently different
|
|
* that merging them into one function is not a good idea
|
|
* (unless you feel masochistic today).
|
|
*/
|
|
static int read_smaps(pid_t pid, char buf[PMAP_BUFSZ], struct smaprec *total)
|
|
{
|
|
FILE *file;
|
|
struct smaprec currec;
|
|
char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3];
|
|
|
|
sprintf(filename, "/proc/%u/smaps", (int)pid);
|
|
|
|
file = fopen_for_read(filename);
|
|
if (!file)
|
|
return 1;
|
|
|
|
memset(&currec, 0, sizeof(currec));
|
|
while (fgets(buf, PMAP_BUFSZ, file)) {
|
|
// Each mapping datum has this form:
|
|
// f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME
|
|
// Size: nnn kB
|
|
// Rss: nnn kB
|
|
// .....
|
|
|
|
char *tp, *p;
|
|
|
|
#define bytes4 *(uint32_t*)buf
|
|
#define Pss_ PACK32_BYTES('P','s','s',':')
|
|
#define Swap PACK32_BYTES('S','w','a','p')
|
|
#define Priv PACK32_BYTES('P','r','i','v')
|
|
#define FETCH(X, N) \
|
|
tp = skip_whitespace(buf+4 + (N)); \
|
|
total->X += currec.X = fast_strtoul_10(&tp); \
|
|
continue
|
|
#define SCAN(S, X) \
|
|
if (memcmp(buf+4, S, sizeof(S)-1) == 0) { \
|
|
FETCH(X, sizeof(S)-1); \
|
|
}
|
|
if (bytes4 == Pss_) {
|
|
FETCH(smap_pss, 0);
|
|
}
|
|
if (bytes4 == Swap && buf[4] == ':') {
|
|
FETCH(smap_swap, 1);
|
|
}
|
|
if (bytes4 == Priv) {
|
|
SCAN("ate_Dirty:", private_dirty);
|
|
}
|
|
#undef bytes4
|
|
#undef Pss_
|
|
#undef Swap
|
|
#undef Priv
|
|
#undef FETCH
|
|
#undef SCAN
|
|
tp = strchr(buf, '-');
|
|
if (tp) {
|
|
// We reached next mapping - the line of this form:
|
|
// f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME
|
|
|
|
// If we have a previous record, there's nothing more
|
|
// for it, print and clear currec
|
|
if (currec.smap_size)
|
|
print_smaprec(&currec);
|
|
free(currec.smap_name);
|
|
memset(&currec, 0, sizeof(currec));
|
|
|
|
*tp = ' ';
|
|
tp = buf;
|
|
currec.smap_start = fast_strtoull_16(&tp);
|
|
currec.smap_size = (fast_strtoull_16(&tp) - currec.smap_start) >> 10;
|
|
strncpy(currec.smap_mode, tp, sizeof(currec.smap_mode)-1);
|
|
|
|
// skipping "rw-s FILEOFS M:m INODE "
|
|
tp = skip_fields(tp, 4);
|
|
tp = skip_whitespace(tp); // there may be many spaces, can't just "tp++"
|
|
p = strchrnul(tp, '\n');
|
|
if (p != tp) {
|
|
currec.smap_name = xstrndup(tp, p - tp);
|
|
}
|
|
total->smap_size += currec.smap_size;
|
|
}
|
|
} // while (got line)
|
|
fclose(file);
|
|
|
|
if (currec.smap_size)
|
|
print_smaprec(&currec);
|
|
free(currec.smap_name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int procps_get_maps(pid_t pid, unsigned opt)
|
|
{
|
|
struct smaprec total;
|
|
int ret;
|
|
char buf[PMAP_BUFSZ] ALIGN4;
|
|
|
|
ret = read_cmdline(buf, sizeof(buf), pid, NULL);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
printf("%u: %s\n", (int)pid, buf);
|
|
|
|
if (!(opt & OPT_q) && (opt & OPT_x))
|
|
puts("Address" TABS " Kbytes PSS Dirty Swap Mode Mapping");
|
|
|
|
memset(&total, 0, sizeof(total));
|
|
ret = read_smaps(pid, buf, &total);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (!(opt & OPT_q)) {
|
|
if (opt & OPT_x)
|
|
printf("--------" DASHES " ------ ------ ------ ------\n"
|
|
"total kB %"SIZEWIDTHx"llu %7lu %7lu %7lu\n",
|
|
total.smap_size, total.smap_pss, total.private_dirty, total.smap_swap);
|
|
else
|
|
printf(" total %"SIZEWIDTH"lluK\n", total.smap_size);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int pmap_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
|
|
int pmap_main(int argc UNUSED_PARAM, char **argv)
|
|
{
|
|
unsigned opts;
|
|
int ret;
|
|
|
|
opts = getopt32(argv, "^" "xq" "\0" "-1"); /* min one arg */
|
|
argv += optind;
|
|
|
|
ret = 0;
|
|
while (*argv) {
|
|
pid_t pid = xatoi_positive(*argv++);
|
|
/* GNU pmap returns 42 if any of the pids failed */
|
|
if (procps_get_maps(pid, opts) != 0)
|
|
ret = 42;
|
|
}
|
|
|
|
return ret;
|
|
}
|