about summary refs log tree commit homepage
path: root/mwrap_httpd.h
diff options
context:
space:
mode:
Diffstat (limited to 'mwrap_httpd.h')
-rw-r--r--mwrap_httpd.h166
1 files changed, 92 insertions, 74 deletions
diff --git a/mwrap_httpd.h b/mwrap_httpd.h
index bfa723d..bd37fb2 100644
--- a/mwrap_httpd.h
+++ b/mwrap_httpd.h
@@ -102,6 +102,7 @@ struct h1_src_loc {
         size_t live;
         size_t max_life;
         off_t lname_len;
+        const struct src_loc *sl;
         char *loc_name;
 };
 
@@ -333,76 +334,76 @@ static void write_html(FILE *fp, const char *s, size_t len)
         }
 }
 
-static void write_uri(FILE *fp, const char *s, size_t len)
+/* URI-safe base-64 (RFC 4648) */
+static void write_b64_url(FILE *fp, const uint8_t *in, size_t len)
 {
-#define CGI_URI_OK(x) \
-        ((x >= 'a' && x <= 'z') || \
-         (x >= 'A' && x <= 'Z') || \
-         (x >= '0' && x <= '9') || \
-         (x == '.' || x == '-' || x == '_' || x == '~'))
-        for (; len--; ++s) {
-                if (caa_likely(CGI_URI_OK(*s))) {
-                        fputc(*s, fp);
-                } else {
-                        static const char cgi_digitmap[] = "0123456789ABCDEF";
-                        unsigned char c = *s;
-                        char x[3];
-
-                        x[2] = cgi_digitmap[(c % 16)];
-                        x[1] = cgi_digitmap[((c / 16) % 16)];
-                        x[0] = '%';
-                        fwrite(x, sizeof(x), 1, fp);
-                }
+        static const uint8_t b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                        "abcdefghijklmnopqrstuvwxyz" "0123456789-_";
+        uint8_t o[4];
+        while (len > 3) {
+                o[0] = b64[(in[0] >> 2) & 0x3f];
+                o[1] = b64[((in[0] << 4) | (in[1] >> 4)) & 0x3f];
+                o[2] = b64[((in[1] << 2) | (in[2] >> 6)) & 0x3f];
+                o[3] = b64[in[2] & 0x3f];
+                fwrite(o, sizeof(o), 1, fp);
+                len -= 3;
+                in += 3;
+        }
+        if (len) {
+                size_t i = 2;
+
+                o[0] = b64[(in[0] >> 2) & 0x3f];
+                o[1] = b64[((in[0] << 4) | (--len ? (in[1] >> 4) : 0)) & 0x3f];
+                if (len)
+                        o[i++] = b64[((in[1] << 2) |
+                                        (--len ? in[2] >> 6 : 0)) & 0x3f];
+                if (len)
+                        o[i++] = b64[in[2] & 0x3f];
+                fwrite(o, i, 1, fp);
         }
-#undef CGI_URI_OK
-}
-
-static bool is_hex(int x)
-{
-        return (((x) >= '0' && (x) <= '9') ||
-               ((x) >= 'a' && (x) <= 'f') ||
-               ((x) >= 'A' && (x) <= 'F'));
-}
-
-static int xtoupper(int x)
-{
-        return (x >= 'a' && x <= 'f') ? (x & ~0x20) : x;
-}
-
-static int hexchar_to_int(int x)
-{
-        return (x < 'A') ? (x - '0') : (xtoupper(x) - 'A' + 10);
-}
-
-static int hexpair_to_int(int x1, int x2)
-{
-        return ((hexchar_to_int(x1) << 4) | hexchar_to_int(x2));
 }
 
 /* unescapes @s in-place and adjusts @len */
-static const char *uri_unescape(const char *s, size_t *len)
+static bool b64_url_decode(const void *ptr, size_t *len)
 {
-        union { const char *in; char *out; } deconst;
-        size_t orig = *len;
-        char *out;
-
-        deconst.in = s;
-        out = deconst.out;
-        for (; orig--; s++) {
-                if (caa_unlikely(*s == '%') && orig > 1 &&
-                                caa_likely(is_hex(s[1])) &&
-                                caa_likely(is_hex(s[2]))) {
-                        orig -= 2;
-                        *len -= 2;
-                        *out++ = hexpair_to_int(s[1], s[2]);
-                        s += 2;
-                } else {
-                        *out++ = *s;
+        union { const void *in; uint8_t *out; } deconst;
+        const uint8_t *in = ptr;
+        uint8_t u = 0;
+
+        deconst.in = ptr;
+        uint8_t *out = deconst.out;
+
+        for (size_t i = 0; i < *len; ++i) {
+                uint8_t c = in[i];
+
+                switch (c) {
+                case 'A' ... 'Z': c -= 'A'; break;
+                case 'a' ... 'z': c -= ('a' - 26); break;
+                case '0' ... '9': c -= ('0' - 52); break;
+                case '-': c = 62; break;
+                case '_': c = 63; break;
+                default: return false;
+                }
+
+                mwrap_assert(c <= 63);
+                switch (i % 4) {
+                case 0: u = c << 2; break;
+                case 1:
+                        *out++ = u | c >> 4;
+                        u = (c & 0xf) << 4;
+                        break;
+                case 2:
+                        *out++ = u | c >> 2;
+                        u = (c & 0x3) << 6;
+                        break;
+                case 3: *out++ = u | c;
                 }
         }
-        return deconst.in;
+        *len = out - in;
+        return true;
 }
 
+/* keep this consistent with Mwrap.xs location_string */
 static off_t write_loc_name(FILE *fp, const struct src_loc *l)
 {
         off_t beg = ftello(fp);
@@ -417,12 +418,21 @@ static off_t write_loc_name(FILE *fp, const struct src_loc *l)
                         FPUTS(":-", fp);
                 else
                         fprintf(fp, ":%zu", l->lineno);
-        } else {
+        }
+        if (l->bt_len) {
                 char **s = bt_syms(l->bt, l->bt_len);
                 if (!s) return -1;
+                if (l->f) fputc('\n', fp);
+
+                /* omit local " [$ADDRESS]" if doing deep backtraces */
+                for (uint32_t i = 0; i < l->bt_len; ++i) {
+                        char *c = memrchr(s[i], '[', strlen(s[i]));
+                        if (c && c > (s[i] + 2) && c[-1] == ' ')
+                                c[-1] = '\0';
+                }
 
                 fputs(s[0], fp);
-                for (uint32_t i = 1; i < l->bt_len; i++) {
+                for (uint32_t i = 1; i < l->bt_len; ++i) {
                         fputc('\n', fp);
                         fputs(s[i], fp);
                 }
@@ -445,7 +455,6 @@ static struct h1_src_loc *accumulate(unsigned long min, size_t *hslc, FILE *lp)
         struct cds_lfht *t = CMM_LOAD_SHARED(totals);
         struct cds_lfht_iter iter;
         struct src_loc *l;
-        ++locating;
         if (t) cds_lfht_for_each_entry(t, &iter, l, hnode) {
                 size_t freed = uatomic_read(&l->freed_bytes);
                 size_t total = uatomic_read(&l->total);
@@ -461,11 +470,10 @@ static struct h1_src_loc *accumulate(unsigned long min, size_t *hslc, FILE *lp)
                                 (long double)hsl.frees) :
                         HUGE_VAL;
                 hsl.max_life = uatomic_read(&l->max_lifespan);
+                hsl.sl = l;
                 hsl.lname_len = write_loc_name(lp, l);
                 fwrite(&hsl, sizeof(hsl), 1, fp);
         }
-        --locating;
-        mwrap_assert(!locating);
         rcu_read_unlock();
 
         struct h1_src_loc *hslv;
@@ -486,9 +494,11 @@ static enum mw_qev each_at(struct mw_h1 *h1, struct mw_h1req *h1r)
         size_t len = h1r->path_len - (sizeof("/at/") - 1);
         size_t min = 0;
 
-        loc = uri_unescape(loc, &len);
-        if (len >= PATH_MAX) return h1_400(h1);
-        struct src_loc *l = mwrap_get(loc, len);
+        if (!b64_url_decode(loc, &len) || len >= PATH_MAX)
+                return h1_400(h1);
+
+        struct src_loc *l = mwrap_get_bin(loc, len);
+
         if (!l) return h1_404(h1);
 
         struct mw_membuf lname;
@@ -503,7 +513,9 @@ static enum mw_qev each_at(struct mw_h1 *h1, struct mw_h1req *h1r)
         if (!fp) return h1_close(h1);
         FPUTS("<html><head><title>", fp);
         write_html(fp, lname.ptr, lname.len);
-        FPUTS("</title></head><body><p>live allocations at ", fp);
+        FPUTS("</title></head><body><p>live allocations at", fp);
+        if (bt_req_depth) FPUTS("<br/>", fp);
+        else fputc('\n', fp);
         write_html(fp, lname.ptr, lname.len);
         free(lname.ptr);
 
@@ -514,7 +526,6 @@ static enum mw_qev each_at(struct mw_h1 *h1, struct mw_h1req *h1r)
                 "<th>address</th></tr>", fp);
 
         rcu_read_lock();
-        ++locating;
         struct alloc_hdr *h;
         cds_list_for_each_entry_rcu(h, &l->allocs, anode) {
                 size_t size = uatomic_read(&h->size);
@@ -522,8 +533,6 @@ static enum mw_qev each_at(struct mw_h1 *h1, struct mw_h1req *h1r)
                         fprintf(fp, "<tr><td>%zu</td><td>%zu</td><td>%p</td>\n",
                                 size, h->as.live.gen, h->real);
         }
-        --locating;
-        mwrap_assert(!locating);
         rcu_read_unlock();
         FPUTS("</table></body></html>", fp);
         return h1_200(h1, fp, &mb);
@@ -574,7 +583,11 @@ static enum mw_qev each_gt(struct mw_h1 *h1, struct mw_h1req *h1r,
         size_t age = uatomic_read(&total_bytes_inc);
         fprintf(fp, "<p>Current age: %zu (live: %zu)",
                 age, age - uatomic_read(&total_bytes_dec));
-        FPUTS("<table><tr>", fp);
+
+        if (bt_req_depth) /* need borders to distinguish multi-level traces */
+                FPUTS("<table\nborder=1><tr>", fp);
+        else /* save screen space if only tracing one line */
+                FPUTS("<table><tr>", fp);
 
         int (*cmp)(const void *, const void *) = NULL;
         for (size_t i = 0; i < CAA_ARRAY_SIZE(fields); i++) {
@@ -603,7 +616,11 @@ static enum mw_qev each_gt(struct mw_h1 *h1, struct mw_h1req *h1r,
                         hsl->bytes, hsl->allocations, hsl->frees,
                         hsl->live, hsl->mean_life, hsl->max_life);
                 FPUTS("<td><a\nhref=\"../at/", fp);
-                write_uri(fp, hsl->loc_name, hsl->lname_len);
+
+                /* yes, we're writing our memory addresses into the URI */
+                write_b64_url(fp, (const void *)&hsl->sl->f,
+                                src_loc_hash_len(hsl->sl));
+
                 FPUTS("\">", fp);
                 write_html(fp, hsl->loc_name, hsl->lname_len);
                 FPUTS("</a></td></tr>", fp);
@@ -1025,6 +1042,7 @@ static void *h1d_run(void *x) /* pthread_create cb */
         int rc;
         struct mw_h1 *h1, *nxt;
         enum mw_qev ev;
+        locating = 1; /* don't report our own memory use */
 
         for (; uatomic_read(&h1d->alive); ) {
                 if (poll_add(h1d, h1d->lfd, POLLIN))