linux调试技巧

实际上,Linux的调试方法非常多,针对不同的问题,不同的场景,不同的应用,都有不同的方法。很难去概括。本篇文章主要涉及本专栏还没有涵盖,但是的确有很重要的方法。本文主要包括动态库注入调试;使用ltrace命令处理动态库的调试;使用strace调试系统调用的问题;Valgrind的简要介绍。

1. 动态库注入

      如何排除其他library的调用问题?动态库注入(library injection)有可能会让你事半功倍。

一个大型的软件系统,会用到非常多的动态库。那么如果该动态库的一个api调用出了问题,而调用该api的地方非常非常多,不同的调用都分散的记录在不同的log里。那么,如何快速的找到是哪个调用者出的问题? 当然我们可以通过动态库注入的方式去调试。

     下面的代码hook了两个常见的函数memcpy和socket:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void _init(void)  
  2. {  
  3.   mtrace();  
  4.   printf("HOOKing: hello\n");  
  5. }  
  6.   
  7. void _fini(void)  
  8. {  
  9.   printf("HOOKing: goodbye\n");  
  10. }  
  11.   
  12. typedef void* (*real_memcpy)(void*, const void*, size_t);  
  13. void *memcpy( void *dest, const void*src, size_t size)  
  14. {  
  15.   real_memcpy real = dlsym((void*)-1, "memcpy");  
  16.   printf("Coping from %p to %p, size %d\n", src,dest,size);  
  17.   
  18.   return real(dest, src, size);  
  19. }  
  20. typedef int (*real_socket)(int socket_family, int socket_type, int protocol);  
  21.   
  22. int socket(int socket_family, int socket_type, int protocol)  
  23. {  
  24.   printf(" SOCKET family %d, SOCKET type %d, SOECKT protocol %d", socket_family, socket_type, protocol);  
  25.   
  26.   real_socket sock = dlsym((void*)-1, "socket");  
  27.   
  28.  return sock(socket_family, socket_type, protocol );  
  29. }  


将上述代码编译成动态库后,需要指定环境变量LD_PRELOAD为上述动态库。它的作用是强制load指定的动态库,即使不需要它。你可以在上面的动态库里添加你想要的任何函数。

2. ltrace

  ltrace能够跟踪进程的库函数调用,它会显现出哪个库函数被调用。

   还是使用hello,world进行简单的了解吧:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <stdio.h>  
  2. int main ()  
  3. {  
  4.     printf("Hello world!\n");  
  5.     return 0;  
  6. }  

使用ltrace + 命令可以启动对任何程序的调试,上述hello world的ltrace为:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. __libc_start_main(0x8048354, 1, 0xbf869aa4, 0x8048390, 0x8048380 <unfinished ...>  
  2. puts("Hello world!"Hello world!  
  3. )                                                             = 13  
  4. +++ exited (status 0) +++  

其实ltrace是一个不用去阅读库的实现代码,而去学习库的整体调用栈的很好的方式。当然了结合代码你可以得到更加详细的实现。

3. strace

   strace会跟踪程序系统调用。所以如果是由于程序的系统调用出问题的话,使用strace可以很快的进行问题定位。上述hello world的strace输出为:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. execve("./hello", ["./hello"], [/* 30 vars */]) = 0  
  2. brk(0)                                  = 0x83d4000  
  3. mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7f8a000  
  4. access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)  
  5. open("/etc/ld.so.cache", O_RDONLY)      = 3  
  6. fstat64(3, {st_mode=S_IFREG|0644, st_size=80846, ...}) = 0  
  7. mmap2(NULL, 80846, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7f76000  
  8. close(3)                                = 0  
  9. open("/lib/libc.so.6", O_RDONLY)        = 3  
  10. read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0000?\270"..., 512) = 512  
  11. fstat64(3, {st_mode=S_IFREG|0755, st_size=1576952, ...}) = 0  
  12. mmap2(0xb6e000, 1295780, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb6e000  
  13. mmap2(0xca5000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x137) = 0xca5000  
  14. mmap2(0xca8000, 9636, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xca8000  
  15. close(3)                                = 0  
  16. mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7f75000  
  17. set_thread_area({entry_number:-1 -> 6, base_addr:0xb7f756c0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0  
  18. mprotect(0xca5000, 8192, PROT_READ)     = 0  
  19. mprotect(0xb6a000, 4096, PROT_READ)     = 0  
  20. munmap(0xb7f76000, 80846)               = 0  
  21. fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0  
  22. mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7f89000  
  23. write(1, "Hello world!\n", 13Hello world!  
  24. )          = 13  
  25. exit_group(0)                           = ?  
  26. Process 2874 detached  

可以看到strace的输出非常丰富。

4. Valgrind

    Valgrind是一款用于内存调试、内存泄漏检测以及性能分析的软件开发工具。

Valgrind包括如下一些工具:

  1. Memcheck。这是valgrind应用最广泛的工具,一个重量级的内存检查器,能够发现开发中绝大多数内存错误使用情况,比如:使用未初始化的内存,使用已经释放了的内存,内存访问越界等。这也是本文将重点介绍的部分。
  2. Callgrind。它主要用来检查程序中函数调用过程中出现的问题。
  3. Cachegrind。它主要用来检查程序中缓存使用出现的问题。
  4. Helgrind。它主要用来检查多线程程序中出现的竞争问题。
  5. Massif。它主要用来检查程序中堆栈使用中出现的问题。
  6. Extension。可以利用core提供的功能,自己编写特定的内存调试工具。

IBM Developer有一篇很好的文章:https://www.ibm.com/developerworks/cn/linux/l-cn-valgrind/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值