From e8a14610119b86133863182033c4b3e4df3f4257 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Mon, 9 Jan 2023 05:34:39 +0000 Subject: documentation updates Document the HTTP POST API and update the README with POP3 info and some minor rewording. We'll also consistently use `DIR' instead of `DIRECTORY' since it's shorter and well understood (`opendir', `readdir', etc.) --- README | 13 ++++++----- script/mwrap-perl | 64 +++++++++++++++++++++++++++++++++++++++++++++++------ script/mwrap-rproxy | 6 ++--- 3 files changed, 67 insertions(+), 16 deletions(-) diff --git a/README b/README index f8a1d1b..1fa1dfb 100644 --- a/README +++ b/README @@ -66,16 +66,17 @@ malloc locations. * Perl source files over 16.7 million lines long are not supported :P -== Public mail archives (HTTP, Atom feeds, IMAP mailbox, NNTP group): +== Public mail archives (HTTP, Atom feeds, IMAP mailbox, NNTP group, POP3): https://80x24.org/mwrap-perl/ - imaps://80x24.org/inbox.comp.lang.perl.mwrap.0 + imaps://;AUTH=ANONYMOUS@80x24.org/inbox.comp.lang.perl.mwrap.0 nntps://80x24.org/inbox.comp.lang.perl.mwrap + https://80x24.org/mwrap-perl/_/text/help/#pop3 -No subscription nor real identities will ever be required to -obtain support. Memory usage reductions start with you; only send -plain-text mail to us and do not top-post. HTML mail and top-posting -costs everybody memory and bandwidth. +No subscription nor real identities will ever be required to obtain support, +but HTML mail is rejected. Memory usage reductions start with you; +only send plain-text mail to us and do not top-post. HTML mail and +top-posting costs everybody memory and bandwidth. mwrap-perl@80x24.org diff --git a/script/mwrap-perl b/script/mwrap-perl index 82629b4..48f5471 100644 --- a/script/mwrap-perl +++ b/script/mwrap-perl @@ -28,8 +28,8 @@ mwrap-perl - run any command under mwrap =head1 SYNOPSIS - # to trace a long-running program and access it via $DIRECTORY/$PID.sock: - MWRAP=socket_dir:$DIRECTORY mwrap-perl COMMAND + # to trace a long-running program and access it via $DIR/$PID.sock: + MWRAP=socket_dir:$DIR mwrap-perl COMMAND # to trace a short-lived command and dump its output to a log: MWRAP=dump_path:$FILENAME mwrap-perl COMMAND @@ -46,14 +46,16 @@ of native (C/C++) functions for non-Perl programs. C is the only environment variable read. It contains multiple options delimited by C<,> with names and values delimited by C<:> -=item socket_dir:$DIRECTORY +=over 4 + +=item socket_dir:$DIR This launches an embedded HTTP server in each process and binds it -to C<$DIRECTORY/$PID.sock>. C +to C<$DIR/$PID.sock>. C or L may be used to access various endpoints in the HTTP server. -=item: bt:$DEPTH +=item bt:$DEPTH The backtrace depth for L in addition to the Perl file and line number where C<$DEPTH> is a non-negative number. @@ -64,15 +66,63 @@ increase the amount of memory mwrap (and liburcu) itself uses. This is only useful in conjunction with C +This may be changed via POST request (see below). + Default: 0 =item dump_path:$FILENAME -Dumps the output +Dumps the output at exit to a given filename: total_bytes call_count location -In the future, dumping to a self-describing CSV will be supported +In the future, dumping to a self-describing CSV will be supported. + +=item dump_fd:$DESCRIPTOR + +As with dump_path, but dumps the output to a given file descriptor. + +=back + +=head1 HTTP POST API + +In addition to the various GET endpoints linked via C, +there are some POST endpoints which are typically accessed via +C + +=over 4 + +=item POST http://0/$PID/reset + +C + +Reset all internal counters. This is not done atomically and does +not release any memory. + +=item POST http://0/$PID/trim + +C + +Runs L with a 0 pad value to release unused memory +back to the kernel. In our malloc implementation, this is done +lazily to avoid contention and does not happen unless sleeping threads. + +=item POST http://0/$PID/ctl + +Set various internal knobs. Currently, C is the +only knob supported: + +C + +Using the C header allows changing the aforementioned +C value to a specified depth level. As with C, only make small +adjustments as the memory cost can increase exponentially with each step. + +It is typically a good idea to reset (C) after changing +the depth on a running process. + +Headers other than C may be accepted in the future to +tweak other settings. =back diff --git a/script/mwrap-rproxy b/script/mwrap-rproxy index 2498bc3..056e80a 100644 --- a/script/mwrap-rproxy +++ b/script/mwrap-rproxy @@ -48,10 +48,10 @@ mwrap-rproxy - reverse proxy for embedded per-process mwrap httpd =head1 SYNOPSIS # start the long-running COMMAND you wish to trace: - MWRAP=socket_dir:$DIRECTORY mwrap-perl COMMAND + MWRAP=socket_dir:$DIR mwrap-perl COMMAND # in a different terminal, point mwrap-proxy to the mwrap-perl socket_dir - mwrap-rproxy --socket-dir=$DIRECTORY -l 127.0.0.1:8080 + mwrap-rproxy --socket-dir=$DIR -l 127.0.0.1:8080 # open http://127.0.0.1:8080/ in your favorite web browser: w3m http://127.0.0.1:8080/ @@ -71,7 +71,7 @@ traced by mwrap-perl. =over 4 -=item --socket-dir=DIRECTORY +=item --socket-dir=DIR If unset, it will attempt to parse C from the C environment (see L). -- cgit v1.2.3-24-ge0c7 From 72a3769760252f4f2cddf1d63e939e4c9b505cdd Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Mon, 9 Jan 2023 05:34:40 +0000 Subject: s/X-Mwrap-BT-Depth/X-Mwrap-BT/ This is more consistent with the `MWRAP=bt:' use, since adding `-Depth' seems unnecessary and makes curl commands too long. --- httpd.h | 2 +- script/mwrap-perl | 8 ++++---- t/httpd.t | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/httpd.h b/httpd.h index 36e487d..89e366e 100644 --- a/httpd.h +++ b/httpd.h @@ -892,7 +892,7 @@ static enum mw_qev h1_parse_harder(struct mw_h1 *h1, struct mw_h1req *h1r, * request bodies, so let pico handle parameters in * HTTP request headers, instead. */ - if (NAME_EQ(hdr, "X-Mwrap-BT-Depth")) { + if (NAME_EQ(hdr, "X-Mwrap-BT")) { errno = 0; depth = strtol(hdr->value, &end, 10); if (errno || !valid_end(end)) diff --git a/script/mwrap-perl b/script/mwrap-perl index 48f5471..182b0bd 100644 --- a/script/mwrap-perl +++ b/script/mwrap-perl @@ -109,19 +109,19 @@ lazily to avoid contention and does not happen unless sleeping threads. =item POST http://0/$PID/ctl -Set various internal knobs. Currently, C is the +Set various internal knobs. Currently, C is the only knob supported: -C +C -Using the C header allows changing the aforementioned +Using the C header allows changing the aforementioned C value to a specified depth level. As with C, only make small adjustments as the memory cost can increase exponentially with each step. It is typically a good idea to reset (C) after changing the depth on a running process. -Headers other than C may be accepted in the future to +Headers other than C may be accepted in the future to tweak other settings. =back diff --git a/t/httpd.t b/t/httpd.t index 7746837..125ae3b 100644 --- a/t/httpd.t +++ b/t/httpd.t @@ -152,12 +152,12 @@ SKIP: { $rc = system(@curl, qw(-d x=y), "http://0/$pid/reset"); is($rc, 0, 'curl /reset'); - $rc = system(@curl, qw(-HX-Mwrap-BT-Depth:10 -XPOST), + $rc = system(@curl, qw(-HX-Mwrap-BT:10 -XPOST), "http://0/$pid/ctl"); - is($rc, 0, 'curl /ctl (X-Mwrap-BT-Depth)'); + is($rc, 0, 'curl /ctl (X-Mwrap-BT)'); like(slurp($cout), qr/\bMWRAP=bt:10\b/, 'changed bt depth'); - $rc = system(@curl, qw(-HX-Mwrap-BT-Depth:10 -d blah http://0/ctl)); + $rc = system(@curl, qw(-HX-Mwrap-BT:10 -d blah http://0/ctl)); is($rc >> 8, 22, '404 w/o PID prefix'); }; -- cgit v1.2.3-24-ge0c7 From 2c7547e929737f28069eeea2b3cd4009a4f7867c Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sun, 8 Jan 2023 04:27:33 +0000 Subject: t/httpd.t: needs sqlite3 3.32.0+ for `.import --csv' Just disable the test if CSV import doesn't work with the currently-installed sqlite3. --- t/httpd.t | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/t/httpd.t b/t/httpd.t index 125ae3b..bf9100e 100644 --- a/t/httpd.t +++ b/t/httpd.t @@ -135,19 +135,28 @@ SKIP: { SKIP: { skip 'no reset w/o curl --unix-socket', 1 if !$curl_unix; - + my ($sqlite_v) = (`sqlite3 --version` =~ /([\d+\.]+)/); + if ($?) { + diag 'sqlite3 missing or broken'; + $sqlite_v = 0; + } else { + my @v = split(/\./, $sqlite_v); + $sqlite_v = ($v[0] << 16) | ($v[1] << 8) | $v[2]; + diag 'sqlite_v='.sprintf('0x%x', $sqlite_v); + } $rc = system(@curl, "http://0/$pid/each/100.csv"); is($rc, 0, '.csv retrieved') or skip 'CSV failed', 1; my $db = "$mwrap_tmp/t.sqlite3"; - $rc = system(qw(sqlite3), $db, ".import --csv $cout mwrap_each"); - if ($rc == -1) { - diag 'sqlite3 missing'; - } else { + + if ($sqlite_v >= 0x32000) { + $rc = system(qw(sqlite3), $db,".import --csv $cout mwrap_each"); is($rc, 0, 'sqlite3 import'); my $n = `sqlite3 $db 'SELECT COUNT(*) FROM mwrap_each'`; is($?, 0, 'sqlite3 count'); my $exp = split(/\n/, slurp($cout)); is($n + 1, $exp, 'imported all rows into sqlite'); + } else { + diag "sqlite 3.32.0+ needed for `.import --csv'"; } $rc = system(@curl, qw(-d x=y), "http://0/$pid/reset"); -- cgit v1.2.3-24-ge0c7 From 718b313cf3fee3799cdea3ecbbfba8a615066baf Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Mon, 9 Jan 2023 07:38:49 +0000 Subject: simplify mw_perl_src_file_cstr to avoid gcc warning This works around a gcc (Debian 10.2.1-6) warning, and is less code, even --- mwrap_core.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mwrap_core.h b/mwrap_core.h index 15df857..deb3bb3 100644 --- a/mwrap_core.h +++ b/mwrap_core.h @@ -389,10 +389,8 @@ static const char *mw_perl_src_file_cstr(unsigned *lineno) { const COP *cop = mwp_curcop(); if (!cop) return NULL; - const char *fn = CopFILE(cop); - if (!fn) return NULL; *lineno = CopLINE(cop); - return fn; + return CopFILE(cop); } # define SRC_FILE_CSTR(lineno) mw_perl_src_file_cstr(lineno) #endif /* MWRAP_PERL */ -- cgit v1.2.3-24-ge0c7 From 8ce0068f470f3dad3a2920e7fdeedeee235c44eb Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 11 Jan 2023 01:12:46 +0000 Subject: support MWRAP=dump_csv:$FILENAME parameter Just reusing code from httpd. --- httpd.h | 217 ++++++++++++++++++++++++++++-------------------------- mwrap_core.h | 35 +++++++-- script/mwrap-perl | 12 ++- t/mwrap.t | 21 +++++- 4 files changed, 173 insertions(+), 112 deletions(-) diff --git a/httpd.h b/httpd.h index ef4d83c..9219d36 100644 --- a/httpd.h +++ b/httpd.h @@ -504,9 +504,11 @@ static off_t write_loc_name(FILE *fp, const struct src_loc *l) return end - beg; } -static struct h1_src_loc *accumulate(unsigned long min, size_t *hslc, FILE *lp) +static struct h1_src_loc * +accumulate(struct mw_fbuf *lb, unsigned long min, size_t *hslc) { struct mw_fbuf fb; + if (!fbuf_init(lb)) return NULL; if (!fbuf_init(&fb)) return NULL; rcu_read_lock(); struct cds_lfht *t = CMM_LOAD_SHARED(totals); @@ -528,18 +530,23 @@ static struct h1_src_loc *accumulate(unsigned long min, size_t *hslc, FILE *lp) HUGE_VAL; hsl.max_life = uatomic_read(&l->max_lifespan); hsl.sl = l; - hsl.lname_len = write_loc_name(lp, l); + hsl.lname_len = write_loc_name(lb->fp, l); fwrite(&hsl, sizeof(hsl), 1, fb.fp); } rcu_read_unlock(); - struct h1_src_loc *hslv; - if (fbuf_close(&fb)) { - hslv = NULL; - } else { - *hslc = fb.len / sizeof(*hslv); - mwrap_assert((fb.len % sizeof(*hslv)) == 0); - hslv = (struct h1_src_loc *)fb.ptr; + if (fbuf_close(&fb) || fbuf_close(lb)) + return NULL; + + struct h1_src_loc *hslv = (struct h1_src_loc *)fb.ptr; + *hslc = fb.len / sizeof(*hslv); + mwrap_assert((fb.len % sizeof(*hslv)) == 0); + char *n = lb->ptr; + for (size_t i = 0; i < *hslc; ++i) { + hslv[i].loc_name = n; + n += hslv[i].lname_len; + if (hslv[i].lname_len < 0) + return NULL; } return hslv; } @@ -609,124 +616,128 @@ static enum mw_qev each_at(struct mw_h1 *h1, struct mw_h1req *h1r) return h1_200(h1, &html, TYPE_HTML); } -/* /$PID/each/$MIN endpoint */ -static enum mw_qev each_gt(struct mw_h1 *h1, struct mw_h1req *h1r, - unsigned long min, bool csv) -{ - static const char default_sort[] = "bytes"; - const char *sort; - size_t sort_len = 0; +typedef int (*cmp_fn)(const void *, const void *); - if (!csv) { - sort = default_sort; - sort_len = sizeof(default_sort) - 1; +static cmp_fn write_csv_header(FILE *fp, const char *sort, size_t sort_len) +{ + cmp_fn cmp = NULL; + for (size_t i = 0; i < CAA_ARRAY_SIZE(fields); i++) { + const char *fn = fields[i].fname; + if (i) + fputc(',', fp); + fputs(fn, fp); + if (fields[i].flen == sort_len && !memcmp(fn, sort, sort_len)) + cmp = fields[i].cmp; } + fputc('\n', fp); + return cmp; +} - if (h1r->qstr && h1r->qlen > 5 && !memcmp(h1r->qstr, "sort=", 5)) { - sort = h1r->qstr + 5; - sort_len = h1r->qlen - 5; +static void write_csv_data(FILE *fp, struct h1_src_loc *hslv, size_t hslc) +{ + for (size_t i = 0; i < hslc; i++) { + struct h1_src_loc *hsl = &hslv[i]; + + fprintf(fp, "%zu,%zu,%zu,%zu,%0.3f,%zu,", + hsl->bytes, hsl->allocations, hsl->frees, + hsl->live, hsl->mean_life, hsl->max_life); + write_q_csv(fp, hsl->loc_name, hsl->lname_len); + fputc('\n', fp); } +} - size_t hslc; +static void *write_csv(FILE *fp, size_t min, const char *sort, size_t sort_len) +{ AUTO_CLOFREE struct mw_fbuf lb; - if (!fbuf_init(&lb)) return h1_close(h1); - AUTO_FREE struct h1_src_loc *hslv = accumulate(min, &hslc, lb.fp); - if (!hslv) - return h1_close(h1); + size_t hslc; + AUTO_FREE struct h1_src_loc *hslv = accumulate(&lb, min, &hslc); + if (!hslv) return NULL; - if (fbuf_close(&lb)) - return h1_close(h1); + cmp_fn cmp = write_csv_header(fp, sort, sort_len); + if (cmp) + qsort(hslv, hslc, sizeof(*hslv), cmp); + write_csv_data(fp, hslv, hslc); + return fp; +} - char *n = lb.ptr; - for (size_t i = 0; i < hslc; ++i) { - hslv[i].loc_name = n; - n += hslv[i].lname_len; - if (hslv[i].lname_len < 0) - return h1_close(h1); +/* /$PID/each/$MIN endpoint */ +static enum mw_qev each_gt(struct mw_h1 *h1, struct mw_h1req *h1r, + size_t min, bool csv) +{ + static const char default_sort[] = "bytes"; + const char *sort = csv ? NULL : default_sort; + size_t sort_len = csv ? 0 : (sizeof(default_sort) - 1); + + if (h1r->qstr && h1r->qlen > 5 && !memcmp(h1r->qstr, "sort=", 5)) { + sort = h1r->qstr + 5; + sort_len = h1r->qlen - 5; } struct mw_fbuf bdy; FILE *fp = wbuf_init(&bdy); if (!fp) return h1_close(h1); - - if (!csv) { - unsigned depth = (unsigned)CMM_LOAD_SHARED(bt_req_depth); - fprintf(fp, "mwrap each >%lu" - "

