日期:2014-05-16 浏览次数:20819 次
实际上,Linux的调试方法非常多,针对不同的问题,不同的场景,不同的应用,都有不同的方法。很难去概括。本篇文章主要涉及本专栏还没有涵盖,但是的确有很重要的方法。本文主要包括动态库注入调试;使用ltrace命令处理动态库的调试;使用strace调试系统调用的问题;Valgrind的简要介绍。
如何排除其他library的调用问题?动态库注入(library injection)有可能会让你事半功倍。
一个大型的软件系统,会用到非常多的动态库。那么如果该动态库的一个api调用出了问题,而调用该api的地方非常非常多,不同的调用都分散的记录在不同的log里。那么,如何快速的找到是哪个调用者出的问题? 当然我们可以通过动态库注入的方式去调试。
下面的代码hook了两个常见的函数memcpy和socket:
void _init(void) { mtrace(); printf("HOOKing: hello\n"); } void _fini(void) { printf("HOOKing: goodbye\n"); } typedef void* (*real_memcpy)(void*, const void*, size_t); void *memcpy( void *dest, const void*src, size_t size) { real_memcpy real = dlsym((void*)-1, "memcpy"); printf("Coping from %p to %p, size %d\n", src,dest,size); return real(dest, src, size); } typedef int (*real_socket)(int socket_family, int socket_type, int protocol); int socket(int socket_family, int socket_type, int protocol) { printf(" SOCKET family %d, SOCKET type %d, SOECKT protocol %d", socket_family, socket_type, protocol); real_socket sock = dlsym((void*)-1, "socket"); return sock(socket_family, socket_type, protocol ); }
将上述代码编译成动态库后,需要指定环境变量LD_PRELOAD为上述动态库。它的作用是强制load指定的动态库,即使不需要它。你可以在上面的动态库里添加你想要的任何函数。
ltrace能够跟踪进程的库函数调用,它会显现出哪个库函数被调用。
还是使用hello,world进行简单的了解吧:
#include <stdio.h> int main () { printf("Hello world!\n"); return 0; }
__libc_start_main(0x8048354, 1, 0xbf869aa4, 0x8048390, 0x8048380 <unfinished ...> puts("Hello world!"Hello world! ) = 13 +++ exited (status 0) +++
strace会跟踪程序系统调用。所以如果是由于程序的系统调用出问题的话,使用strace可以很快的进行问题定位。上述hello world的strace输出为:
execve("./hello", ["./hello"], [/* 30 vars */]) = 0 brk(0) = 0x83d4000 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7f8a000 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) open("/etc/ld.so.cache", O_RDONLY) = 3 fstat64(3, {st_mode=S_IFREG|0644, st_size=80846, .