mwrap (Perl version) user+dev discussion/patches/pulls/bugs/help
 help / color / mirror / code / Atom feed
From: Eric Wong <e@80x24.org>
To: mwrap-perl@80x24.org
Subject: [PATCH 1/3] support changing bt_req_depth dynamically
Date: Fri, 16 Dec 2022 14:39:31 +0000	[thread overview]
Message-ID: <20221216143933.22430-2-e@80x24.org> (raw)
In-Reply-To: <20221216143933.22430-1-e@80x24.org>

This is a valuable tuning knob and allows users to forgo
restarting an app to change backtrace depth.  A /reset is
recommended.
---
 Mwrap.xs     | 16 ++++++++++++
 httpd.h      | 70 +++++++++++++++++++++++++++++++++++++++++++++-------
 mwrap_core.h | 11 +++++----
 t/httpd.t    |  4 +++
 t/mwrap.t    |  8 ++++++
 5 files changed, 95 insertions(+), 14 deletions(-)

diff --git a/Mwrap.xs b/Mwrap.xs
index 568ec2b..846d89d 100644
--- a/Mwrap.xs
+++ b/Mwrap.xs
@@ -132,6 +132,22 @@ mwrap_reset()
 CODE:
 	mwrap_reset();
 
+unsigned
+mwrap_bt_depth(arg = &PL_sv_undef)
+	SV *arg;
+CODE:
+	if (SvOK(arg)) {
+		UV n = SvUVx(arg);
+		if (n > MWRAP_BT_MAX)
+			n = MWRAP_BT_MAX;
+		CMM_STORE_SHARED(bt_req_depth, (uint32_t)n);
+		RETVAL = n;
+	} else {
+		RETVAL = CMM_LOAD_SHARED(bt_req_depth);
+	}
+OUTPUT:
+	RETVAL
+
 Devel::Mwrap::SrcLoc
 mwrap_get(loc)
 	SV *loc;
diff --git a/httpd.h b/httpd.h
index 9b4ebe7..fc7b873 100644
--- a/httpd.h
+++ b/httpd.h
@@ -41,6 +41,7 @@
 #define URL "https://80x24.org/mwrap-perl.git/about"
 #define TYPE_HTML "text/html; charset=UTF-8"
 #define TYPE_CSV "text/csv"
+#define TYPE_PLAIN "text/plain"
 
 enum mw_qev {
 	MW_QEV_IGNORE = 0,
@@ -341,6 +342,15 @@ static enum mw_qev h1_do_trim(struct mw_h1 *h1)
 	return h1_res_oneshot(h1, r200, sizeof(r200) - 1);
 }
 
+static enum mw_qev h1_do_ctl_finish(struct mw_h1 *h1)
+{
+	struct mw_fbuf plain;
+	FILE *fp = wbuf_init(&plain);
+	if (!fp) return h1_close(h1);
+	fprintf(fp, "MWRAP=bt:%u\n", (unsigned)CMM_LOAD_SHARED(bt_req_depth));
+	return h1_200(h1, &plain, TYPE_PLAIN);
+}
+
 #define PATH_SKIP(h1r, pfx) path_skip(h1r, pfx, sizeof(pfx) - 1)
 static const char *path_skip(struct mw_h1req *h1r, const char *pfx, size_t len)
 {
@@ -570,7 +580,7 @@ static enum mw_qev each_at(struct mw_h1 *h1, struct mw_h1req *h1r)
 	FPUTS("<html><head><title>", fp);
 	write_html(fp, lb.ptr, lb.len);
 	FPUTS("</title></head><body><p>live allocations at:", fp);
-	if (bt_req_depth) FPUTS("<br/>", fp);
+	if (l->bt_len > 1 || (l->bt_len == 1 && l->f)) FPUTS("<br/>", fp);
 	else fputc(' ', fp);
 	write_html(fp, lb.ptr, lb.len);
 
@@ -627,13 +637,14 @@ static enum mw_qev each_gt(struct mw_h1 *h1, struct mw_h1req *h1r,
 	if (!fp) return h1_close(h1);
 
 	if (!csv) {
+		unsigned depth = (unsigned)CMM_LOAD_SHARED(bt_req_depth);
 		fprintf(fp, "<html><head><title>mwrap each &gt;%lu"
 			"</title></head><body><p>mwrap each &gt;%lu "
 			"(change `%lu' in URL to adjust filtering) - "
-			"MWRAP=bt:%u", min, min, min, (unsigned)bt_req_depth);
+			"MWRAP=bt:%u", min, min, min, depth);
 		show_stats(fp);
 		/* need borders to distinguish multi-level traces */
-		if (bt_req_depth)
+		if (depth)
 			FPUTS("<table\nborder=1><tr>", fp);
 		else /* save screen space if only tracing one line */
 			FPUTS("<table><tr>", fp);
@@ -757,6 +768,8 @@ static enum mw_qev h1_dispatch(struct mw_h1 *h1, struct mw_h1req *h1r)
 			return h1_do_reset(h1);
 		if (h1r->path_len == 5 && !memcmp(h1r->path, "/trim", 5))
 			return h1_do_trim(h1);
+		if (h1r->path_len == 4 && !memcmp(h1r->path, "/ctl", 4))
+			return h1_do_ctl_finish(h1);
 	}
 	return h1_404(h1);
 }
