diff options
| author | Denys Vlasenko <vda.linux@googlemail.com> | 2025-08-05 14:04:01 +0200 |
|---|---|---|
| committer | Denys Vlasenko <vda.linux@googlemail.com> | 2025-08-05 14:21:51 +0200 |
| commit | d16bde623ced3cf113648e0bb977c1befed6d601 (patch) | |
| tree | 08f688dc57a6efbc9e82f6eaa7587849fee68f2c /procps | |
| parent | 58b2353baaebdfdfd166e0745aea0fe75d1a3d10 (diff) | |
| download | busybox-w32-d16bde623ced3cf113648e0bb977c1befed6d601.tar.gz busybox-w32-d16bde623ced3cf113648e0bb977c1befed6d601.tar.bz2 busybox-w32-d16bde623ced3cf113648e0bb977c1befed6d601.zip | |
top,pmap: do not use common code for reading /proc/PID/smaps
The logic is in fact quite far from common.
While at it, stop accounting "---p" mappings as mapped (e.g. VSZ in top).
Nothing is mapped there (why would kernel waste RAM to map pages
which can't be accessed?).
function old new delta
read_smaps - 562 +562
read_cmdline 315 326 +11
print_smaprec 97 101 +4
procps_scan 1219 1211 -8
.rodata 115541 115533 -8
skip_whitespace_if_prefixed_with 25 - -25
procps_read_smaps 864 577 -287
------------------------------------------------------------------------------
(add/remove: 1/1 grow/shrink: 2/3 up/down: 577/-328) Total: 249 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'procps')
| -rw-r--r-- | procps/pmap.c | 122 | ||||
| -rw-r--r-- | procps/top.c | 16 |
2 files changed, 118 insertions, 20 deletions
diff --git a/procps/pmap.c b/procps/pmap.c index 49f7688d9..de58f3304 100644 --- a/procps/pmap.c +++ b/procps/pmap.c | |||
| @@ -29,10 +29,14 @@ | |||
| 29 | 29 | ||
| 30 | #if ULLONG_MAX == 0xffffffff | 30 | #if ULLONG_MAX == 0xffffffff |
| 31 | # define TABS "\t" | 31 | # define TABS "\t" |
| 32 | # define SIZEWIDTHx "7" | ||
| 33 | # define SIZEWIDTH "9" | ||
| 32 | # define AFMTLL "8" | 34 | # define AFMTLL "8" |
| 33 | # define DASHES "" | 35 | # define DASHES "" |
| 34 | #else | 36 | #else |
| 35 | # define TABS "\t\t" | 37 | # define TABS "\t\t" |
| 38 | # define SIZEWIDTHx "15" | ||
| 39 | # define SIZEWIDTH "17" | ||
| 36 | # define AFMTLL "16" | 40 | # define AFMTLL "16" |
| 37 | # define DASHES "--------" | 41 | # define DASHES "--------" |
| 38 | #endif | 42 | #endif |
| @@ -42,22 +46,114 @@ enum { | |||
| 42 | OPT_q = 1 << 1, | 46 | OPT_q = 1 << 1, |
| 43 | }; | 47 | }; |
| 44 | 48 | ||
| 45 | static void print_smaprec(struct smaprec *currec, void *data) | 49 | struct smaprec { |
| 46 | { | 50 | // For mixed 32/64 userspace, 32-bit pmap still needs |
| 47 | unsigned opt = (uintptr_t)data; | 51 | // 64-bit field here to correctly show 64-bit processes: |
| 52 | unsigned long long smap_start; | ||
| 53 | // Make size wider too: | ||
| 54 | // I've seen 1203765248 kb large "---p" mapping in a browser, | ||
| 55 | // this cuts close to 4 terabytes. | ||
| 56 | unsigned long long smap_size; | ||
| 57 | // (strictly speaking, other fields need to be wider too, | ||
| 58 | // but they are in kbytes, not bytes, and they hold sizes, | ||
| 59 | // not start addresses, sizes tend to be less than 4 terabytes) | ||
| 60 | unsigned long private_dirty; | ||
| 61 | unsigned long smap_pss, smap_swap; | ||
| 62 | char smap_mode[5]; | ||
| 63 | char *smap_name; | ||
| 64 | }; | ||
| 48 | 65 | ||
| 66 | static void print_smaprec(struct smaprec *currec) | ||
| 67 | { | ||
| 49 | printf("%0" AFMTLL "llx ", currec->smap_start); | 68 | printf("%0" AFMTLL "llx ", currec->smap_start); |
| 50 | 69 | ||
| 51 | if (opt & OPT_x) | 70 | if (option_mask32 & OPT_x) |
| 52 | printf("%7lu %7lu %7lu %7lu ", | 71 | printf("%7llu %7lu %7lu %7lu ", |
| 53 | currec->smap_size, | 72 | currec->smap_size, |
| 54 | currec->smap_pss, | 73 | currec->smap_pss, |
| 55 | currec->private_dirty, | 74 | currec->private_dirty, |
| 56 | currec->smap_swap); | 75 | currec->smap_swap); |
| 57 | else | 76 | else |
| 58 | printf("%7luK", currec->smap_size); | 77 | printf("%7lluK", currec->smap_size); |
| 78 | |||
| 79 | printf(" %.4s %s\n", currec->smap_mode, currec->smap_name ? : " [ anon ]"); | ||
| 80 | } | ||
| 81 | |||
| 82 | /* libbb's procps_read_smaps() looks somewhat similar, | ||
| 83 | * but the collected information is sufficiently different | ||
| 84 | * that merging them into one function is not a good idea | ||
| 85 | * (unless you feel masochistic today). | ||
| 86 | */ | ||
| 87 | static int read_smaps(pid_t pid, struct smaprec *total) | ||
| 88 | { | ||
| 89 | FILE *file; | ||
| 90 | struct smaprec currec; | ||
| 91 | char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3]; | ||
| 92 | char buf[4096]; // how long the max filenames we expect? | ||
| 93 | |||
| 94 | sprintf(filename, "/proc/%u/smaps", (int)pid); | ||
| 95 | |||
| 96 | file = fopen_for_read(filename); | ||
| 97 | if (!file) | ||
| 98 | return 1; | ||
| 99 | |||
| 100 | memset(&currec, 0, sizeof(currec)); | ||
| 101 | while (fgets(buf, sizeof(buf), file)) { | ||
| 102 | // Each mapping datum has this form: | ||
| 103 | // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME | ||
| 104 | // Size: nnn kB | ||
| 105 | // Rss: nnn kB | ||
| 106 | // ..... | ||
| 107 | |||
| 108 | char *tp, *p; | ||
| 109 | |||
| 110 | if (buf[0] == 'S' || buf[0] == 'P') { | ||
| 111 | #define SCAN(S, X) \ | ||
| 112 | if (memcmp(buf, S, sizeof(S)-1) == 0) { \ | ||
| 113 | tp = skip_whitespace(buf + sizeof(S)-1); \ | ||
| 114 | total->X += currec.X = fast_strtoul_10(&tp); \ | ||
| 115 | continue; \ | ||
| 116 | } | ||
| 117 | SCAN("Pss:" , smap_pss ); | ||
| 118 | SCAN("Swap:" , smap_swap ); | ||
| 119 | SCAN("Private_Dirty:", private_dirty); | ||
| 120 | #undef SCAN | ||
| 121 | } | ||
| 122 | tp = strchr(buf, '-'); | ||
| 123 | if (tp) { | ||
| 124 | // We reached next mapping - the line of this form: | ||
| 125 | // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME | ||
| 126 | |||
| 127 | // If we have a previous record, there's nothing more | ||
| 128 | // for it, print and clear currec | ||
| 129 | if (currec.smap_size) | ||
| 130 | print_smaprec(&currec); | ||
| 131 | free(currec.smap_name); | ||
| 132 | memset(&currec, 0, sizeof(currec)); | ||
| 133 | |||
| 134 | *tp = ' '; | ||
| 135 | tp = buf; | ||
| 136 | currec.smap_start = fast_strtoull_16(&tp); | ||
| 137 | currec.smap_size = (fast_strtoull_16(&tp) - currec.smap_start) >> 10; | ||
| 138 | strncpy(currec.smap_mode, tp, sizeof(currec.smap_mode)-1); | ||
| 139 | |||
| 140 | // skipping "rw-s FILEOFS M:m INODE " | ||
| 141 | tp = skip_fields(tp, 4); | ||
| 142 | tp = skip_whitespace(tp); // there may be many spaces, can't just "tp++" | ||
| 143 | p = strchrnul(tp, '\n'); | ||
| 144 | if (p != tp) { | ||
| 145 | currec.smap_name = xstrndup(tp, p - tp); | ||
| 146 | } | ||
| 147 | total->smap_size += currec.smap_size; | ||
| 148 | } | ||
| 149 | } // while (got line) | ||
| 150 | fclose(file); | ||
| 151 | |||
| 152 | if (currec.smap_size) | ||
| 153 | print_smaprec(&currec); | ||
| 154 | free(currec.smap_name); | ||
| 59 | 155 | ||
| 60 | printf(" %.4s %s\n", currec->smap_mode, currec->smap_name); | 156 | return 0; |
| 61 | } | 157 | } |
| 62 | 158 | ||
| 63 | static int procps_get_maps(pid_t pid, unsigned opt) | 159 | static int procps_get_maps(pid_t pid, unsigned opt) |
| @@ -66,25 +162,27 @@ static int procps_get_maps(pid_t pid, unsigned opt) | |||
| 66 | int ret; | 162 | int ret; |
| 67 | char buf[256]; | 163 | char buf[256]; |
| 68 | 164 | ||
| 69 | read_cmdline(buf, sizeof(buf), pid, NULL); | 165 | ret = read_cmdline(buf, sizeof(buf), pid, NULL); |
| 166 | if (ret < 0) | ||
| 167 | return ret; | ||
| 168 | |||
| 70 | printf("%u: %s\n", (int)pid, buf); | 169 | printf("%u: %s\n", (int)pid, buf); |
| 71 | 170 | ||
| 72 | if (!(opt & OPT_q) && (opt & OPT_x)) | 171 | if (!(opt & OPT_q) && (opt & OPT_x)) |
| 73 | puts("Address" TABS " Kbytes PSS Dirty Swap Mode Mapping"); | 172 | puts("Address" TABS " Kbytes PSS Dirty Swap Mode Mapping"); |
| 74 | 173 | ||
| 75 | memset(&total, 0, sizeof(total)); | 174 | memset(&total, 0, sizeof(total)); |
| 76 | 175 | ret = read_smaps(pid, &total); | |
| 77 | ret = procps_read_smaps(pid, &total, print_smaprec, (void*)(uintptr_t)opt); | ||
| 78 | if (ret) | 176 | if (ret) |
| 79 | return ret; | 177 | return ret; |
| 80 | 178 | ||
| 81 | if (!(opt & OPT_q)) { | 179 | if (!(opt & OPT_q)) { |
| 82 | if (opt & OPT_x) | 180 | if (opt & OPT_x) |
| 83 | printf("--------" DASHES " ------ ------ ------ ------\n" | 181 | printf("--------" DASHES " ------ ------ ------ ------\n" |
| 84 | "total" TABS " %7lu %7lu %7lu %7lu\n", | 182 | "total kB %"SIZEWIDTHx"llu %7lu %7lu %7lu\n", |
| 85 | total.smap_size, total.smap_pss, total.private_dirty, total.smap_swap); | 183 | total.smap_size, total.smap_pss, total.private_dirty, total.smap_swap); |
| 86 | else | 184 | else |
| 87 | printf("mapped: %luK\n", total.smap_size); | 185 | printf(" total %"SIZEWIDTH"lluK\n", total.smap_size); |
| 88 | } | 186 | } |
| 89 | 187 | ||
| 90 | return 0; | 188 | return 0; |
diff --git a/procps/top.c b/procps/top.c index a87fa0ae4..e0ebf7026 100644 --- a/procps/top.c +++ b/procps/top.c | |||
| @@ -1302,20 +1302,20 @@ int top_main(int argc UNUSED_PARAM, char **argv) | |||
| 1302 | } | 1302 | } |
| 1303 | #if ENABLE_FEATURE_TOPMEM | 1303 | #if ENABLE_FEATURE_TOPMEM |
| 1304 | else { /* TOPMEM */ | 1304 | else { /* TOPMEM */ |
| 1305 | if (!(p->smaps.mapped_ro | p->smaps.mapped_rw)) | 1305 | if (!(p->mapped_ro | p->mapped_rw)) |
| 1306 | continue; /* kernel threads are ignored */ | 1306 | continue; /* kernel threads are ignored */ |
| 1307 | n = ntop; | 1307 | n = ntop; |
| 1308 | /* No bug here - top and topmem are the same */ | 1308 | /* No bug here - top and topmem are the same */ |
| 1309 | top = xrealloc_vector(topmem, 6, ntop++); | 1309 | top = xrealloc_vector(topmem, 6, ntop++); |
| 1310 | strcpy(topmem[n].comm, p->comm); | 1310 | strcpy(topmem[n].comm, p->comm); |
| 1311 | topmem[n].pid = p->pid; | 1311 | topmem[n].pid = p->pid; |
| 1312 | topmem[n].vsz = p->smaps.mapped_rw + p->smaps.mapped_ro; | 1312 | topmem[n].vsz = p->mapped_rw + p->mapped_ro; |
| 1313 | topmem[n].vszrw = p->smaps.mapped_rw; | 1313 | topmem[n].vszrw = p->mapped_rw; |
| 1314 | topmem[n].rss_sh = p->smaps.shared_clean + p->smaps.shared_dirty; | 1314 | topmem[n].rss_sh = p->shared_clean + p->shared_dirty; |
| 1315 | topmem[n].rss = p->smaps.private_clean + p->smaps.private_dirty + topmem[n].rss_sh; | 1315 | topmem[n].rss = p->private_clean + p->private_dirty + topmem[n].rss_sh; |
| 1316 | topmem[n].dirty = p->smaps.private_dirty + p->smaps.shared_dirty; | 1316 | topmem[n].dirty = p->private_dirty + p->shared_dirty; |
| 1317 | topmem[n].dirty_sh = p->smaps.shared_dirty; | 1317 | topmem[n].dirty_sh = p->shared_dirty; |
| 1318 | topmem[n].stack = p->smaps.stack; | 1318 | topmem[n].stack = p->stack; |
| 1319 | } | 1319 | } |
| 1320 | #endif | 1320 | #endif |
| 1321 | } /* end of "while we read /proc" */ | 1321 | } /* end of "while we read /proc" */ |
