From dcbb69e0a5cce9058afc5a96b9862982ae17cb39 Mon Sep 17 00:00:00 2001 From: Andy Knowles Date: Wed, 30 Jul 2025 18:29:48 +0200 Subject: sha384sum: new applet function old new delta sha512384_end - 198 +198 packed_usage 35021 35134 +113 init384 - 80 +80 sha384_begin - 19 +19 sha384_end - 10 +10 applet_names 2823 2833 +10 md5_sha1_sum_main 501 507 +6 sha3_end 54 59 +5 applet_main 1628 1632 +4 show_usage_if_dash_dash_help 79 72 -7 hash_file 358 344 -14 sha512_end 197 10 -187 ------------------------------------------------------------------------------ (add/remove: 4/0 grow/shrink: 5/3 up/down: 445/-208) Total: 237 bytes Signed-off-by: Andy Knowles Signed-off-by: Denys Vlasenko --- include/libbb.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/libbb.h b/include/libbb.h index 4f44680aa..7105bd479 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -2209,8 +2209,9 @@ enum { MD5_OUTSIZE = 16, SHA1_OUTSIZE = 20, SHA256_OUTSIZE = 32, + SHA384_OUTSIZE = 48, SHA512_OUTSIZE = 64, - SHA3_OUTSIZE = 28, + //SHA3-224_OUTSIZE = 28, /* size of input block */ SHA2_INSIZE = 64, }; @@ -2227,6 +2228,7 @@ typedef struct sha512_ctx_t { uint64_t hash[8]; uint8_t wbuffer[128]; /* always correctly aligned for uint64_t */ } sha512_ctx_t; +typedef struct sha512_ctx_t sha384_ctx_t; typedef struct sha3_ctx_t { uint64_t state[25]; unsigned bytes_queued; @@ -2244,6 +2246,9 @@ void sha256_begin(sha256_ctx_t *ctx) FAST_FUNC; void sha512_begin(sha512_ctx_t *ctx) FAST_FUNC; void sha512_hash(sha512_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC; unsigned sha512_end(sha512_ctx_t *ctx, void *resbuf) FAST_FUNC; +void sha384_begin(sha384_ctx_t *ctx) FAST_FUNC; +#define sha384_hash sha512_hash +unsigned sha384_end(sha384_ctx_t *ctx, void *resbuf) FAST_FUNC; void sha3_begin(sha3_ctx_t *ctx) FAST_FUNC; void sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC; unsigned sha3_end(sha3_ctx_t *ctx, void *resbuf) FAST_FUNC; -- cgit v1.2.3-55-g6feb From 4f3a56dc12a2126bdacff73f1cfe586d06e800c0 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 1 Aug 2025 01:02:43 +0200 Subject: ls: fix -Q to match GNU function old new delta print_name 137 229 +92 display_files 375 402 +27 c_escape_conv_str00 - 24 +24 display 1476 1485 +9 conv_str 33 - -33 ------------------------------------------------------------------------------ (add/remove: 2/1 grow/shrink: 3/0 up/down: 152/-33) Total: 119 bytes Signed-off-by: Denys Vlasenko --- coreutils/ls.c | 50 +++++++++++++++++++++++++++++++++++++------------ include/libbb.h | 3 +++ libbb/c_escape.c | 20 ++++++++++++++++++++ libbb/dump.c | 39 +++++++++++++++++++------------------- testsuite/hexdump.tests | 10 ++++++++++ testsuite/ls.tests | 26 +++++++++++++++++++++++-- 6 files changed, 114 insertions(+), 34 deletions(-) create mode 100644 libbb/c_escape.c (limited to 'include') diff --git a/coreutils/ls.c b/coreutils/ls.c index c725be92d..9e4b83032 100644 --- a/coreutils/ls.c +++ b/coreutils/ls.c @@ -457,19 +457,29 @@ static unsigned calc_name_len(const char *name) if (!(option_mask32 & (OPT_q|OPT_Q))) return strlen(name); - name = printable_string2(&uni_stat, name); - if (!(option_mask32 & OPT_Q)) { + printable_string2(&uni_stat, name); return uni_stat.unicode_width; } - // TODO: quote chars 7..13 as \a,b,t,n,v,f,r - // other chars <32 or >127 as \ooo octal - len = 2 + uni_stat.unicode_width; + len = 2 + strlen(name); while (*name) { + unsigned char ch = (unsigned char)*name; + if (ch < ' ' || ch > 0x7e) { + ch -= 7; + if ((signed char)ch >= 0 && ch <= 6) { + // quote chars 7..13 as \a,b,t,n,v,f,r + len++; + goto next; + } + // other chars <32 or >126 as \ooo octal + len += 3; + goto next; + } if (*name == '"' || *name == '\\') { len++; } + next: name++; } return len; @@ -492,23 +502,39 @@ static unsigned print_name(const char *name) return strlen(name); } - name = printable_string2(&uni_stat, name); - if (!(option_mask32 & OPT_Q)) { + name = printable_string2(&uni_stat, name); fputs_stdout(name); return uni_stat.unicode_width; } - // TODO: quote chars 7..13 as \a,b,t,n,v,f,r - // other chars <32 or >127 as \ooo octal - len = 2 + uni_stat.unicode_width; + len = 2 + strlen(name); putchar('"'); while (*name) { - if (*name == '"' || *name == '\\') { + unsigned char ch = (unsigned char)*name; + if (ch < ' ' || ch > 0x7e) { + putchar('\\'); + ch -= 7; + if ((signed char)ch >= 0 && ch <= 6) { + // quote chars 7..13 as \a,b,t,n,v,f,r + ch = c_escape_conv_str07[1 + 3 * ch]; + len++; + goto put_ch; + } + // other chars <32 or >126 as \ooo octal + ch = (unsigned char)*name; + putchar('0' + ((ch>>6) & 7)); + putchar('0' + ((ch>>3) & 7)); + ch = '0' + (ch & 7); + len += 3; + goto put_ch; + } + if (ch == '"' || ch == '\\') { putchar('\\'); len++; } - putchar(*name); + put_ch: + putchar(ch); name++; } putchar('"'); diff --git a/include/libbb.h b/include/libbb.h index 7105bd479..cdc05049c 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -1113,6 +1113,9 @@ char *bin2hex(char *dst, const char *src, int count) FAST_FUNC; /* Reverse */ char* hex2bin(char *dst, const char *src, int count) FAST_FUNC; +extern const char c_escape_conv_str00[]; +#define c_escape_conv_str07 (c_escape_conv_str00+3) + void FAST_FUNC xorbuf_3(void *dst, const void *src1, const void *src2, unsigned count); void FAST_FUNC xorbuf(void* buf, const void* mask, unsigned count); void FAST_FUNC xorbuf16_aligned_long(void* buf, const void* mask); diff --git a/libbb/c_escape.c b/libbb/c_escape.c new file mode 100644 index 000000000..6c109f2e0 --- /dev/null +++ b/libbb/c_escape.c @@ -0,0 +1,20 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2025 by Denys Vlasenko + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +//kbuild:lib-y += c_escape.o + +#include "libbb.h" + +const char c_escape_conv_str00[] ALIGN1 = + "\\""0""\0" // [0]:00 + "\\""a""\0" // [1]:07 + "\\""b""\0" // [2]:08 + "\\""t""\0" // [3]:09 + "\\""n""\0" // [4]:0a + "\\""v""\0" // [5]:0b + "\\""f""\0" // [6]:0c + "\\""r" // [7]:0d + ; diff --git a/libbb/dump.c b/libbb/dump.c index ac5d47d9e..0cc7775d6 100644 --- a/libbb/dump.c +++ b/libbb/dump.c @@ -478,37 +478,36 @@ static void bpad(PR *pr) continue; } -static const char conv_str[] ALIGN1 = - "\0" "\\""0""\0" - "\007""\\""a""\0" - "\b" "\\""b""\0" - "\f" "\\""f""\0" - "\n" "\\""n""\0" - "\r" "\\""r""\0" - "\t" "\\""t""\0" - "\v" "\\""v""\0" - ; - static void conv_c(PR *pr, unsigned char *p) { - const char *str = conv_str; - - do { - if (*p == *str) { - ++str; - goto strpr; /* map e.g. '\n' to "\\n" */ - } - str += 4; - } while (*str); + const char *str; + unsigned char ch; + + ch = *p; + if (ch == 0 || (ch -= 6, (signed char)ch > 0 && ch <= 7)) { + /* map chars 0,7..13 to "\0","\{a,b,t,n,v,f,r}" */ + str = c_escape_conv_str00 + 3 * ch; + goto strpr; + } if (isprint_asciionly(*p)) { *pr->cchar = 'c'; printf(pr->fmt, *p); } else { +#if 1 char buf[4]; /* gcc-8.0.1 needs lots of casts to shut up */ sprintf(buf, "%03o", (unsigned)(uint8_t)*p); str = buf; +#else // use faster version? +20 bytes of code + char buf[4]; + buf[3] = '\0'; + ch = *p; + buf[2] = '0' + (ch & 7); ch >>= 3; + buf[1] = '0' + (ch & 7); ch >>= 3; + buf[0] = '0' + ch; + str = buf; +#endif strpr: *pr->cchar = 's'; printf(pr->fmt, str); diff --git a/testsuite/hexdump.tests b/testsuite/hexdump.tests index b2f6a2201..d2c0a5dc8 100755 --- a/testsuite/hexdump.tests +++ b/testsuite/hexdump.tests @@ -56,6 +56,16 @@ testing "hexdump -e %3_u" \ " \ "" "$input" +testing "hexdump -e %3_c" \ + "hexdump -e '16/1 \" %3_c\" \"\n\"'" \ +' \\0 001 002 003 004 005 006 \\a \\b \\t \\n \\v \\f \\r 016 017 + 020 021 022 023 024 025 026 027 030 031 032 033 034 035 036 037 + p q r s t u v w x y z { | } ~ 177 + 200 201 202 203 204 205 206 207 210 211 212 213 214 215 216 217 + 360 361 362 363 364 365 366 367 370 371 372 373 374 375 376 377 +' \ + "" "$input" + testing "hexdump -e /1 %d" \ "hexdump -e '16/1 \" %4d\" \"\n\"'" \ "\ diff --git a/testsuite/ls.tests b/testsuite/ls.tests index 9309d366b..a95911034 100755 --- a/testsuite/ls.tests +++ b/testsuite/ls.tests @@ -19,7 +19,7 @@ test x"$CONFIG_UNICODE_SUPPORT" = x"y" \ && test x"$CONFIG_LAST_SUPPORTED_WCHAR" = x"767" \ && test x"$CONFIG_FEATURE_LS_SORTFILES" = x"y" \ && testing "ls unicode test with codepoints limited to 767" \ -"(cd ls.testdir && sh ../ls.mk_uni_tests) && ls -1 ls.testdir" \ +"(cd ls.testdir && sh ../ls.mk_uni_tests) && ls -1q ls.testdir" \ '0001_1__Some_correct_UTF-8_text___________________________________________| 0002_2__Boundary_condition_test_cases_____________________________________| 0003_2.1__First_possible_sequence_of_a_certain_length_____________________| @@ -138,7 +138,7 @@ test x"$CONFIG_UNICODE_SUPPORT" = x"y" \ && test x"$CONFIG_SUBST_WCHAR" = x"63" \ && test x"$CONFIG_LAST_SUPPORTED_WCHAR" = x"0" \ && testing "ls unicode test with unlimited codepoints" \ -"(cd ls.testdir && sh ../ls.mk_uni_tests) && ls -1 ls.testdir" \ +"(cd ls.testdir && sh ../ls.mk_uni_tests) && ls -1q ls.testdir" \ '0001_1__Some_correct_UTF-8_text___________________________________________| 0002_2__Boundary_condition_test_cases_____________________________________| 0003_2.1__First_possible_sequence_of_a_certain_length_____________________| @@ -262,6 +262,28 @@ test x"$CONFIG_FEATURE_LS_SORTFILES" = x"y" \ "A\nB\nA\nB\nA\nB\n" \ "" "" +rm -rf ls.testdir 2>/dev/null +mkdir ls.testdir || exit 1 +touch "`printf "ls.testdir/\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f_\x7f\x80\xfe\xff_\x22_\x27_\x5c"`" + +sq="'" + +# testing "test name" "command" "expected result" "file input" "stdin" +testing "ls -q" \ +'ls -q ls.testdir' \ +'???????????????????????????????_????_"_'$sq'_\\''\n' \ +"" "" + +testing "ls -Q" \ +'ls -Q ls.testdir' \ +'"\\001\\002\\003\\004\\005\\006\\a\\b\\t\\n\\v\\f\\r\\016\\017\\020\\021\\022\\023\\024\\025\\026\\027\\030\\031\\032\\033\\034\\035\\036\\037_\\177\\200\\376\\377_\\"_'$sq'_\\\\"\n' \ +"" "" + +testing "ls -qQ" \ +'ls -qQ ls.testdir' \ +'"\\001\\002\\003\\004\\005\\006\\a\\b\\t\\n\\v\\f\\r\\016\\017\\020\\021\\022\\023\\024\\025\\026\\027\\030\\031\\032\\033\\034\\035\\036\\037_\\177\\200\\376\\377_\\"_'$sq'_\\\\"\n' \ +"" "" + # Clean up rm -rf ls.testdir 2>/dev/null -- cgit v1.2.3-55-g6feb From 0fea54a983dd72f8b725f8aeaf0494c7f86043f7 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 1 Aug 2025 03:01:38 +0200 Subject: libbb/dump: much faster formatting of %_u on x86 function old new delta display 1485 1481 -4 Signed-off-by: Denys Vlasenko --- include/platform.h | 2 +- libbb/dump.c | 20 ++++++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/platform.h b/include/platform.h index ea0512f36..e10c5b558 100644 --- a/include/platform.h +++ b/include/platform.h @@ -187,7 +187,7 @@ #elif defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN # define BB_BIG_ENDIAN 0 # define BB_LITTLE_ENDIAN 1 -#elif defined(__386__) +#elif defined(__i386__) # define BB_BIG_ENDIAN 0 # define BB_LITTLE_ENDIAN 1 #else diff --git a/libbb/dump.c b/libbb/dump.c index 0cc7775d6..d34094576 100644 --- a/libbb/dump.c +++ b/libbb/dump.c @@ -494,12 +494,28 @@ static void conv_c(PR *pr, unsigned char *p) *pr->cchar = 'c'; printf(pr->fmt, *p); } else { -#if 1 +#if defined(__i386__) || defined(__x86_64__) + /* Abuse partial register operations */ + uint32_t buf; + unsigned n = *p; + asm ( //00000000 00000000 00000000 aabbbccc +"\n shll $10,%%eax" //00000000 000000aa bbbccc00 00000000 +"\n shrw $5,%%ax" //00000000 000000aa 00000bbb ccc00000 +"\n shrb $5,%%al" //00000000 000000aa 00000bbb 00000ccc +"\n shll $8,%%eax" //000000aa 00000bbb 00000ccc 00000000 +"\n bswapl %%eax" //00000000 00000ccc 00000bbb 000000aa +"\n addl $0x303030,%%eax" +"\n" : "=a" (n) + : "0" (n) + ); + buf = n; + str = (void*)&buf; +#elif 1 char buf[4]; /* gcc-8.0.1 needs lots of casts to shut up */ sprintf(buf, "%03o", (unsigned)(uint8_t)*p); str = buf; -#else // use faster version? +20 bytes of code +#else // use faster version? +20 bytes of code relative to sprintf() method char buf[4]; buf[3] = '\0'; ch = *p; -- cgit v1.2.3-55-g6feb From 91d8b4eb5c770dfcc05f74dd0bd7b3fabc236530 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 2 Aug 2025 07:18:56 +0200 Subject: ftpd: code shrink, move replace_char() to libbb function old new delta modprobe_main 803 804 +1 escape_text 127 122 -5 replace 18 - -18 ------------------------------------------------------------------------------ (add/remove: 0/1 grow/shrink: 1/1 up/down: 1/-23) Total: -22 bytes Signed-off-by: Denys Vlasenko --- include/libbb.h | 13 +++++++++++++ libbb/replace.c | 14 ++++++++++++++ modutils/modprobe-small.c | 21 ++++++--------------- modutils/modprobe.c | 2 +- modutils/modutils.c | 9 --------- modutils/modutils.h | 1 - networking/ftpd.c | 39 ++++++++++++--------------------------- 7 files changed, 46 insertions(+), 53 deletions(-) (limited to 'include') diff --git a/include/libbb.h b/include/libbb.h index cdc05049c..895200192 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -1113,6 +1113,19 @@ char *bin2hex(char *dst, const char *src, int count) FAST_FUNC; /* Reverse */ char* hex2bin(char *dst, const char *src, int count) FAST_FUNC; +/* Returns strlen as a bonus */ +//size_t replace_char(char *s, char what, char with) FAST_FUNC; +static inline size_t replace_char(char *str, char from, char to) +{ + char *p = str; + while (*p) { + if (*p == from) + *p = to; + p++; + } + return p - str; +} + extern const char c_escape_conv_str00[]; #define c_escape_conv_str07 (c_escape_conv_str00+3) diff --git a/libbb/replace.c b/libbb/replace.c index 6183d3e6f..bc26b04cc 100644 --- a/libbb/replace.c +++ b/libbb/replace.c @@ -46,3 +46,17 @@ char* FAST_FUNC xmalloc_substitute_string(const char *src, int count, const char //dbg_msg("subst9:'%s'", buf); return buf; } + +#if 0 /* inlined in libbb.h */ +/* Returns strlen as a bonus */ +size_t FAST_FUNC replace_char(char *str, char from, char to) +{ + char *p = str; + while (*p) { + if (*p == from) + *p = to; + p++; + } + return p - str; +} +#endif diff --git a/modutils/modprobe-small.c b/modutils/modprobe-small.c index 77e42e3fb..31a215a29 100644 --- a/modutils/modprobe-small.c +++ b/modutils/modprobe-small.c @@ -186,15 +186,6 @@ static char* find_keyword(char *ptr, size_t len, const char *word) return NULL; } -static void replace(char *s, char what, char with) -{ - while (*s) { - if (what == *s) - *s = with; - ++s; - } -} - static char *filename2modname(const char *filename, char *modname) { int i; @@ -230,7 +221,7 @@ static char* str_2_list(const char *str) dst[len] = '\0'; memcpy(dst, str, len); //TODO: protect against 2+ spaces: "word word" - replace(dst, ' ', '\0'); + replace_char(dst, ' ', '\0'); return dst; } @@ -369,14 +360,14 @@ static int parse_module(module_info *info, const char *pathname) } bksp(); /* remove last ' ' */ info->aliases = copy_stringbuf(); - replace(info->aliases, '-', '_'); + replace_char(info->aliases, '-', '_'); /* "dependency1 depandency2" */ reset_stringbuf(); ptr = find_keyword(module_image, len, "depends="); if (ptr && *ptr) { - replace(ptr, ',', ' '); - replace(ptr, '-', '_'); + replace_char(ptr, ',', ' '); + replace_char(ptr, '-', '_'); dbg2_error_msg("dep:'%s'", ptr); append(ptr); } @@ -707,7 +698,7 @@ static int process_module(char *name, const char *cmdline_options) dbg1_error_msg("process_module('%s','%s')", name, cmdline_options); - replace(name, '-', '_'); + replace_char(name, '-', '_'); dbg1_error_msg("already_loaded:%d is_remove:%d", already_loaded(name), is_remove); @@ -735,7 +726,7 @@ static int process_module(char *name, const char *cmdline_options) char *opt_filename = xasprintf("/etc/modules/%s", name); options = xmalloc_open_read_close(opt_filename, NULL); if (options) - replace(options, '\n', ' '); + replace_char(options, '\n', ' '); #if ENABLE_FEATURE_CMDLINE_MODULE_OPTIONS if (cmdline_options) { /* NB: cmdline_options always have one leading ' ' diff --git a/modutils/modprobe.c b/modutils/modprobe.c index 543f53e99..f890abe53 100644 --- a/modutils/modprobe.c +++ b/modutils/modprobe.c @@ -579,7 +579,7 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv) parser_t *p = config_open2(CONFIG_DEFAULT_DEPMOD_FILE, xfopen_for_read); for (i = 0; argv[i]; i++) - replace(argv[i], '-', '_'); + replace_char(argv[i], '-', '_'); while (config_read(p, tokens, 2, 1, "# \t", PARSE_NORMAL)) { colon = last_char_is(tokens[0], ':'); diff --git a/modutils/modutils.c b/modutils/modutils.c index cbff20961..862f71f57 100644 --- a/modutils/modutils.c +++ b/modutils/modutils.c @@ -69,15 +69,6 @@ void FAST_FUNC moddb_free(module_db *db) } } -void FAST_FUNC replace(char *s, char what, char with) -{ - while (*s) { - if (what == *s) - *s = with; - ++s; - } -} - int FAST_FUNC string_to_llist(char *string, llist_t **llist, const char *delim) { char *tok; diff --git a/modutils/modutils.h b/modutils/modutils.h index 4a702e97c..9b05116d1 100644 --- a/modutils/modutils.h +++ b/modutils/modutils.h @@ -47,7 +47,6 @@ module_entry *moddb_get(module_db *db, const char *s) FAST_FUNC; module_entry *moddb_get_or_create(module_db *db, const char *s) FAST_FUNC; void moddb_free(module_db *db) FAST_FUNC; -void replace(char *s, char what, char with) FAST_FUNC; int string_to_llist(char *string, llist_t **llist, const char *delim) FAST_FUNC; char *filename2modname(const char *filename, char *modname) FAST_FUNC; #if ENABLE_FEATURE_CMDLINE_MODULE_OPTIONS diff --git a/networking/ftpd.c b/networking/ftpd.c index 0d6a289c7..c3125410e 100644 --- a/networking/ftpd.c +++ b/networking/ftpd.c @@ -190,54 +190,39 @@ struct globals { } while (0) +/* escape_text("pfx:", str, (0xff << 8) + '\r') + * Duplicate 0xff, append \r ^^^^^^^^^^^^^^^^^^ + */ static char * escape_text(const char *prepend, const char *str, unsigned escapee) { - unsigned retlen, remainlen, chunklen; - char *ret, *found; + char *ret, *p; char append; append = (char)escapee; escapee >>= 8; - remainlen = strlen(str); - retlen = strlen(prepend); - ret = xmalloc(retlen + remainlen * 2 + 1 + 1); - strcpy(ret, prepend); + ret = xmalloc(strlen(prepend) + strlen(str) * 2 + 1 + 1); + p = stpcpy(ret, prepend); for (;;) { - found = strchrnul(str, escapee); - chunklen = found - str + 1; + char *found = strchrnul(str, escapee); - /* Copy chunk up to and including escapee (or NUL) to ret */ - memcpy(ret + retlen, str, chunklen); - retlen += chunklen; + /* Copy up to and including escapee (or NUL) */ + p = mempcpy(p, str, found - str + 1); if (*found == '\0') { /* It wasn't escapee, it was NUL! */ - ret[retlen - 1] = append; /* replace NUL */ - ret[retlen] = '\0'; /* add NUL */ break; } - ret[retlen++] = escapee; /* duplicate escapee */ str = found + 1; + *p++ = escapee; /* duplicate escapee */ } + p[-1] = append; /* replace NUL */ + *p = '\0'; /* add NUL */ return ret; } -/* Returns strlen as a bonus */ -static unsigned -replace_char(char *str, char from, char to) -{ - char *p = str; - while (*p) { - if (*p == from) - *p = to; - p++; - } - return p - str; -} - static void verbose_log(const char *str) { -- cgit v1.2.3-55-g6feb From d16bde623ced3cf113648e0bb977c1befed6d601 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 5 Aug 2025 14:04:01 +0200 Subject: 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 --- include/libbb.h | 41 ++++++------------ libbb/procps.c | 130 +++++++++++++++++++++----------------------------------- procps/pmap.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++------ procps/top.c | 16 +++---- 4 files changed, 178 insertions(+), 131 deletions(-) (limited to 'include') diff --git a/include/libbb.h b/include/libbb.h index 895200192..fba898943 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -2081,33 +2081,6 @@ enum { COMM_LEN = 16 }; # endif #endif -struct smaprec { - unsigned long mapped_rw; - unsigned long mapped_ro; - unsigned long shared_clean; - unsigned long shared_dirty; - unsigned long private_clean; - unsigned long private_dirty; - unsigned long stack; - unsigned long smap_pss, smap_swap; - unsigned long smap_size; - // 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; - // (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) - char smap_mode[5]; - char *smap_name; -}; - -#if !ENABLE_PMAP -#define procps_read_smaps(pid, total, cb, data) \ - procps_read_smaps(pid, total) -#endif -int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total, - void (*cb)(struct smaprec *, void *), void *data); - typedef struct procps_status_t { DIR *dir; IF_FEATURE_SHOW_THREADS(DIR *task_dir;) @@ -2137,7 +2110,13 @@ typedef struct procps_status_t { #endif unsigned tty_major,tty_minor; #if ENABLE_FEATURE_TOPMEM - struct smaprec smaps; + unsigned long mapped_rw; + unsigned long mapped_ro; + unsigned long shared_clean; + unsigned long shared_dirty; + unsigned long private_clean; + unsigned long private_dirty; + unsigned long stack; #endif char state[4]; /* basename of executable in exec(2), read from /proc/N/stat @@ -2186,11 +2165,15 @@ void free_procps_scan(procps_status_t* sp) FAST_FUNC; procps_status_t* procps_scan(procps_status_t* sp, int flags) FAST_FUNC; /* Format cmdline (up to col chars) into char buf[size] */ /* Puts [comm] if cmdline is empty (-> process is a kernel thread) */ -void read_cmdline(char *buf, int size, unsigned pid, const char *comm) FAST_FUNC; +int read_cmdline(char *buf, int size, unsigned pid, const char *comm) FAST_FUNC; pid_t *find_pid_by_name(const char* procName) FAST_FUNC; pid_t *pidlist_reverse(pid_t *pidList) FAST_FUNC; int starts_with_cpu(const char *str) FAST_FUNC; unsigned get_cpu_count(void) FAST_FUNC; +/* Some internals reused by pmap: */ +unsigned long FAST_FUNC fast_strtoul_10(char **endptr); +unsigned long long FAST_FUNC fast_strtoull_16(char **endptr); +char* FAST_FUNC skip_fields(char *str, int count); /* Use strict=1 if you process input from untrusted source: diff --git a/libbb/procps.c b/libbb/procps.c index 2cdfce42a..3256fafc5 100644 --- a/libbb/procps.c +++ b/libbb/procps.c @@ -109,7 +109,7 @@ void FAST_FUNC free_procps_scan(procps_status_t* sp) } #if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP -static unsigned long long fast_strtoull_16(char **endptr) +unsigned long long FAST_FUNC fast_strtoull_16(char **endptr) { unsigned char c; char *str = *endptr; @@ -130,7 +130,7 @@ static unsigned long long fast_strtoull_16(char **endptr) #if ENABLE_FEATURE_FAST_TOP || ENABLE_FEATURE_TOPMEM || ENABLE_PMAP /* We cut a lot of corners here for speed */ -static unsigned long fast_strtoul_10(char **endptr) +unsigned long FAST_FUNC fast_strtoul_10(char **endptr) { unsigned char c; char *str = *endptr; @@ -159,7 +159,7 @@ static unsigned long long fast_strtoull_10(char **endptr) return n; } # else -# define fast_strtoull_10(endptr) fast_strtoul_10(endptr) +# define fast_strtoull_10(endptr) fast_strtoul_10(endptr) # endif # if ENABLE_FEATURE_FAST_TOP @@ -173,7 +173,7 @@ static long fast_strtol_10(char **endptr) } # endif -static char *skip_fields(char *str, int count) +char* FAST_FUNC skip_fields(char *str, int count) { do { while (*str++ != ' ') @@ -184,35 +184,25 @@ static char *skip_fields(char *str, int count) } #endif -#if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP -static char* skip_whitespace_if_prefixed_with(char *buf, const char *prefix) +#if ENABLE_FEATURE_TOPMEM +static NOINLINE void procps_read_smaps(pid_t pid, procps_status_t *sp) { - char *tp = is_prefixed_with(buf, prefix); - if (tp) { - tp = skip_whitespace(tp); - } - return tp; -} + // There is A LOT of /proc/PID/smaps data on a big system. + // Optimize this for speed, makes "top -m" faster. +//TODO large speedup: +//read /proc/PID/smaps_rollup (cumulative stats of all mappings, much faster) +//and /proc/PID/maps to get mapped_ro and mapped_rw (IOW: VSZ,VSZRW) -int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total, - void (*cb)(struct smaprec *, void *), void *data) -{ FILE *file; - struct smaprec currec; char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3]; char buf[PROCPS_BUFSIZE]; -#if !ENABLE_PMAP - void (*cb)(struct smaprec *, void *) = NULL; - void *data = NULL; -#endif sprintf(filename, "/proc/%u/smaps", (int)pid); file = fopen_for_read(filename); if (!file) - return 1; + return; - memset(&currec, 0, sizeof(currec)); while (fgets(buf, PROCPS_BUFSIZE, file)) { // Each mapping datum has this form: // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME @@ -220,80 +210,53 @@ int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total, // Rss: nnn kB // ..... - char *tp, *p; + char *tp; + if (buf[0] == 'S' || buf[0] == 'P') { #define SCAN(S, X) \ - if ((tp = skip_whitespace_if_prefixed_with(buf, S)) != NULL) { \ - total->X += currec.X = fast_strtoul_10(&tp); \ - continue; \ - } - if (cb) { - SCAN("Pss:" , smap_pss ); - SCAN("Swap:" , smap_swap ); - } - SCAN("Private_Dirty:", private_dirty); - SCAN("Private_Clean:", private_clean); - SCAN("Shared_Dirty:" , shared_dirty ); - SCAN("Shared_Clean:" , shared_clean ); + if (memcmp(buf, S, sizeof(S)-1) == 0) { \ + tp = skip_whitespace(buf + sizeof(S)-1); \ + sp->X += fast_strtoul_10(&tp); \ + continue; \ + } + SCAN("Private_Dirty:", private_dirty) + SCAN("Private_Clean:", private_clean) + SCAN("Shared_Dirty:" , shared_dirty ) + SCAN("Shared_Clean:" , shared_clean ) #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 (cb) { - /* If we have a previous record, there's nothing more - * for it, call the callback and clear currec - */ - if (currec.smap_size) - cb(&currec, data); - free(currec.smap_name); - } - memset(&currec, 0, sizeof(currec)); + char *rwx; + unsigned long sz; *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); - + sz = fast_strtoull_16(&tp); // start + sz = (fast_strtoull_16(&tp) - sz) >> 10; // end - start + // tp -> "rw-s" string + rwx = tp; // skipping "rw-s FILEOFS M:m INODE " tp = skip_whitespace(skip_fields(tp, 4)); - // filter out /dev/something (something != zero) - if (!is_prefixed_with(tp, "/dev/") || strcmp(tp, "/dev/zero\n") == 0) { - if (currec.smap_mode[1] == 'w') { - currec.mapped_rw = currec.smap_size; - total->mapped_rw += currec.smap_size; - } else if (currec.smap_mode[1] == '-') { - currec.mapped_ro = currec.smap_size; - total->mapped_ro += currec.smap_size; - } + // if not a device memory mapped... + if (memcmp(tp, "/dev/", 5) != 0 // not "/dev/something" + || strcmp(tp + 5, "zero\n") == 0 // or is "/dev/zero" (which isn't a device) + ) { + if (rwx[1] == 'w') + sp->mapped_rw += sz; + else if (rwx[0] == 'r' || rwx[2] == 'x') + sp->mapped_ro += sz; + // else: seen "---p" mappings (mmap guard gaps?), + // do NOT account these as VSZ, they aren't really } - if (strcmp(tp, "[stack]\n") == 0) - total->stack += currec.smap_size; - if (cb) { - p = skip_non_whitespace(tp); - if (p == tp) { - currec.smap_name = xstrdup(" [ anon ]"); - } else { - *p = '\0'; - currec.smap_name = xstrdup(tp); - } - } - total->smap_size += currec.smap_size; + sp->stack += sz; } } fclose(file); - - if (cb) { - if (currec.smap_size) - cb(&currec, data); - free(currec.smap_name); - } - - return 0; } #endif @@ -502,7 +465,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) #if ENABLE_FEATURE_TOPMEM if (flags & PSSCAN_SMAPS) - procps_read_smaps(pid, &sp->smaps, NULL, NULL); + procps_read_smaps(pid, sp); #endif /* TOPMEM */ #if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS if (flags & PSSCAN_RUIDGID) { @@ -585,13 +548,15 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) return sp; } -void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) +int FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) { int sz; char filename[sizeof("/proc/%u/cmdline") + sizeof(int)*3]; sprintf(filename, "/proc/%u/cmdline", pid); sz = open_read_close(filename, buf, col - 1); + if (sz < 0) + return sz; if (sz > 0) { const char *base; int comm_len; @@ -614,7 +579,7 @@ void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) * It allows to see thread names set by prctl(PR_SET_NAME). */ if (!comm) - return; + return 0; comm_len = strlen(comm); /* Why compare up to comm_len, not COMM_LEN-1? * Well, some processes rewrite argv, and use _spaces_ there @@ -628,13 +593,14 @@ void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) memmove(buf + comm_len, buf, col - comm_len); snprintf(buf, col, "{%s}", comm); if (col <= comm_len) - return; + return 0; buf[comm_len - 1] = ' '; buf[col - 1] = '\0'; } } else { snprintf(buf, col, "[%s]", comm ? comm : "?"); } + return 0; } /* from kernel: 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 @@ #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 @@ -42,22 +46,114 @@ enum { OPT_q = 1 << 1, }; -static void print_smaprec(struct smaprec *currec, void *data) -{ - unsigned opt = (uintptr_t)data; +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; +}; +static void print_smaprec(struct smaprec *currec) +{ printf("%0" AFMTLL "llx ", currec->smap_start); - if (opt & OPT_x) - printf("%7lu %7lu %7lu %7lu ", + if (option_mask32 & OPT_x) + printf("%7llu %7lu %7lu %7lu ", currec->smap_size, currec->smap_pss, currec->private_dirty, currec->smap_swap); else - printf("%7luK", currec->smap_size); + 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, struct smaprec *total) +{ + FILE *file; + struct smaprec currec; + char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3]; + char buf[4096]; // how long the max filenames we expect? + + sprintf(filename, "/proc/%u/smaps", (int)pid); + + file = fopen_for_read(filename); + if (!file) + return 1; + + memset(&currec, 0, sizeof(currec)); + while (fgets(buf, sizeof(buf), 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; + + if (buf[0] == 'S' || buf[0] == 'P') { +#define SCAN(S, X) \ + if (memcmp(buf, S, sizeof(S)-1) == 0) { \ + tp = skip_whitespace(buf + sizeof(S)-1); \ + total->X += currec.X = fast_strtoul_10(&tp); \ + continue; \ + } + SCAN("Pss:" , smap_pss ); + SCAN("Swap:" , smap_swap ); + SCAN("Private_Dirty:", private_dirty); +#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); - printf(" %.4s %s\n", currec->smap_mode, currec->smap_name); + return 0; } static int procps_get_maps(pid_t pid, unsigned opt) @@ -66,25 +162,27 @@ static int procps_get_maps(pid_t pid, unsigned opt) int ret; char buf[256]; - read_cmdline(buf, sizeof(buf), pid, NULL); + 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 = procps_read_smaps(pid, &total, print_smaprec, (void*)(uintptr_t)opt); + ret = read_smaps(pid, &total); if (ret) return ret; if (!(opt & OPT_q)) { if (opt & OPT_x) printf("--------" DASHES " ------ ------ ------ ------\n" - "total" TABS " %7lu %7lu %7lu %7lu\n", + "total kB %"SIZEWIDTHx"llu %7lu %7lu %7lu\n", total.smap_size, total.smap_pss, total.private_dirty, total.smap_swap); else - printf("mapped: %luK\n", total.smap_size); + printf(" total %"SIZEWIDTH"lluK\n", total.smap_size); } 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) } #if ENABLE_FEATURE_TOPMEM else { /* TOPMEM */ - if (!(p->smaps.mapped_ro | p->smaps.mapped_rw)) + if (!(p->mapped_ro | p->mapped_rw)) continue; /* kernel threads are ignored */ n = ntop; /* No bug here - top and topmem are the same */ top = xrealloc_vector(topmem, 6, ntop++); strcpy(topmem[n].comm, p->comm); topmem[n].pid = p->pid; - topmem[n].vsz = p->smaps.mapped_rw + p->smaps.mapped_ro; - topmem[n].vszrw = p->smaps.mapped_rw; - topmem[n].rss_sh = p->smaps.shared_clean + p->smaps.shared_dirty; - topmem[n].rss = p->smaps.private_clean + p->smaps.private_dirty + topmem[n].rss_sh; - topmem[n].dirty = p->smaps.private_dirty + p->smaps.shared_dirty; - topmem[n].dirty_sh = p->smaps.shared_dirty; - topmem[n].stack = p->smaps.stack; + topmem[n].vsz = p->mapped_rw + p->mapped_ro; + topmem[n].vszrw = p->mapped_rw; + topmem[n].rss_sh = p->shared_clean + p->shared_dirty; + topmem[n].rss = p->private_clean + p->private_dirty + topmem[n].rss_sh; + topmem[n].dirty = p->private_dirty + p->shared_dirty; + topmem[n].dirty_sh = p->shared_dirty; + topmem[n].stack = p->stack; } #endif } /* end of "while we read /proc" */ -- cgit v1.2.3-55-g6feb