From 5fc775e2efd1adf4985d9abcb1d970dcaa944555 Mon Sep 17 00:00:00 2001 From: Yichao Zhou Date: Thu, 13 Apr 2017 22:06:19 +0200 Subject: [PATCH 1/2] log.h: LOG_CALLSTACK --- src/nvim/log.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/nvim/log.h b/src/nvim/log.h index 221f0bbaf6..48be7606e7 100644 --- a/src/nvim/log.h +++ b/src/nvim/log.h @@ -61,6 +61,30 @@ __VA_ARGS__) #endif +#if defined(__linux__) +# include +# define LOG_CALLSTACK(prefix) \ + do { \ + void *trace[100]; \ + int trace_size = backtrace(trace, 100); \ + \ + char exe[1024]; \ + ssize_t elen = readlink("/proc/self/exe", exe, sizeof(exe) - 1); \ + exe[elen] = 0; \ + \ + for (int i = 1; i < trace_size; i++) { \ + char buf[256]; \ + snprintf(buf, sizeof(buf), "addr2line -e %s -f -p %p", exe, trace[i]); \ + FILE *fp = popen(buf, "r"); \ + while (fgets(buf, sizeof(buf) - 1, fp) != NULL) { \ + buf[strlen(buf)-1] = 0; \ + do_log(DEBUG_LOG_LEVEL, __func__, __LINE__, true, prefix "%s", buf); \ + } \ + fclose(fp); \ + } \ + } while (0) +#endif + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "log.h.generated.h" #endif From eb2473e9ea0a875b79612f84d1fae32b670d9b88 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 23 Jul 2017 17:00:30 +0200 Subject: [PATCH 2/2] log: log_callstack() --- src/nvim/README.md | 19 +++++++++++++------ src/nvim/log.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++ src/nvim/log.h | 22 +--------------------- 3 files changed, 61 insertions(+), 27 deletions(-) diff --git a/src/nvim/README.md b/src/nvim/README.md index 660e3d1010..1c1c3c364e 100644 --- a/src/nvim/README.md +++ b/src/nvim/README.md @@ -1,13 +1,20 @@ +Nvim core source +================ -Module-specific details are documented at the top of each module (e.g. -terminal.c, screen.c). +Module-specific details are documented at the top of each module (`terminal.c`, +`screen.c`, ...). -See also `:help development`. +See `:help development` for more guidelines. -UI Debugging ------------- +Logs +---- -At `DEBUG_LOG_LEVEL`, all UI events are logged. +Low-level log messages sink to `$NVIM_LOG_FILE`. + +You can use `LOG_CALLSTACK()` anywhere in the source to log the current +stacktrace. (Currently Linux-only.) + +UI events are logged at level 0 (`DEBUG_LOG_LEVEL`). rm -rf build/ make CMAKE_EXTRA_FLAGS="-DMIN_LOG_LEVEL=0" diff --git a/src/nvim/log.c b/src/nvim/log.c index f1dbe61dda..252fe5438d 100644 --- a/src/nvim/log.c +++ b/src/nvim/log.c @@ -173,6 +173,53 @@ FILE *open_log_file(void) return stderr; } +#if defined(__linux__) +# include +void log_callstack(const char *const func_name, const int line_num) +{ + void *trace[100]; + int trace_size = backtrace(trace, ARRAY_SIZE(trace)); + + char exepath[MAXPATHL] = { 0 }; + size_t exepathlen = MAXPATHL; + if (os_exepath(exepath, &exepathlen) != 0) { + abort(); + } + assert(24 + exepathlen < IOSIZE); // Must fit in `cmdbuf` below. + + do_log(DEBUG_LOG_LEVEL, func_name, line_num, true, "trace:"); + + char cmdbuf[IOSIZE + (20 * ARRAY_SIZE(trace))]; + snprintf(cmdbuf, sizeof(cmdbuf), "addr2line -e %s -f -p", exepath); + for (int i = 1; i < trace_size; i++) { + char buf[20]; // 64-bit pointer 0xNNNNNNNNNNNNNNNN with leading space. + snprintf(buf, sizeof(buf), " %p", trace[i]); + xstrlcat(cmdbuf, buf, sizeof(cmdbuf)); + } + // Now we have a command string like: + // addr2line -e /path/to/exe -f -p 0x123 0x456 ... + + log_lock(); + FILE *log_file = open_log_file(); + if (log_file == NULL) { + goto end; + } + + FILE *fp = popen(cmdbuf, "r"); + char linebuf[IOSIZE]; + while (fgets(linebuf, sizeof(linebuf) - 1, fp) != NULL) { + fprintf(log_file, " %s", linebuf); + } + pclose(fp); + + if (log_file != stderr && log_file != stdout) { + fclose(log_file); + } +end: + log_unlock(); +} +#endif + static bool do_log_to_file(FILE *log_file, int log_level, const char *func_name, int line_num, bool eol, const char* fmt, ...) diff --git a/src/nvim/log.h b/src/nvim/log.h index 48be7606e7..2bd18f5776 100644 --- a/src/nvim/log.h +++ b/src/nvim/log.h @@ -62,27 +62,7 @@ #endif #if defined(__linux__) -# include -# define LOG_CALLSTACK(prefix) \ - do { \ - void *trace[100]; \ - int trace_size = backtrace(trace, 100); \ - \ - char exe[1024]; \ - ssize_t elen = readlink("/proc/self/exe", exe, sizeof(exe) - 1); \ - exe[elen] = 0; \ - \ - for (int i = 1; i < trace_size; i++) { \ - char buf[256]; \ - snprintf(buf, sizeof(buf), "addr2line -e %s -f -p %p", exe, trace[i]); \ - FILE *fp = popen(buf, "r"); \ - while (fgets(buf, sizeof(buf) - 1, fp) != NULL) { \ - buf[strlen(buf)-1] = 0; \ - do_log(DEBUG_LOG_LEVEL, __func__, __LINE__, true, prefix "%s", buf); \ - } \ - fclose(fp); \ - } \ - } while (0) +# define LOG_CALLSTACK() log_callstack(__func__, __LINE__) #endif #ifdef INCLUDE_GENERATED_DECLARATIONS