linux addr2line 定位ASan Dump信息

前言

作为程序员,在编写代码或是调试码山时,当代码运行出现“Segmentation fault (core dumped)”时,是不是心里一紧,代码哪里报错了?此文正是为解决此问题而来,会用到两个核心工具addr2lineASan,请诸君慢慢看来。

一,addr2line简介

addr2line是 GNU Compiler Collection (GCC) 中的一个实用工具,用于将程序中的地址转换为文件名、行号和函数名。这对于调试和逆向工程非常有用,因为它允许你查看在给定地址上执行的代码的具体位置。

以下是 `addr2line` 的一些主要特点:

1. **地址转换**:通过提供内存地址,你可以将地址转换为源文件名、行号和函数名。
2. **符号解析**:除了地址,`addr2line` 还可以解析函数名、文件名等符号。
3. **可定制性**:可以通过 `-e` 和 `-f` 选项来指定要使用的可执行文件和目标格式。
4. **与GDB集成**:由于 `addr2line` 是 GDB 的一个组成部分,它可以与 GDB 一起使用,以提供更详细的信息。
5. **跨平台**:`addr2line` 在多种操作系统上都可以使用,包括 Linux、Windows 和 macOS。
6. **命令行接口**:可以通过命令行直接调用 `addr2line`,也可以将其集成到脚本或工具中。

使用方法示例:
```sh
addr2line -p -i -f -e  


```
* `-p` 选项允许从进程的内存中获取地址信息。
* `-i` 选项允许解析符号。
* `-f` 选项允许显示完整的文件路径。
* `-e ` 选项允许指定要查询的可执行文件。
* `
` 是你要查询的内存地址。

这只是一个简单的介绍,你可以通过运行man addr2line或访问 GCC 的官方文档来获取更详细的信息。

二,ASan简介

ASan,全称为AddressSanitizer,是一个用于检测C/C++程序中动态内存错误的工具。它由一个编译器检测模块(LLVMpass)和一个替换malloc函数的运行时库组成。ASan在性能和内存错误检测方面都优于Valgrind,尤其适用于检测各种内存错误,包括但不限于:

1. 内存溢出
2. 悬空指针(引用)
3. 使用释放后的堆上内存
4. 使用返回的栈上内存
5. 使用退出作用域的变量
6. 非法释放
7. 内存泄漏

ASan适用于所有适用LLVM的平台,且llvm版本大于3.1。对于gcc,4.8版本之后才加入ASan,但ASan的完整功能则要gcc版本在4.9.2以上。

使用ASan时,只需在gcc选项中加上-fsanitize=address选项。如果想要在使用ASan的时候获取更好的性能,可以加上O1或者更高的编译优化选项。想要在错误信息中让栈追溯信息更友好,可以加上-fno-omit-frame-pointer选项。

三,Makefile脚本如何添加Asan调试信息

1,CFLAGS增加-g

CFLAGS += -g

2,LDFLAGS增加-fsanitize=address

LDFLAGS += -fsanitize=address

3,完整Makefile文件编写,使用单多文件编译

######################################
# source
######################################
# C++ sources
CXX_SOURCES = $(wildcard *.cpp)

######################################
# target
######################################
TARGET_NAME = $(patsubst %.cpp, %, $(CXX_SOURCES))
TARGET = nnetwork 

######################################
# building variables
######################################
# debug build?
DEBUG = 1

#######################################
# paths
#######################################
# Build path
BUILD_DIR = build

#######################################
# binaries
#######################################
#PREFIX = arm-linux-
# PREFIX = 
# The g++ compiler bin path can be either defined in make command via GCC_PATH variable (> make GCC_PATH=xxx)
# either it can be added to the PATH environment variable.
#PREFIX = arm-linux-androideabi-
ifdef GCC_PATH
export CXX = $(GCC_PATH)/$(PREFIX)g++
export AS = $(GCC_PATH)/$(PREFIX)g++ -x assembler-with-cpp
export CP = $(GCC_PATH)/$(PREFIX)objcopy
export SZ = $(GCC_PATH)/$(PREFIX)size
else
export CXX = $(PREFIX)g++
export AS = $(PREFIX)g++ -x assembler-with-cpp
export CP = $(PREFIX)objcopy
export SZ = $(PREFIX)size
endif

ifeq ($(DEBUG), 1)
CFLAGS += -g -gdwarf-2
# optimization
OPT = -O0
else
# optimization
OPT = -O2
endif

# C defines
C_DEFS =  

# C includes
TOP=.
C_INCLUDES = -I$(TOP)
C_INCLUDES += -I$(TOP)/../include
LIBPATH=$(TOP)/usr/lib/
CFLAGS += $(C_DEFS) $(C_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections 
# Generate dependency information
CFLAGS += -MMD -MP -MF"$(@:%.o=%.d)"

# libraries
LIBS = -lc -lm -fsanitize=address -fno-omit-frame-pointer
LIBDIR = -L$(LIBPATH) -L./
LDFLAGS = $(LIBDIR) $(LIBS)

V = 0
ifeq ($(V),1)
export Q=
export NQ=true
else
export Q=@
export NQ=echo
endif

# default action: build all
.PHONY:all clean
all: $(BUILD_DIR) $(BUILD_DIR)/$(TARGET)
export LIBPATH
OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(CXX_SOURCES:.cpp=.o)))
vpath %.cpp $(sort $(dir $(CXX_SOURCES)))

$(BUILD_DIR)/%.o: %.cpp Makefile | $(BUILD_DIR) 
	$(Q)$(CXX) -c $< -o $@ $(CFLAGS)
	
$(BUILD_DIR)/$(TARGET): $(OBJECTS) Makefile
	$(Q)$(CXX) $(OBJECTS) $(LDFLAGS) -o $@
	$(SZ) $@
$(BUILD_DIR):
	mkdir $@		
	
#######################################
# clean up
#######################################
clean:
	-rm -fR $(BUILD_DIR)
  
#######################################
# dependencies
#######################################
-include $(wildcard $(BUILD_DIR)/*.d)

四,addr2line如何使用

使用规则很简单,如下

addr2line -p -i -f -e  

executable对应symbol 可执行文件、库;

address 为程序虚拟地址。

下面是我在编写神经网络BP算法时,调试信息:

这张图是ASan dump堆栈信息,函数的调用关系为自底向上,也就是#0 为最后一个函数调用。

代码存在多文件时,ASan除main文件能显示行号,其他文件未能显示行号,此时需要借助addr2line进行手动定位。

图中红色标记为错误地址。

使用如下命令即可定位代码位置:

$addr2line 0x1752 -e build/nnetwork -f -C

NNetwork::calc_layer_deriva_aoutput(Matrix const&, unsigned int)

/home/book/c-example/2-high-level/algorithm/nnetwork/nnetwork.cpp:50

50 则为nnetwork.cpp 问题代码行号。

五,C语言新手用例使用讲解

手写一个空指针访问内存简单样例,如下:

#include 

void test_segment()
{
    int *p = NULL;
    *p = 1;
}

int main()
{
    test_segment();                                                                                                                                                                                           
}

编译,运行错误信息如下:

linux addr2line 定位ASan Dump信息_第1张图片

显示问题代码正是第6行,完美符合预期。

你可能感兴趣的:(Linux,linux)