diff options
| author | Ron Yorston <rmy@pobox.com> | 2016-03-30 00:42:05 +0200 |
|---|---|---|
| committer | Denys Vlasenko <vda.linux@googlemail.com> | 2016-03-30 00:44:11 +0200 |
| commit | 610c4c385b38280c7bde7a48d95ec019cbfe1ab4 (patch) | |
| tree | 82ad4366ce9e927f64efa89d178c4dd2bfea1435 /libbb | |
| parent | 9844d7e830a2c55421e27e8828d2067c50f57c23 (diff) | |
| download | busybox-w32-610c4c385b38280c7bde7a48d95ec019cbfe1ab4.tar.gz busybox-w32-610c4c385b38280c7bde7a48d95ec019cbfe1ab4.tar.bz2 busybox-w32-610c4c385b38280c7bde7a48d95ec019cbfe1ab4.zip | |
applet_tables: save space by removing applet name offsets
The array applet_nameofs consumes two bytes per applet. It encodes
nofork/noexec flags
suid flags
the offset of the applet name in the applet_name string
Change the applet_table build tool to store the flags in two separate
arrays (applet_flags and applet_suid). Replace applet_nameofs[] with a
smaller version that only stores a limited number of offsets.
This requires changes to the macros APPLET_IS_NOFORK, APPLET_IS_NOEXEC
and APPLET_SUID.
According to Valgrind the original find_applet_by_name required
353 cycles per call, averaged over all names. Adjusting the number
of known offsets allows space to be traded off against execution time:
KNOWN_OFFSETS cycles bytes (wrt KNOWN_OFFSETS = 0)
0 9057 -
2 4604 32
4 2407 75
8 1342 98
16 908 130
32 884 194
This patch uses KNOWN_OFFSETS = 8.
v2:
Remove some dead code from the applet_table tool;
Treat the applet in the middle of the table as a special case.
v3:
Use the middle applet to adjust the start of the linear search as
well as the last applet found.
v4:
Use an augmented linear search in find_applet_by_name.
Drop the special treatment of the middle name from get_applet_name:
most of the advantage now derives from the last stored value.
v5:
Don't store index in applet_nameofs, it can be calculated.
v6:
Tweaks by Denys
function old new delta
find_applet_by_name 25 125 +100
applet_suid - 92 +92
run_applet_no_and_exit 452 460 +8
run_applet_and_exit 695 697 +2
applet_name_compare 31 - -31
applet_nameofs 734 14 -720
------------------------------------------------------------------------------
(add/remove: 1/1 grow/shrink: 3/1 up/down: 202/-751) Total: -549 bytes
text data bss dec hex filename
925464 906 17160 943530 e65aa busybox_old
924915 906 17160 942981 e6385 busybox_unstripped
Signed-off-by: Ron Yorston <rmy@pobox.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'libbb')
| -rw-r--r-- | libbb/appletlib.c | 82 | ||||
| -rw-r--r-- | libbb/vfork_daemon_rexec.c | 3 |
2 files changed, 55 insertions, 30 deletions
diff --git a/libbb/appletlib.c b/libbb/appletlib.c index 95e589e74..aeaf238f1 100644 --- a/libbb/appletlib.c +++ b/libbb/appletlib.c | |||
| @@ -139,36 +139,56 @@ void FAST_FUNC bb_show_usage(void) | |||
| 139 | xfunc_die(); | 139 | xfunc_die(); |
| 140 | } | 140 | } |
| 141 | 141 | ||
| 142 | #if NUM_APPLETS > 8 | ||
| 143 | static int applet_name_compare(const void *name, const void *idx) | ||
| 144 | { | ||
| 145 | int i = (int)(ptrdiff_t)idx - 1; | ||
| 146 | return strcmp(name, APPLET_NAME(i)); | ||
| 147 | } | ||
| 148 | #endif | ||
| 149 | int FAST_FUNC find_applet_by_name(const char *name) | 142 | int FAST_FUNC find_applet_by_name(const char *name) |
| 150 | { | 143 | { |
| 151 | #if NUM_APPLETS > 8 | 144 | unsigned i, max; |
| 152 | /* Do a binary search to find the applet entry given the name. */ | 145 | int j; |
| 153 | const char *p; | 146 | const char *p; |
| 154 | p = bsearch(name, (void*)(ptrdiff_t)1, ARRAY_SIZE(applet_main), 1, applet_name_compare); | 147 | |
| 155 | /* | 148 | p = applet_names; |
| 156 | * if (!p) return -1; | 149 | i = 0; |
| 157 | * ^^^^^^^^^^^^^^^^^^ the code below will do this if p == NULL :) | 150 | #if KNOWN_APPNAME_OFFSETS <= 0 |
| 158 | */ | 151 | max = NUM_APPLETS; |
| 159 | return (int)(ptrdiff_t)p - 1; | ||
| 160 | #else | 152 | #else |
| 161 | /* A version which does not pull in bsearch */ | 153 | max = NUM_APPLETS * KNOWN_APPNAME_OFFSETS; |
| 162 | int i = 0; | 154 | for (j = ARRAY_SIZE(applet_nameofs)-1; j >= 0; j--) { |
| 163 | const char *p = applet_names; | 155 | const char *pp = applet_names + applet_nameofs[j]; |
| 164 | while (i < NUM_APPLETS) { | 156 | if (strcmp(name, pp) >= 0) { |
| 165 | if (strcmp(name, p) == 0) | 157 | //bb_error_msg("name:'%s' >= pp:'%s'", name, pp); |
| 166 | return i; | 158 | p = pp; |
| 167 | p += strlen(p) + 1; | 159 | i = max - NUM_APPLETS; |
| 160 | break; | ||
| 161 | } | ||
| 162 | max -= NUM_APPLETS; | ||
| 163 | } | ||
| 164 | max /= (unsigned)KNOWN_APPNAME_OFFSETS; | ||
| 165 | i /= (unsigned)KNOWN_APPNAME_OFFSETS; | ||
| 166 | //bb_error_msg("name:'%s' starting from:'%s' i:%u max:%u", name, p, i, max); | ||
| 167 | #endif | ||
| 168 | |||
| 169 | /* Open-coding without strcmp/strlen calls for speed */ | ||
| 170 | while (i < max) { | ||
| 171 | char ch; | ||
| 172 | j = 0; | ||
| 173 | /* Do we see "name\0" in applet_names[p] position? */ | ||
| 174 | while ((ch = *p) == name[j]) { | ||
| 175 | if (ch == '\0') { | ||
| 176 | //bb_error_msg("found:'%s' i:%u", name, i); | ||
| 177 | return i; /* yes */ | ||
| 178 | } | ||
| 179 | p++; | ||
| 180 | j++; | ||
| 181 | } | ||
| 182 | /* No. | ||
| 183 | * p => 1st non-matching char in applet_names[], | ||
| 184 | * skip to and including NUL. | ||
| 185 | */ | ||
| 186 | while (ch != '\0') | ||
| 187 | ch = *++p; | ||
| 188 | p++; | ||
| 168 | i++; | 189 | i++; |
| 169 | } | 190 | } |
| 170 | return -1; | 191 | return -1; |
| 171 | #endif | ||
| 172 | } | 192 | } |
| 173 | 193 | ||
| 174 | 194 | ||
| @@ -583,6 +603,7 @@ static void install_links(const char *busybox, int use_symbolic_links, | |||
| 583 | * busybox.h::bb_install_loc_t, or else... */ | 603 | * busybox.h::bb_install_loc_t, or else... */ |
| 584 | int (*lf)(const char *, const char *); | 604 | int (*lf)(const char *, const char *); |
| 585 | char *fpc; | 605 | char *fpc; |
| 606 | const char *appname = applet_names; | ||
| 586 | unsigned i; | 607 | unsigned i; |
| 587 | int rc; | 608 | int rc; |
| 588 | 609 | ||
| @@ -593,7 +614,7 @@ static void install_links(const char *busybox, int use_symbolic_links, | |||
| 593 | for (i = 0; i < ARRAY_SIZE(applet_main); i++) { | 614 | for (i = 0; i < ARRAY_SIZE(applet_main); i++) { |
| 594 | fpc = concat_path_file( | 615 | fpc = concat_path_file( |
| 595 | custom_install_dir ? custom_install_dir : install_dir[APPLET_INSTALL_LOC(i)], | 616 | custom_install_dir ? custom_install_dir : install_dir[APPLET_INSTALL_LOC(i)], |
| 596 | APPLET_NAME(i)); | 617 | appname); |
| 597 | // debug: bb_error_msg("%slinking %s to busybox", | 618 | // debug: bb_error_msg("%slinking %s to busybox", |
| 598 | // use_symbolic_links ? "sym" : "", fpc); | 619 | // use_symbolic_links ? "sym" : "", fpc); |
| 599 | rc = lf(busybox, fpc); | 620 | rc = lf(busybox, fpc); |
| @@ -601,6 +622,8 @@ static void install_links(const char *busybox, int use_symbolic_links, | |||
| 601 | bb_simple_perror_msg(fpc); | 622 | bb_simple_perror_msg(fpc); |
| 602 | } | 623 | } |
| 603 | free(fpc); | 624 | free(fpc); |
| 625 | while (*appname++ != '\0') | ||
| 626 | continue; | ||
| 604 | } | 627 | } |
| 605 | } | 628 | } |
| 606 | # else | 629 | # else |
| @@ -754,7 +777,7 @@ void FAST_FUNC run_applet_no_and_exit(int applet_no, char **argv) | |||
| 754 | 777 | ||
| 755 | /* Reinit some shared global data */ | 778 | /* Reinit some shared global data */ |
| 756 | xfunc_error_retval = EXIT_FAILURE; | 779 | xfunc_error_retval = EXIT_FAILURE; |
| 757 | applet_name = APPLET_NAME(applet_no); | 780 | applet_name = bb_get_last_path_component_nostrip(argv[0]); |
| 758 | 781 | ||
| 759 | /* Special case. POSIX says "test --help" | 782 | /* Special case. POSIX says "test --help" |
| 760 | * should be no different from e.g. "test --foo". | 783 | * should be no different from e.g. "test --foo". |
| @@ -785,11 +808,14 @@ void FAST_FUNC run_applet_no_and_exit(int applet_no, char **argv) | |||
| 785 | 808 | ||
| 786 | void FAST_FUNC run_applet_and_exit(const char *name, char **argv) | 809 | void FAST_FUNC run_applet_and_exit(const char *name, char **argv) |
| 787 | { | 810 | { |
| 788 | int applet = find_applet_by_name(name); | 811 | int applet; |
| 789 | if (applet >= 0) | 812 | |
| 790 | run_applet_no_and_exit(applet, argv); | ||
| 791 | if (is_prefixed_with(name, "busybox")) | 813 | if (is_prefixed_with(name, "busybox")) |
| 792 | exit(busybox_main(argv)); | 814 | exit(busybox_main(argv)); |
| 815 | /* find_applet_by_name() search is more expensive, so goes second */ | ||
| 816 | applet = find_applet_by_name(name); | ||
| 817 | if (applet >= 0) | ||
| 818 | run_applet_no_and_exit(applet, argv); | ||
| 793 | } | 819 | } |
| 794 | 820 | ||
| 795 | #endif /* !defined(SINGLE_APPLET_MAIN) */ | 821 | #endif /* !defined(SINGLE_APPLET_MAIN) */ |
diff --git a/libbb/vfork_daemon_rexec.c b/libbb/vfork_daemon_rexec.c index d6ca7b263..1adb5b3c4 100644 --- a/libbb/vfork_daemon_rexec.c +++ b/libbb/vfork_daemon_rexec.c | |||
| @@ -116,8 +116,6 @@ int FAST_FUNC run_nofork_applet(int applet_no, char **argv) | |||
| 116 | 116 | ||
| 117 | save_nofork_data(&old); | 117 | save_nofork_data(&old); |
| 118 | 118 | ||
| 119 | applet_name = APPLET_NAME(applet_no); | ||
| 120 | |||
| 121 | xfunc_error_retval = EXIT_FAILURE; | 119 | xfunc_error_retval = EXIT_FAILURE; |
| 122 | 120 | ||
| 123 | /* In case getopt() or getopt32() was already called: | 121 | /* In case getopt() or getopt32() was already called: |
| @@ -157,6 +155,7 @@ int FAST_FUNC run_nofork_applet(int applet_no, char **argv) | |||
| 157 | * need argv untouched because they free argv[i]! */ | 155 | * need argv untouched because they free argv[i]! */ |
| 158 | char *tmp_argv[argc+1]; | 156 | char *tmp_argv[argc+1]; |
| 159 | memcpy(tmp_argv, argv, (argc+1) * sizeof(tmp_argv[0])); | 157 | memcpy(tmp_argv, argv, (argc+1) * sizeof(tmp_argv[0])); |
| 158 | applet_name = tmp_argv[0]; | ||
| 160 | /* Finally we can call NOFORK applet's main() */ | 159 | /* Finally we can call NOFORK applet's main() */ |
| 161 | rc = applet_main[applet_no](argc, tmp_argv); | 160 | rc = applet_main[applet_no](argc, tmp_argv); |
| 162 | } else { | 161 | } else { |