@@ -816,12 +829,31 @@ static enum mw_qev h1_drain_input(struct mw_h1 *h1, struct mw_h1req *h1r)
 	return h1_dispatch(h1, h1r);
 }
 
+static bool valid_end(const char *end)
+{
+	switch (*end) {
+	case '\r': case ' ': case '\t': case '\n': return true;
+	default: return false;
+	}
+}
+
+/* no error reporting, too much code */
+static void ctl_set(struct mw_h1 *h1, long n)
+{
+	if (n >= 0) {
+		if (n > MWRAP_BT_MAX)
+			n = MWRAP_BT_MAX;
+		CMM_STORE_SHARED(bt_req_depth, (uint32_t)n);
+	}
+}
+
 static enum mw_qev h1_parse_harder(struct mw_h1 *h1, struct mw_h1req *h1r)
 {
 	enum { HDR_IGN, HDR_CONN, HDR_XENC, HDR_CLEN } cur = HDR_IGN;
 	bool conn_set = false;
 	char *end;
 	struct phr_header *hdr = h1r->hdr;
+	long depth = -1;
 
 	h1->prev_len = 0;
 	h1->has_input = 0;
@@ -837,8 +869,21 @@ static enum mw_qev h1_parse_harder(struct mw_h1 *h1, struct mw_h1req *h1r)
 			cur = HDR_CONN;
 		else if (NAME_EQ(hdr, "Trailer"))
 			return h1_400(h1);
-		else if (hdr->name)
+		else if (hdr->name) {
 			cur = HDR_IGN;
+			/*
+			 * don't want to increase code to deal with POST
+			 * request bodies, so let pico handle parameters in
+			 * HTTP request headers, instead.
+			 */
+			if (NAME_EQ(hdr, "X-Mwrap-BT-Depth")) {
+				errno = 0;
+				depth = strtol(hdr->value, &end, 10);
+				if (errno || !valid_end(end))
+					depth = -1;
+			}
+		}
+
 		/* else: continuation line */
 		if (!hdr->value_len)
 			continue;
@@ -860,12 +905,8 @@ static enum mw_qev h1_parse_harder(struct mw_h1 *h1, struct mw_h1req *h1r)
 			h1->has_input = 1;
 			errno = 0;
 			h1->in_len = strtoul(hdr->value, &end, 10);
-			if (errno)
+			if (errno || !valid_end(end))
 				return h1_400(h1);
-			switch (*end) {
-			case '\r': case ' ': case '\t': case '\n': break;
-			default: return h1_400(h1);
-			}
 			break;
 		case HDR_IGN:
 			break;
@@ -883,6 +924,17 @@ static enum mw_qev h1_parse_harder(struct mw_h1 *h1, struct mw_h1req *h1r)
 	} else {
 		return h1_404(h1);
 	}