mwrap each >%lu " - "(change `%lu' in URL to adjust filtering) - " - "MWRAP=bt:%u .csv", - min, min, min, depth, min); - show_stats(fp); - /* need borders to distinguish multi-level traces */ - if (depth) - FPUTS("", fp); - else /* save screen space if only tracing one line */ - FPUTS("", fp); + if (csv) { + if (write_csv(fp, min, sort, sort_len)) + return h1_200(h1, &bdy, TYPE_CSV); + return h1_close(h1); } - int (*cmp)(const void *, const void *) = NULL; - if (csv) { - for (size_t i = 0; i < CAA_ARRAY_SIZE(fields); i++) { - const char *fn = fields[i].fname; - if (i) - fputc(',', fp); - fputs(fn, fp); - if (fields[i].flen == sort_len && - !memcmp(fn, sort, sort_len)) - cmp = fields[i].cmp; - } - fputc('\n', fp); - } else { - for (size_t i = 0; i < CAA_ARRAY_SIZE(fields); i++) { - const char *fn = fields[i].fname; - FPUTS("", fp); + size_t hslc; + AUTO_CLOFREE struct mw_fbuf lb; + AUTO_FREE struct h1_src_loc *hslv = accumulate(&lb, min, &hslc); + if (!hslv) + return h1_close(h1); + + unsigned depth = (unsigned)CMM_LOAD_SHARED(bt_req_depth); + fprintf(fp, "mwrap each >%lu" + "

mwrap each >%lu " + "(change `%lu' in URL to adjust filtering) - " + "MWRAP=bt:%u .csv", + min, min, min, depth, min); + show_stats(fp); + /* need borders to distinguish multi-level traces */ + if (depth) + FPUTS("

", fp); + else /* save screen space if only tracing one line */ + FPUTS("
", fp); - if (fields[i].flen == sort_len && - !memcmp(fn, sort, sort_len)) { - cmp = fields[i].cmp; - fprintf(fp, "%s", fields[i].fname); - } else { - fprintf(fp, "%s", - min, fn, fn); - } - FPUTS("
", fp); + cmp_fn cmp = NULL; + for (size_t i = 0; i < CAA_ARRAY_SIZE(fields); i++) { + const char *fn = fields[i].fname; + FPUTS("", fp); } - if (!csv) - FPUTS("", fp); + FPUTS("", fp); if (cmp) qsort(hslv, hslc, sizeof(*hslv), cmp); - else if (!csv) + else FPUTS("", fp); - if (csv) { - for (size_t i = 0; i < hslc; i++) { - struct h1_src_loc *hsl = &hslv[i]; - fprintf(fp, "%zu,%zu,%zu,%zu,%0.3f,%zu,", - hsl->bytes, hsl->allocations, hsl->frees, - hsl->live, hsl->mean_life, hsl->max_life); - write_q_csv(fp, hsl->loc_name, hsl->lname_len); - fputc('\n', fp); - } - } else { - for (size_t i = 0; i < hslc; i++) { - struct h1_src_loc *hsl = &hslv[i]; + for (size_t i = 0; i < hslc; i++) { + struct h1_src_loc *hsl = &hslv[i]; - fprintf(fp, "" - "", - hsl->bytes, hsl->allocations, hsl->frees, - hsl->live, hsl->mean_life, hsl->max_life); - FPUTS("" + "", + hsl->bytes, hsl->allocations, hsl->frees, + hsl->live, hsl->mean_life, hsl->max_life); + FPUTS("", fp); - } - FPUTS("
", fp); + if (fields[i].flen == sort_len && + !memcmp(fn, sort, sort_len)) { + cmp = fields[i].cmp; + fprintf(fp, "%s", fields[i].fname); + } else { + fprintf(fp, "%s", + min, fn, fn); } + FPUTS("
sort= not understood
%zu%zu%zu%zu%0.3f%zu%zu%zu%zu%zu%0.3f%zusl), - src_loc_hash_len(hsl->sl)); + write_b64_url(fp, src_loc_hash_tip(hsl->sl), + src_loc_hash_len(hsl->sl)); - FPUTS("\">", fp); - write_html(fp, hsl->loc_name, hsl->lname_len); - FPUTS("
", fp); + FPUTS("\">", fp); + write_html(fp, hsl->loc_name, hsl->lname_len); + FPUTS("", fp); } - return h1_200(h1, &bdy, csv ? TYPE_CSV : TYPE_HTML); + FPUTS("", fp); + return h1_200(h1, &bdy, TYPE_HTML); } /* /$PID/ root endpoint */ @@ -781,7 +792,7 @@ static enum mw_qev h1_dispatch(struct mw_h1 *h1, struct mw_h1req *h1r) if ((c = PATH_SKIP(h1r, "/each/"))) { errno = 0; char *e; - unsigned long min = strtoul(c, &e, 10); + size_t min = (size_t)strtoul(c, &e, 10); if (!errno) { if (*e == ' ' || *e == '?') return each_gt(h1, h1r, min, false); diff --git a/mwrap_core.h b/mwrap_core.h index deb3bb3..fff0538 100644 --- a/mwrap_core.h +++ b/mwrap_core.h @@ -732,6 +732,7 @@ enomem: struct dump_arg { FILE *fp; size_t min; + bool dump_csv; }; char **bt_syms(void * const *addrlist, uint32_t size) @@ -754,12 +755,16 @@ static void cleanup_free(void *any) free(*p); } +static void *write_csv(FILE *, size_t min, const char *sort, size_t sort_len); static void *dump_to_file(struct dump_arg *a) { struct cds_lfht_iter iter; struct src_loc *l; struct cds_lfht *t; + if (a->dump_csv) + return write_csv(a->fp, a->min, NULL, 0); + ++locating; rcu_read_lock(); t = CMM_LOAD_SHARED(totals); @@ -857,7 +862,7 @@ __attribute__ ((destructor)) static void mwrap_dtor(void) { const char *opt = getenv("MWRAP"); const char *modes[] = { "a", "a+", "w", "w+", "r+" }; - struct dump_arg a = { .min = 0 }; + struct dump_arg a = { .min = 0, .dump_csv = false }; size_t i; int dump_fd; char *dump_path; @@ -870,9 +875,24 @@ __attribute__ ((destructor)) static void mwrap_dtor(void) return; ++locating; - if ((dump_path = strstr(opt, "dump_path:")) && - (dump_path += sizeof("dump_path")) && - *dump_path) { + + /* parse dump_csv:$PATHNAME */ + if ((dump_path = strstr(opt, "dump_csv:"))) { + dump_path += sizeof("dump_csv"); + if (!*dump_path) + dump_path = NULL; + else + a.dump_csv = true; + } + if (!dump_path) { + /* parse dump_path:$PATHNAME */ + if ((dump_path = strstr(opt, "dump_path:"))) { + dump_path += sizeof("dump_path"); + if (!*dump_path) + dump_path = NULL; + } + } + if (dump_path) { char *end = strchr(dump_path, ','); char buf[PATH_MAX]; if (end) { @@ -887,10 +907,13 @@ __attribute__ ((destructor)) static void mwrap_dtor(void) fprintf(stderr, "open %s failed: %m\n", dump_path); goto out; } - } - else if (!sscanf(opt, "dump_fd:%d", &dump_fd)) + } else if ((s = strstr(opt, "dump_fd:")) && + !sscanf(s, "dump_fd:%d", &dump_fd)) goto out; + /* allow dump_csv standalone for dump_fd */ + if (!a.dump_csv && strstr(opt, "dump_csv")) + a.dump_csv = true; if ((s = strstr(opt, "dump_min:"))) sscanf(s, "dump_min:%zu", &a.min); diff --git a/script/mwrap-perl b/script/mwrap-perl index 182b0bd..eb29176 100644 --- a/script/mwrap-perl +++ b/script/mwrap-perl @@ -76,12 +76,20 @@ Dumps the output at exit to a given filename: total_bytes call_count location -In the future, dumping to a self-describing CSV will be supported. - =item dump_fd:$DESCRIPTOR As with dump_path, but dumps the output to a given file descriptor. +=item dump_csv:$FILENAME + +Dump CSV to the given filename. + +This output matches the HTTP server output and includes column headers, +but is subject to change in future releases. + +C without the C<:> may also be used in conjunction with +C, such as C. + =back =head1 HTTP POST API diff --git a/t/mwrap.t b/t/mwrap.t index 6f99715..ccd739b 100644 --- a/t/mwrap.t +++ b/t/mwrap.t @@ -9,7 +9,8 @@ my $dump = "$mwrap_tmp/dump"; { my $env = { MWRAP => "dump_path:$dump,dump_min:10000" }; my $nr = 1000; - mwrap_run('dump test', $env, '-e', '$x = "hello world" x '.$nr); + my $script = '$x = "hello world" x '.$nr; + mwrap_run('dump test', $env, '-e', $script); ok(-s $dump, "dump file written to"); my $s = slurp($dump); truncate($dump, 0); @@ -23,6 +24,24 @@ my $dump = "$mwrap_tmp/dump"; } else { fail("$s failed to match $re"); } + + $env->{MWRAP} = "dump_csv:$dump"; + mwrap_run('dump_csv test', $env, '-e', $script); + ok(-s $dump, "CSV written to path"); + $s = slurp($dump); + truncate($dump, 0); + my $nr_comma = ($s =~ tr/,/,/); + my $nr_cr = ($s =~ tr/\n/\n/); + ok($nr_comma > ($nr_cr * 4), 'CSV has more commas than CR'); + + $env->{MWRAP} = 'dump_csv,dump_fd:2'; + mwrap_run('dump_csv,dump_fd test', $env, '-e', $script); + ok(-s $mwrap_err, "CSV written to stderr"); + $s = slurp($mwrap_err); + truncate($mwrap_err, 0); + $nr_comma = ($s =~ tr/,/,/); + $nr_cr = ($s =~ tr/\n/\n/); + ok($nr_comma > ($nr_cr * 4), 'CSV has more commas than CR'); } SKIP: { # C++ program which uses malloc via "new" -- cgit v1.2.3-24-ge0c7 From 86d350a3854af1a5a292972d4f70154e61ce5e80 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 11 Jan 2023 01:12:47 +0000 Subject: add mwrap-decode-csv tool This is a useful companion to the dump_csv: directive. It also fixes a bug where HTML escaping was unnecessarily done to the CSV output by -rproxy. --- MANIFEST | 1 + Makefile.PL | 3 ++- lib/Devel/Mwrap/Rproxy.pm | 33 +++++++++++++++------------ script/mwrap-decode-csv | 57 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 15 deletions(-) create mode 100644 script/mwrap-decode-csv diff --git a/MANIFEST b/MANIFEST index 096cec9..cf42979 100644 --- a/MANIFEST +++ b/MANIFEST @@ -20,6 +20,7 @@ mymalloc.h picohttpparser.h picohttpparser_c.h ppport.h +script/mwrap-decode-csv script/mwrap-perl script/mwrap-rproxy t/httpd-unit.t diff --git a/Makefile.PL b/Makefile.PL index dadf80b..41e8f03 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -83,7 +83,8 @@ push @writemakefile_args, ( CCFLAGS => "$Config{ccflags} $ccflags", PREREQ_PM => {}, ABSTRACT_FROM => 'lib/Devel/Mwrap.pm', - EXE_FILES => [qw(script/mwrap-perl script/mwrap-rproxy)], + EXE_FILES => [qw(script/mwrap-perl script/mwrap-rproxy + script/mwrap-decode-csv)], AUTHOR => 'mwrap hackers ', LIBS => $LIBS, # e.g. -lurcu-cds LICENSE => 'gpl_2', # GPL-3.0+, CPAN::Meta::Spec limitation diff --git a/lib/Devel/Mwrap/Rproxy.pm b/lib/Devel/Mwrap/Rproxy.pm index d5a9d9d..de65685 100644 --- a/lib/Devel/Mwrap/Rproxy.pm +++ b/lib/Devel/Mwrap/Rproxy.pm @@ -11,9 +11,8 @@ package Devel::Mwrap::Rproxy; use v5.12; # strict use Fcntl qw(SEEK_SET); use IO::Socket::UNIX; -use Plack::Util; -sub new { bless { socket_dir => $_[1]}, $_[0] } +sub new { require Plack::Util; bless { socket_dir => $_[1]}, $_[0] } sub r { [ $_[0], [ @@ -104,13 +103,26 @@ sub a2l { $a2l ? do { chomp(my $line = $a2l->lookup($addr)); $line =~ s/\Q?? at ??:0\E//; # FreeBSD - $line = Plack::Util::encode_html($line); $line =~ /\?\?/ ? "$line $exe($addr)" : ($line =~ /\S/ ? $line : "$exe($addr)"); } : "$exe($addr)" } } +sub decode_csv { + my ($in, $out) = @_; + while (<$in>) { + s/\\n/\0\0/g; + s!(["\0]) + ([^\("\0]+) # exe + \(([^\)"\0]+)\) # addr + (["\0])! + $1.a2l($2,$3).$4!gex; + s/\0\0/\\n/g; + $out->write($_); + } +} + sub call { # PSGI entry point my ($self, $env) = @_; my $uri = $env->{REQUEST_URI}; @@ -152,23 +164,16 @@ sub call { # PSGI entry point local %addr2line; # extract executable|library(address) if ($csv) { - while (<$c>) { - s/\\n/\0\0/g; - s!(["\0]) - ([^\("\0]+) # exe - \(([^\)"\0]+)\) # addr - (["\0])! - $1.a2l($2,$3).$4!gex; - s/\0\0/\\n/g; - $http_out->write($_); - } + decode_csv($c, $http_out); } else { while (<$c>) { s!> ([^\(<]+) # exe \(([^\)<]+)\) # addr '.a2l($1,$2).'<'!gex; + '>'.Plack::Util::encode_html( + a2l($1,$2)). + '<'!gex; $http_out->write($_); } } diff --git a/script/mwrap-decode-csv b/script/mwrap-decode-csv new file mode 100644 index 0000000..5bbc171 --- /dev/null +++ b/script/mwrap-decode-csv @@ -0,0 +1,57 @@ +#!perl -w +# Copyright (C) mwrap hackers +# License: GPL-3.0+ +# addr2line decoder for the output of MWRAP=dump_csv:$FILENAME +use v5.12; +use Devel::Mwrap::Rproxy; +use IO::Handle; +Devel::Mwrap::Rproxy::decode_csv(*STDIN{IO}, *STDOUT{IO}); +__END__ +=head1 NAME + +mwrap-decode-csv - decode non-Perl addresses from mwrap CSV dumps + +=head1 SYNOPSIS + + MWRAP=dump_csv:$FILENAME,bt:2 mwrap-perl COMMAND... + + mwrap-decode-csv <$FILENAME + +=head1 DESCRIPTION + +mwrap-decode-csv is a convenient wrapper for L +for decoding C backtraces from CSV files. + +It reads the CSV via standard input, and emits to standard output. + +It expects CSV files emitted by a L via +C or retrieved directly via C. + +It is not needed for CSVs retrieved via L, +since mwrap-rproxy already performs the same function as mwrap-decode-csv. + +To get useful C backtraces of Perl programs, C +directive must be used (carefully). See L. + +addr2line from GNU binutils 2.39+ (August 2022) is recommended to +support C addresses. + +=head1 CONTACT + +Feedback welcome via plain-text mail to L + +Mail archives are hosted at L + +=head1 COPYRIGHT + +Copyright all contributors L + +License: GPL-3.0+ L + +Source code is at L + +=head1 SEE ALSO + +L, L + +=cut -- cgit v1.2.3-24-ge0c7 From 64a55ae0ba1d09ccda458eb895d849e7d38cab81 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 11 Jan 2023 01:12:48 +0000 Subject: %p => PID expansion for dump_path + dump_csv This makes it possible to dump per-PID files for processes which fork. `%p' matches what the Linux sys.kernel.core_pattern sysctl understands. --- mwrap_core.h | 15 +++++++++++++++ script/mwrap-perl | 6 ++++++ t/mwrap.t | 15 +++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/mwrap_core.h b/mwrap_core.h index fff0538..86e4498 100644 --- a/mwrap_core.h +++ b/mwrap_core.h @@ -895,12 +895,27 @@ __attribute__ ((destructor)) static void mwrap_dtor(void) if (dump_path) { char *end = strchr(dump_path, ','); char buf[PATH_MAX]; + AUTO_FREE char *pid_path = NULL; if (end) { mwrap_assert((end - dump_path) < (intptr_t)sizeof(buf)); end = mempcpy(buf, dump_path, end - dump_path); *end = 0; dump_path = buf; } + + /* %p => PID expansion (Linux core_pattern uses %p, too) */ + if ((s = strchr(dump_path, '%')) && s[1] == 'p' && + /* don't allow injecting extra formats: */ + !strchr(s + 2, '%')) { + s[1] = 'd'; /* s/%p/%d/ to make asprintf happy */ + int n = asprintf(&pid_path, dump_path, (int)getpid()); + if (n < 0) + fprintf(stderr, + "asprintf failed: %m, dumping to %s\n", + dump_path); + else + dump_path = pid_path; + } dump_fd = open(dump_path, O_CLOEXEC|O_WRONLY|O_APPEND|O_CREAT, 0666); if (dump_fd < 0) { diff --git a/script/mwrap-perl b/script/mwrap-perl index eb29176..371aee6 100644 --- a/script/mwrap-perl +++ b/script/mwrap-perl @@ -76,6 +76,10 @@ Dumps the output at exit to a given filename: total_bytes call_count location +C<$FILENAME> may contain C<%p> where C<%p> is a placeholder for +the PID being dumped. No other use of C<%> is accepted, and +multiple C<%> means all C<%> (including C<%p>) are handled as-is. + =item dump_fd:$DESCRIPTOR As with dump_path, but dumps the output to a given file descriptor. @@ -90,6 +94,8 @@ but is subject to change in future releases. C without the C<:> may also be used in conjunction with C, such as C. +Expands C<%p> to the PID in C<$FILENAME> as described for C + =back =head1 HTTP POST API diff --git a/t/mwrap.t b/t/mwrap.t index ccd739b..783f6e7 100644 --- a/t/mwrap.t +++ b/t/mwrap.t @@ -42,6 +42,21 @@ my $dump = "$mwrap_tmp/dump"; $nr_comma = ($s =~ tr/,/,/); $nr_cr = ($s =~ tr/\n/\n/); ok($nr_comma > ($nr_cr * 4), 'CSV has more commas than CR'); + + $env->{MWRAP} = "dump_path:$dump.%p"; + mwrap_run('dump_path PID expansion', $env, '-e', $script); + my @d = grep(/\.\d+\z/, glob("$dump.*")); + is(scalar(@d), 1, 'got PID file') or diag explain([glob("$dump*")]); + unlink(@d) or BAIL_OUT "unlink: $!"; + + # don't allow injecting random formats + for my $fmt ('%p.%m', '%m.%p') { + my $fn = $dump.$fmt; + $env->{MWRAP} = "dump_path:$fn"; + mwrap_run("PID expansion fails on $fmt", $env, '-e', $script); + ok($fn, "$fmt used as-is"); + unlink($fn) or BAIL_OUT "unlink: $!"; + } } SKIP: { # C++ program which uses malloc via "new" -- cgit v1.2.3-24-ge0c7 From 20add372333a15adbb96b69c273f3f50e84189c7 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 11 Jan 2023 01:12:49 +0000 Subject: rewrite README and update manpage to favor CSV The CSV format has far more info, is self-describing, and Devel::Mwrap->dump never existed. I'm not sure if I care to support a Perl API now that the AF_UNIX HTTP interface exists. --- Mwrap.xs | 5 +++++ README | 61 +++++++++++++++++++++++++++++++------------------------ script/mwrap-perl | 26 ++++++++++++------------ 3 files changed, 52 insertions(+), 40 deletions(-) diff --git a/Mwrap.xs b/Mwrap.xs index 4d4c996..cbb7d15 100644 --- a/Mwrap.xs +++ b/Mwrap.xs @@ -6,6 +6,11 @@ #define MWRAP_PERL 1 #include "mwrap_core.h" +/* + * The Devel::Mwrap Perl API is probably no longer useful now that + * the AF_UNIX HTTP interface exists. It'll probably remain undocumented. + */ + /* * I hate typedefs, especially when they're hiding the fact that there's * a pointer, but XS needs this, apparently, and it does s/__/::/g diff --git a/README b/README index 1fa1dfb..88abe7d 100644 --- a/README +++ b/README @@ -4,11 +4,17 @@ Devel::Mwrap is designed to answer the question: Which lines of Perl are hitting malloc the most? -Devel::Mwrap wraps all malloc-family calls to trace the Perl source -location of such calls and bytes allocated at each callsite. It -can also function as a leak detector and show live allocations -at every call site. Depending on your application and workload, -the overhead is roughly a 50%-100% increase memory and runtime. +Devel::Mwrap wraps all malloc-family calls to trace the Perl +source location of such calls and bytes allocated at each +callsite. It is primarily designed to identify high memory use, +but may function as a leak detector as it can show live +allocations at every call site. Depending on your application +and workload, the overhead is roughly a 50%-100% increase memory +and runtime. + +It also gives configurable backtraces of all dynamically-linked +malloc callsites for any program where backtrace(3) works, +including programs not linked to Perl. It is thread-safe and requires the concurrent lock-free hash table from the Userspace RCU project: https://liburcu.org/ @@ -31,31 +37,30 @@ See `INSTALL' document == Usage -Devel::Mwrap works as an LD_PRELOAD and supplies a mwrap-perl script to -improve ease-of-use. You can set dump_path: in the MWRAP environment -variable to append the results to a log file: +Devel::Mwrap works as an LD_PRELOAD and supplies a mwrap-perl wrapper to +improve ease-of-use. You can set dump_csv: in the MWRAP environment +variable to append the results to a CSV file: - MWRAP=dump_path:/path/to/log mwrap-perl PERL_COMMAND + MWRAP=dump_csv:/path/to/csv.%p mwrap-perl COMMAND - # And to display the locations with the most allocations: - sort -k1,1rn dump, Devel::Mwrap->reset, Devel::Mwrap->each, etc. + MWRAP=socket_dir:/some/dir mwrap-perl COMMAND -However, Devel::Mwrap MUST be loaded via LD_PRELOAD to have any -effect in tracking malloc use. However, it is safe to keep -"use Devel::Mwrap" in performance-critical deployments, -as overhead is only incurred when used as an LD_PRELOAD. +And connect via `curl --unix-socket /some/dir/$PID.sock' or `mwrap-rproxy'. +See mwrap-rproxy(1p) and mwrap-perl(1p) manpages for more info. -The output of the Devel::Mwrap->dump is a text file with 3 columns: +== Compared to other tools - total_bytes call_count location +* mwrap-perl knows about Perl code, and an `mwrap' RubyGem exists, too -Where location is a Perl source location or an address retrieved -by backtrace_symbols(3). It is recommended to use the sort(1) -command on either of the first two columns to find the hottest -malloc locations. +* mwrap does not catch memory errors; use ASan, valgrind, or similar + +* mwrap is reasonably fast, fast enough for the author to run on + production-facing Perl daemons + +* the AF_UNIX HTTP interface allows inspecting live processes without + interruption instead of waiting for an exit dump == Known problems @@ -66,6 +71,8 @@ malloc locations. * Perl source files over 16.7 million lines long are not supported :P +* large C backtraces (off by default) are expensive for large programs + == Public mail archives (HTTP, Atom feeds, IMAP mailbox, NNTP group, POP3): https://80x24.org/mwrap-perl/ @@ -73,10 +80,10 @@ malloc locations. nntps://80x24.org/inbox.comp.lang.perl.mwrap https://80x24.org/mwrap-perl/_/text/help/#pop3 -No subscription nor real identities will ever be required to obtain support, -but HTML mail is rejected. Memory usage reductions start with you; -only send plain-text mail to us and do not top-post. HTML mail and -top-posting costs everybody memory and bandwidth. +No subscription nor real identities will ever be required to obtain +support or contribute, HTML mail is rejected. Memory usage reductions +start with you; only send plain-text mail to us and do not top-post. +HTML mail and top-posting costs everybody memory and bandwidth. mwrap-perl@80x24.org diff --git a/script/mwrap-perl b/script/mwrap-perl index 371aee6..cf88375 100644 --- a/script/mwrap-perl +++ b/script/mwrap-perl @@ -70,31 +70,31 @@ This may be changed via POST request (see below). Default: 0 -=item dump_path:$FILENAME +=item dump_csv:$FILENAME -Dumps the output at exit to a given filename: +Dump CSV to the given filename. - total_bytes call_count location +This output matches the HTTP server output and includes column headers, +but is subject to change in future releases. + +C without the C<:> may also be used in conjunction with +C, such as C. C<$FILENAME> may contain C<%p> where C<%p> is a placeholder for the PID being dumped. No other use of C<%> is accepted, and multiple C<%> means all C<%> (including C<%p>) are handled as-is. -=item dump_fd:$DESCRIPTOR - -As with dump_path, but dumps the output to a given file descriptor. +=item dump_path:$FILENAME -=item dump_csv:$FILENAME +Gives a simpler, legacy output compatible with the old Ruby version: -Dump CSV to the given filename. + total_bytes call_count location -This output matches the HTTP server output and includes column headers, -but is subject to change in future releases. +Expands C<%p> to the PID in C<$FILENAME> as described for C -C without the C<:> may also be used in conjunction with -C, such as C. +=item dump_fd:$DESCRIPTOR -Expands C<%p> to the PID in C<$FILENAME> as described for C +As with dump_path, but dumps the output to a given file descriptor. =back -- cgit v1.2.3-24-ge0c7 From 3d09a04398b23c1baeaecd0e17601fd4f5c277bb Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 11 Jan 2023 04:29:29 +0000 Subject: fix uninitialized warnings The `dump_fd' warning is legit but wasn't caught on my other system. The `lineno' warning pops up on my system with `-O0' --- mwrap_core.h | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/mwrap_core.h b/mwrap_core.h index 86e4498..a2c6d4a 100644 --- a/mwrap_core.h +++ b/mwrap_core.h @@ -388,9 +388,8 @@ static const COP *mwp_curcop(void) static const char *mw_perl_src_file_cstr(unsigned *lineno) { const COP *cop = mwp_curcop(); - if (!cop) return NULL; - *lineno = CopLINE(cop); - return CopFILE(cop); + *lineno = cop ? CopLINE(cop) : 0; + return cop ? CopFILE(cop) : NULL; } # define SRC_FILE_CSTR(lineno) mw_perl_src_file_cstr(lineno) #endif /* MWRAP_PERL */ @@ -922,9 +921,13 @@ __attribute__ ((destructor)) static void mwrap_dtor(void) fprintf(stderr, "open %s failed: %m\n", dump_path); goto out; } - } else if ((s = strstr(opt, "dump_fd:")) && - !sscanf(s, "dump_fd:%d", &dump_fd)) - goto out; + } else { + s = strstr(opt, "dump_fd:"); + if (!s) + goto out; + if (!sscanf(s, "dump_fd:%d", &dump_fd)) + goto out; + } /* allow dump_csv standalone for dump_fd */ if (!a.dump_csv && strstr(opt, "dump_csv")) -- cgit v1.2.3-24-ge0c7 From 223134c33d92dbdd4136335957d470c5861880b6 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Fri, 13 Jan 2023 07:53:45 +0000 Subject: mwrap-perl: update manpage to synopsis to favor CSV --- script/mwrap-perl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/mwrap-perl b/script/mwrap-perl index cf88375..c9a5666 100644 --- a/script/mwrap-perl +++ b/script/mwrap-perl @@ -31,8 +31,8 @@ mwrap-perl - run any command under mwrap # to trace a long-running program and access it via $DIR/$PID.sock: MWRAP=socket_dir:$DIR mwrap-perl COMMAND - # to trace a short-lived command and dump its output to a log: - MWRAP=dump_path:$FILENAME mwrap-perl COMMAND + # to trace a short-lived command and dump its output to a CSV: + MWRAP=dump_csv$FILENAME mwrap-perl COMMAND =head1 DESCRIPTION -- cgit v1.2.3-24-ge0c7 From 0295f1f53fdcfa7f6cd6696525325f3d12f6a660 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Fri, 13 Jan 2023 07:53:57 +0000 Subject: move _GNU_SOURCE definition This is to make the Ruby version happy. --- mwrap_core.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mwrap_core.h b/mwrap_core.h index a2c6d4a..a246fea 100644 --- a/mwrap_core.h +++ b/mwrap_core.h @@ -4,6 +4,9 @@ * Disclaimer: I don't really know my way around XS or Perl internals well */ #define _LGPL_SOURCE /* allows URCU to inline some stuff */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif #include "mymalloc.h" /* includes dlmalloc_c.h */ #ifndef MWRAP_PERL # define MWRAP_PERL 0 @@ -18,9 +21,6 @@ # define MWRAP_BT_MAX 32 #endif -#ifndef _GNU_SOURCE -# define _GNU_SOURCE -#endif #include #include #include -- cgit v1.2.3-24-ge0c7 From bca8045e15c2373196634be46eef4a93029c8417 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Fri, 13 Jan 2023 07:57:20 +0000 Subject: script/*: shorten and point to docs Try to keep these scripts as short as possible so it's easier to find the manpage. For -rproxy, I guess a small note at the top will have to do... --- script/mwrap-decode-csv | 1 - script/mwrap-perl | 23 ++++++++--------------- script/mwrap-rproxy | 2 +- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/script/mwrap-decode-csv b/script/mwrap-decode-csv index 5bbc171..ad73b8c 100644 --- a/script/mwrap-decode-csv +++ b/script/mwrap-decode-csv @@ -1,7 +1,6 @@ #!perl -w # Copyright (C) mwrap hackers # License: GPL-3.0+ -# addr2line decoder for the output of MWRAP=dump_csv:$FILENAME use v5.12; use Devel::Mwrap::Rproxy; use IO::Handle; diff --git a/script/mwrap-perl b/script/mwrap-perl index c9a5666..632deaf 100644 --- a/script/mwrap-perl +++ b/script/mwrap-perl @@ -1,24 +1,17 @@ -#!/usr/bin/perl -w +#!perl -w # Copyright (C) mwrap hackers # License: GPL-3.0+ use v5.12; use Devel::Mwrap; -my ($so) = grep(m!/Mwrap\.so\z!, @DynaLoader::dl_shared_objects); -defined($so) or die 'Mwrap.so not loaded'; -my $cur = $ENV{LD_PRELOAD}; +my ($so) = grep(m!/Mwrap\.so\z!, @DynaLoader::dl_shared_objects) + or die 'Mwrap.so not loaded'; if (!@ARGV || ($ARGV[0] // '') =~ /\A(?:-h|--help)\z/) { - require Pod::Usage; - Pod::Usage::pod2usage(@ARGV ? 0 : 1); + require Pod::Usage; Pod::Usage::pod2usage(@ARGV ? 0 : 1); } -if (defined $cur) { - my @cur = split(/[: \t]+/, $cur); - if (!grep(/\A\Q$so\E\z/, @cur)) { - # drop old redundant versions - my @keep = grep(!m!/Mwrap\.so$!, @cur); - $ENV{LD_PRELOAD} = join(':', $so, @keep); - } -} else { - $ENV{LD_PRELOAD} = $so; +my @cur = split(/[: \t]+/, $ENV{LD_PRELOAD} // ''); +if (!grep(/\A\Q$so\E\z/, @cur)) { # drop old redundant versions + my @keep = grep(!m!/Mwrap\.so$!, @cur); + $ENV{LD_PRELOAD} = join(':', $so, @keep); } exec @ARGV; __END__ diff --git a/script/mwrap-rproxy b/script/mwrap-rproxy index 056e80a..57fdde7 100644 --- a/script/mwrap-rproxy +++ b/script/mwrap-rproxy @@ -1,7 +1,7 @@ #!perl -w # Copyright (C) mwrap hackers # License: GPL-3.0+ -# thin wrapper for Devel::Mwrap::Rproxy +# thin wrapper for Devel::Mwrap::Rproxy (see __END__ below for manpage) use v5.12; # strict eval { require Plack::Runner } or die "Plack not installed: $@\n"; use Getopt::Long qw(:config no_ignore_case no_auto_abbrev pass_through); -- cgit v1.2.3-24-ge0c7 From 73d7e6dddf6a12b764d7570f3de50779c24e630b Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Fri, 13 Jan 2023 08:31:23 +0000 Subject: use perror(3) instead of fprintf(3) perror(3) is standardized and reduces our binary text size a bit: text data bss dec hex filename 80330 1888 4352 86570 1522a before/Mwrap.so 80043 1896 4352 86291 15113 after/Mwrap.so --- httpd.h | 24 ++++++++++++------------ mwrap_core.h | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/httpd.h b/httpd.h index 9219d36..8a105aa 100644 --- a/httpd.h +++ b/httpd.h @@ -221,7 +221,7 @@ static FILE *fbuf_init(struct mw_fbuf *fb) { fb->ptr = NULL; fb->fp = open_memstream(&fb->ptr, &fb->len); - if (!fb->fp) fprintf(stderr, "open_memstream: %m\n"); + if (!fb->fp) perror("open_memstream"); return fb->fp; } @@ -237,7 +237,7 @@ static int fbuf_close(struct mw_fbuf *fb) { int e = ferror(fb->fp) | fclose(fb->fp); fb->fp = NULL; - if (e) fprintf(stderr, "ferror|fclose: %m\n"); + if (e) perror("ferror|fclose"); return e; } @@ -279,7 +279,7 @@ static enum mw_qev h1_200(struct mw_h1 *h1, struct mw_fbuf *fb, const char *ct) */ off_t clen = ftello(fb->fp); if (clen < 0) { - fprintf(stderr, "ftello: %m\n"); + perror("ftello"); fbuf_close(fb); return h1_close(h1); } @@ -468,7 +468,7 @@ static off_t write_loc_name(FILE *fp, const struct src_loc *l) off_t beg = ftello(fp); if (beg < 0) { - fprintf(stderr, "ftello: %m\n"); + perror("ftello"); return beg; } if (l->f) { @@ -498,7 +498,7 @@ static off_t write_loc_name(FILE *fp, const struct src_loc *l) } off_t end = ftello(fp); if (end < 0) { - fprintf(stderr, "ftello: %m\n"); + perror("ftello"); return end; } return end - beg; @@ -868,7 +868,7 @@ static enum mw_qev h1_drain_input(struct mw_h1 *h1, struct mw_h1req *h1r, return h1_close(h1); default: /* ENOMEM, ENOBUFS, ... */ assert(errno != EBADF); - fprintf(stderr, "read: %m\n"); + perror("read"); return h1_close(h1); } } @@ -1001,7 +1001,7 @@ static enum mw_qev h1_event_step(struct mw_h1 *h1, struct mw_h1d *h1d) if (!h1r) { h1r = h1d->shared_h1r = malloc(sizeof(*h1r)); if (!h1r) { - fprintf(stderr, "h1r malloc: %m\n"); + perror("h1r malloc"); return h1_close(h1); } } @@ -1045,7 +1045,7 @@ static enum mw_qev h1_event_step(struct mw_h1 *h1, struct mw_h1d *h1d) return h1_close(h1); default: /* ENOMEM, ENOBUFS, ... */ assert(errno != EBADF); - fprintf(stderr, "read: %m\n"); + perror("read"); return h1_close(h1); } } @@ -1153,7 +1153,7 @@ static void h1d_unlink(struct mw_h1d *h1d, bool do_close) if (h1d->lfd < 0 || !h1d->pid_len) return; if (getsockname(h1d->lfd, &sa.any, &len) < 0) { - fprintf(stderr, "getsockname: %m\n"); + perror("getsockname"); return; } if (do_close) { /* only safe to close if thread isn't running */ @@ -1219,13 +1219,13 @@ static int h1d_init(struct mw_h1d *h1d, const char *menv) return fprintf(stderr, "unlink(%s): %m\n", sa.un.sun_path); h1d->lfd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (h1d->lfd < 0) - return fprintf(stderr, "socket: %m\n"); + return perror("socket"), 1; if (bind(h1d->lfd, &sa.any, (socklen_t)sizeof(sa)) < 0) { - fprintf(stderr, "bind: %m\n"); + perror("bind"); goto close_fail; } if (listen(h1d->lfd, 1024) < 0) { - fprintf(stderr, "listen: %m\n"); + perror("listen"); goto close_fail; } h1d->alive = 1; /* runs in parent, before pthread_create */ diff --git a/mwrap_core.h b/mwrap_core.h index a246fea..a84cd6d 100644 --- a/mwrap_core.h +++ b/mwrap_core.h @@ -742,7 +742,7 @@ char **bt_syms(void * const *addrlist, uint32_t size) #else /* make FreeBSD look like glibc output: */ char **s = backtrace_symbols_fmt(addrlist, size, "%f(%n%D) [%a]"); #endif - if (!s) fprintf(stderr, "backtrace_symbols: %m\n"); + if (!s) perror("backtrace_symbols"); return s; } @@ -1057,7 +1057,7 @@ __attribute__((constructor)) static void mwrap_ctor(void) h->real = h; call_rcu(&h->as.dead, free_hdr_rcu); } else - fprintf(stderr, "malloc: %m\n"); + perror("malloc"); h1d_start(); CHECK(int, 0, pthread_sigmask(SIG_SETMASK, &old, NULL)); -- cgit v1.2.3-24-ge0c7