バックトレース in ARM(Qemu)
以前、挫折していた Linux 環境での C のバックトレース出力について、改めてトライしてみました。
ちなみに、ホスト環境(x86)ではそのものの backtrace()
や backtrace_symbols()
といったAPIが利用できるけど、 buildroot で構築したクロスビルド環境には execinfo.h
がなかったので、自力でバックトレースを取ることに。
業務で使用するログ機能の強化を考えていたのですが、スタックトレースは必須だと思っていたので、 Qemu 環境ではありますが動作して良かったです。
同じプログラムが業務で使用している開発ボードでも動作するか確認してみよう。
/** @file backtrace.c * @brief porting backtrace. * @sa http://stackoverflow.com/questions/2536021/any-porting-available-of-stacktrace-for-uclibc/2536136#2536136 */ #ifndef _GNU_SOURCE #define _GNU_SOURCE /* for Dl_info */ #endif #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <stdbool.h> #include <stdarg.h> #include <inttypes.h> #include <limits.h> #include <dlfcn.h> #include <errno.h> #include "log.h" extern void *__libc_stack_end; struct backtrace_frame { void *fp; void *sp; void *lr; void *pc; }; #define lengthof(array) (sizeof(array)/sizeof((array)[0])) size_t Backtrace(void **frames, size_t size) { void *top_frame_p; void *current_frame_p; struct backtrace_frame *frame_p; size_t frame_count; top_frame_p = __builtin_frame_address(0); current_frame_p = top_frame_p; frame_p = (struct backtrace_frame *) ((void **) current_frame_p - 3); frame_count = 0; if ((current_frame_p != NULL) && (current_frame_p > (void *) &frame_count) && (current_frame_p < __libc_stack_end)) { while ((frame_count < size) && (current_frame_p != NULL) && (current_frame_p > (void *) &frame_count) && (current_frame_p < __libc_stack_end)) { frame_p = (struct backtrace_frame *) ((void **) current_frame_p - 3); frames[frame_count] = frame_p->lr; ++frame_count; current_frame_p = frame_p->fp; } } return frame_count; } void PrintBacktrace(void) { void *stack[128] = { NULL }; Dl_info info; int i; if (Backtrace(stack, lengthof(stack)) == 0) { fprintf(stderr, "not found backtrace...\n"); return; } for (i = 0; stack[i] != NULL; ++i) { if (dladdr(stack[i], &info) == 0) { fprintf(stderr, " unknown(???+???)[%p]\n", stack[i]); } else { fprintf(stderr, " %s(%s+%p)[%p]\n", info.dli_fname, info.dli_sname, stack[i] - info.dli_saddr, stack[i]); } } return; } int bar(void) { printf("%s(%d) %s: called\n", __FILE__, __LINE__, __func__); PrintBacktrace(); return 0; } int foo(void) { printf("%s(%d) %s: called\n", __FILE__, __LINE__, __func__); bar(); return 0; } int main(int argc, char **argv) { foo(); return 0; }
# makefile for backtrace. TARGET := backtrace ARCH := arm ifeq ($(ARCH),arm) CROSS_COMPILE := arm-buildroot-linux-uclibcgnueabi- else CROSS_COMPILE := endif CC := $(CROSS_COMPILE)gcc CFLAGS := -g -O0 -I. LDFLAGS := -rdynamic -ldl ifeq ($(ARCH),arm) LDFLAGS += -mapcs-frame else LDFLAGS += -fno-omit-frame-pointer endif all: $(CC) -std=c99 $(CFLAGS) -o $(TARGET) $(TARGET).c log.c $(LDFLAGS) clean: rm -rf $(TARGET)
出力結果は以下の通り。
# ./backtrace backtrace.c(101) foo: called backtrace.c(94) bar: called /root/backtrace(PrintBacktrace+0x2c)[0x88b4] /root/backtrace(bar+0x24)[0x898c] /root/backtrace(foo+0x24)[0x89c4] /root/backtrace(main+0x10)[0x8688] /lib/libc.so.1(__uClibc_main+0x1fc)[0xb6f0172c]