+
+	/*
+	 * special case for /ctl, since I don't feel like parsing queries
+	 * in the request body (ensure no query string, too)
+	 */
+	if (h1r->method_len == 4 && !memcmp(h1r->method, "POST", 4)) {
+		if (h1r->path_len == 4 && !memcmp(h1r->path, "/ctl", 4))
+			ctl_set(h1, depth);
+	}
+
+	/* break off QUERY_STRING */
 	h1r->qstr = memchr(h1r->path, '?', h1r->path_len);
 	if (h1r->qstr) {
 		++h1r->qstr; /* ignore '?' */
diff --git a/mwrap_core.h b/mwrap_core.h
index 3fd67f1..9e4f065 100644
--- a/mwrap_core.h
+++ b/mwrap_core.h
@@ -13,6 +13,7 @@
 typedef void COP;
 #endif
 
+/* set a sensible max to avoid stack overflows */
 #ifndef MWRAP_BT_MAX
 #	define	MWRAP_BT_MAX 32
 #endif
@@ -136,14 +137,14 @@ static void *my_mempcpy(void *dest, const void *src, size_t n)
 
 
 #define SRC_LOC_BT(bt) union stk_bt bt; do { \
-	uint32_t depth = locating ? 1 : bt_req_depth; \
+	uint32_t depth = locating ? 1 : CMM_LOAD_SHARED(bt_req_depth); \
 	switch (depth) { \
 	case 0: \
 	case 1: bt.sl.bt_len = 1; bt.sl.bt[0] = RETURN_ADDRESS(0); break; \
 	default: /* skip 1st level of BT since thats our function */ \
-		mwrap_assert(bt_req_depth <= MWRAP_BT_MAX); \
+		mwrap_assert(depth <= MWRAP_BT_MAX); \
 		++locating; \
-		long n = (long)backtrace(bt_dst(&bt), bt_req_depth); \
+		long n = (long)backtrace(bt_dst(&bt), depth); \
 		--locating; \
 		bt.sl.bt_len = n <= 1 ? 0 : (uint32_t)n - 1; \
 		if (n > 1) mwrap_assert(bt.sl.bt[0] == RETURN_ADDRESS(0)); \
@@ -1026,9 +1027,9 @@ __attribute__((constructor)) static void mwrap_ctor(void)
 			char *end;
 			unsigned long n = strtoul(bt, &end, 10);
 			if (n && !errno && (*end == ',' || *end == 0)) {
-				if (n >= MWRAP_BT_MAX)
+				if (n > MWRAP_BT_MAX)
 					n = MWRAP_BT_MAX;
-				bt_req_depth = (uint32_t)n;
+				CMM_STORE_SHARED(bt_req_depth, (uint32_t)n);
 			}
 		}
 	}
diff --git a/t/httpd.t b/t/httpd.t
index 176c6ed..3fe9c1f 100644
--- a/t/httpd.t
+++ b/t/httpd.t
@@ -157,6 +157,10 @@ SKIP: {
 	$rc = system(qw(curl -vsSf --unix-socket), $sock, '-o', $cout,
 		'-d', 'x=y', "http://0/$pid/reset");
 	is($rc, 0, 'curl /reset');
+	$rc = system(qw(curl -vsSf --unix-socket), $sock, '-o', $cout,
+		'-HX-Mwrap-BT-Depth:10', '-XPOST', "http://0/$pid/ctl");
+	is($rc, 0, 'curl /ctl (X-Mwrap-BT-Depth)');
+	like(slurp($cout), qr/\bMWRAP=bt:10\b/, 'changed bt depth');
 };
 
 
diff --git a/t/mwrap.t b/t/mwrap.t
index bf6ae6e..76a2366 100644
--- a/t/mwrap.t
+++ b/t/mwrap.t
@@ -166,4 +166,12 @@ EOM
 		'aligned_alloc + cfree function ran w/o crashing');
 };
 
+is(Devel::Mwrap::bt_depth(), 0, 'default bt depth is zero');
+is(Devel::Mwrap::bt_depth(5), 5, 'set depth to reasonable level');
+is(Devel::Mwrap::bt_depth(), 5, 'depth stays at 5');
+is(Devel::Mwrap::bt_depth(500), 32, 'depth clamped to 32 when 500 attempted');
+is(Devel::Mwrap::bt_depth(), 32, 'depth stayed clamped at 32');
+is(Devel::Mwrap::bt_depth(undef), 32, 'depth stayed clamped at 32');
+is(Devel::Mwrap::bt_depth(-1), 32, 'depth stayed clamped at 32');
+
 done_testing;

  reply	other threads:[~2022-12-16 14:39 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-12-16 14:39 [PATCH 0/3] fleshing out some things Eric Wong
2022-12-16 14:39 ` Eric Wong [this message]
2022-12-16 14:39 ` [PATCH 2/3] support symlink install Eric Wong
2022-12-16 14:39 ` [PATCH 3/3] various documentation updates Eric Wong

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20221216143933.22430-2-e@80x24.org \
    --to=e@80x24.org \
    --cc=mwrap-perl@80x24.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mwrap-perl.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).