about summary refs log tree commit homepage
path: root/ext/mwrap/mwrap_core.h
diff options
context:
space:
mode:
Diffstat (limited to 'ext/mwrap/mwrap_core.h')
-rw-r--r--ext/mwrap/mwrap_core.h72
1 files changed, 55 insertions, 17 deletions
diff --git a/ext/mwrap/mwrap_core.h b/ext/mwrap/mwrap_core.h
index 827ee7b..a84cd6d 100644
--- a/ext/mwrap/mwrap_core.h
+++ b/ext/mwrap/mwrap_core.h
@@ -4,7 +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 */
-#define _GNU_SOURCE
+#ifndef _GNU_SOURCE
+#        define _GNU_SOURCE
+#endif
 #include "mymalloc.h" /* includes dlmalloc_c.h */
 #ifndef MWRAP_PERL
 #        define MWRAP_PERL 0
@@ -19,9 +21,6 @@
 #        define        MWRAP_BT_MAX 32
 #endif
 
-#ifndef _GNU_SOURCE
-#        define _GNU_SOURCE
-#endif
 #include <execinfo.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -389,11 +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;
-        const char *fn = CopFILE(cop);
-        if (!fn) return NULL;
-        *lineno = CopLINE(cop);
-        return fn;
+        *lineno = cop ? CopLINE(cop) : 0;
+        return cop ? CopFILE(cop) : NULL;
 }
 #        define SRC_FILE_CSTR(lineno) mw_perl_src_file_cstr(lineno)
 #endif /* MWRAP_PERL */
@@ -735,6 +731,7 @@ enomem:
 struct dump_arg {
         FILE *fp;
         size_t min;
+        bool dump_csv;
 };
 
 char **bt_syms(void * const *addrlist, uint32_t size)
@@ -745,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;
 }
 
@@ -757,12 +754,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);
@@ -860,7 +861,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;
@@ -873,27 +874,64 @@ __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];
+                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) {
                         fprintf(stderr, "open %s failed: %m\n", dump_path);
                         goto out;
                 }
+        } else {
+                s = strstr(opt, "dump_fd:");
+                if (!s)
+                        goto out;
+                if (!sscanf(s, "dump_fd:%d", &dump_fd))
+                        goto out;
         }
-        else if (!sscanf(opt, "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);
 
@@ -1019,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));