文章目录
-
(1)优雅地打印栈回溯(鸡肋)
-
(3)gdb不用输入yes或者no的配置
-
(4) gdb源码显示,汇编显示
-
(5)gdb调试ls或者python
-
(6)保存所有断点到文件里面
-
(7)until
-
(8)attach gdb
-
三、gdb打印完整字符串
-
四、使用gdb调试段错误segment fault
-
五、“double free or corruption”错误定位
-
(1)addr2line
-
(2)gdb的MALLOC_CHECK_
-
(3)mtrace和systap
-
(1)使用未初始化内存
-
(2)内存泄漏
-
(3)两次释放内存
-
(4)使用已经释放的内存
-
(5)不匹配使用new/delete或new[]/delete[]
-
八、nm、ldd、objdump
-
九、使用linux的GDB打印STL 容器
一、基础调试
-
eg:my_course/course/gdb_study/test.cc
#include <cstdio>
#include <vector>
void func()
std::vector<int> arr;
printf("arr[0] =%d\n", arr[0]);
int main()
func();
return 0;
}
my_course/course/gdb_study/run.sh
set -e
cmake -B build
cmake --build build --target cpptest
build/cpptest
my_course/course/gdb_study/CMakeLists.txt
cmake_minimum_required(VERSION 3.25)
set(CMAKE_C_STANDARD 14)
set(CMAKE_CXX_FLAGS "-Wall -g")
project(test LANGUAGES CXX)
add_executable(cpptest test.cc)
(gdb) bt
#0 func () at /home/wangji/code/my_course/course/gdb_study/test.cc:7
#1 0x000055555555524d in main () at /home/wangji/code/my_course/course/gdb_study/test.cc:12
(gdb) l
2 #include <vector>
4 void func()
5 {
6 std::vector<int> arr;
7 printf("arr[0] =%d\n", arr[0]);
8 }
10 int main()
11 {
(gdb) p arr
$1 = std::vector of length 0, capacity 0
(gdb) p arr[0]
Cannot access memory at address 0x0
(gdb) b func
Breakpoint 1 at 0x5555555551b6: file /home/wangji/code/my_course/course/gdb_study/test.cc, line 5.
(gdb) b test.cc:12
Breakpoint 2 at 0x555555555248: file /home/wangji/code/my_course/course/gdb_study/test.cc, line 12.
#include <cstdio>
#include <vector>
#include <string>
struct Myclass
std::vector<int> arr;
std::string m_name;
int get_size() { return arr.size(); }
void grow_size(int n)
if (arr.size() < n)
arr.resize(n);
void grow_size()
grow_size(get_size() + 1);
int get_value()
return arr[0];
void func()
std::vector<int> arr;
printf("arr[0] =%d\n", arr[0]);
int main()
Myclass t_mc;
t_mc.func();
return 0;
}
(gdb) p this
$1 = (Myclass * const) 0x7fffffffdcc0
(gdb) p *this
$2 = {arr = std::vector of length 0, capacity 0, m_mmember = 0, m_age = 0, m_name = ""}
##给string对象赋值
(gdb) p m_name.operator=("wangji")
$3 = "wangji"
(gdb) p *this
$5 = {arr = std::vector of length 0, capacity 0, m_mmember = 0, m_age = 0, m_name = "wangji"}
set print pretty on 只是为了更好看的输出打印信息,
ptype 变量:查看变量类型;x/NumType Addr :用于查看内存的(若是字符串类型:x/100s Addr);
watch Var。监听 Var 变量的改变动作,也就是变量 Var 的值每次被改变的时候就会中断
二、进阶
(1)优雅地打印栈回溯(鸡肋)
整合至CMake如下:
cmake_minimum_required(VERSION 3.25)
set(CMAKE_C_STANDARD 14)
set(CMAKE_CXX_FLAGS "-Wall -g")
project(test LANGUAGES CXX)
add_subdirectory(backward-cpp)
add_executable(cpptest test.cc)
target_sources(cpptest PUBLIC ${BACKWARD_ENABLE})
add_backward(cpptest)
-
-
测试:
-
eg:
#include <cstdio>
#include <vector>
#include <string>
#include <iostream>
struct Student
int age;
const char *name;
std::string show() const
return std::string(name) + std::to_string(age);
int main()
std::vector<Student> students = {{21, "wang"}, {22, "ji"}, {44}};
for (auto i = 0; i < students.size(); ++i)
std::cout << students[i].show() << std::endl;
return 0;
}
(gdb) b show if name ==0
Breakpoint 1 at 0x55f1: file /home/wangji/code/my_course/course/gdb_study/test.cc, line 10.
(gdb) p *this
$4 = {age = 44, name = 0x0}
零时断点,仅仅第一次有效
(gdb) tb show
#include <cstdio>
#include <vector>
#include <string>
#include <iostream>
int cnt = 0;
struct Student
int age;
const char *name;
std::string show() const
if (age >= 18)
++cnt;
return std::string(name) + std::to_string(age);
int main()
std::vector<Student> students = {{21, "wang"}, {22, "ji"}, {44}};
for (auto i = 0; i < students.size(); ++i)
std::cout << students[i].show() << std::endl;
std::cout << cnt << std::endl;
return 0;
}
(gdb) watch cnt if cnt==1
(gdb) k
Kill the program being debugged? (y or n) y
读取或者写入都会有断点
(gdb) awatch cnt
只有读才有断点
(gdb) rwatch cnt
(3)gdb不用输入yes或者no的配置
wangji@DESKTOP-9TAUMH9:~/code/my_course/course/gdb_study$ cat ~/.gdbinit
set confirm off
set auto-load safe-path /
(4) gdb源码显示,汇编显示
(gdb) layout src
(gdb) layout asm
(5)gdb调试ls或者python
gdb ls
(gdb) r
gdb python
(gdb) r run.sh
(6)保存所有断点到文件里面
(gdb) save breakpoints test.gdb
加载断点到gdb中
(gdb) source test.gdb
(7)until
(gdb) b main
Breakpoint 1 at 0x51ca: file /home/wangji/code/my_course/course/gdb_study/test.cc, line 21.
(gdb) r
(gdb) until test.cc:32
(8)attach gdb
wangji@DESKTOP-9TAUMH9:~/code/my_course/course/gdb_study$ ps -ef|grep -i cpp
wangji@DESKTOP-9TAUMH9:~/code/my_course/course/gdb_study$ sudo gdb
(gdb) attach 13578
三、gdb打印完整字符串
gdb默认打印的最大长度:
(gdb) show print elements
Limit on string chars or array elements to print is 200
可以看到默认只会打印200个字符。这个可以通过前面的举例验证。
更改打印的字符串长度值?
使打印的字符串长度不受限制。
(gdb) set print elements 0
(gdb) show print elements
Limit on string chars or array elements to print is unlimited.
使用set print elements [n] 来更改打印字符串的长度。
(gdb) set print elements 300
(gdb) show print elements
Limit on string chars or array elements to print is 300.
四、使用gdb调试段错误segment fault
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
char *buf;
buf = malloc(1<<31);
fgets(buf, 1024, stdin);
printf("%s\n", buf);
return 1;
debug:
(gdb) run
Starting program: /home/dgawd/cpsc/363/a.out
test string
Program received signal SIGSEGV, Segmentation fault.
0x4007fc13 in _IO_getline_info () from /lib/libc.so.6
(gdb) backtrace
#0 0x4007fc13 in _IO_getline_info () from /lib/libc.so.6
#1 0x4007fb6c in _IO_getline () from /lib/libc.so.6
#2 0x4007ef51 in fgets () from /lib/libc.so.6
#3 0x80484b2 in main (argc=1, argv=0xbffffaf4) at segfault.c:10
#4 0x40037f5c in __libc_start_main () from /lib/libc.so.6
(gdb) frame 3
#3 0x80484b2 in main (argc=1, argv=0xbffffaf4) at segfault.c:10
10 fgets(buf, 1024, stdin)
(gdb) print buf
$1 = 0x0
原因:发现malloc在不能分配够所需的内存的时候就会返回NULL。所以coredump了
五、“double free or corruption”错误定位
先写一个简单的测试程序模拟double free错误:
// file: t.c
#include <stdlib.h>
void *func1(size_t size) {
return malloc(size);
void func2(void *p) {
free(p);
int main() {
char *ptr = func1(sizeof(char));
*ptr = 'a';
func2(ptr);
func2(ptr);
return 0;
}
(1)addr2line
debug:
$ ./a.out
*** Error in `./a.out': double free or corruption (fasttop): 0x0000000000602010 ***
======= Backtrace: =========
/lib64/libc.so.6(+0x7c619)[0x7f127fe73619]
./a.out[0x4005af]
./a.out[0x4005e6]
/lib64/libc.so.6(__libc_start_main+0xf5)[0x7f127fe18c05]
./a.out[0x4004b9]
======= Memory map: ========
00400000-00401000 r-xp 00000000 08:02 143753 /home/chenjianfei/test/a.out
00600000-00601000 r--p 00000000 08:02 143753 /home/chenjianfei/test/a.out
00601000-00602000 rw-p 00001000 08:02 143753 /home/chenjianfei/test/a.out
00602000-00623000 rw-p 00000000 00:00 0 [heap]
7f1278000000-7f1278021000 rw-p 00000000 00:00 0
7f1278021000-7f127c000000 ---p 00000000 00:00 0
7f127fbe1000-7f127fbf6000 r-xp 00000000 08:02 657981 /usr/lib64/libgcc_s-4.8.5-20150702.so.1
7f127fbf6000-7f127fdf5000 ---p 00015000 08:02 657981 /usr/lib64/libgcc_s-4.8.5-20150702.so.1
7f127fdf5000-7f127fdf6000 r--p 00014000 08:02 657981 /usr/lib64/libgcc_s-4.8.5-20150702.so.1
7f127fdf6000-7f127fdf7000 rw-p 00015000 08:02 657981 /usr/lib64/libgcc_s-4.8.5-20150702.so.1
7f127fdf7000-7f127ffaf000 r-xp 00000000 08:02 656720 /usr/lib64/libc-2.17.so
7f127ffaf000-7f12801af000 ---p 001b8000 08:02 656720 /usr/lib64/libc-2.17.so
7f12801af000-7f12801b3000 r--p 001b8000 08:02 656720 /usr/lib64/libc-2.17.so
7f12801b3000-7f12801b5000 rw-p 001bc000 08:02 656720 /usr/lib64/libc-2.17.so
7f12801b5000-7f12801ba000 rw-p 00000000 00:00 0
7f12801ba000-7f12801db000 r-xp 00000000 08:02 656713 /usr/lib64/ld-2.17.so
7f12803c9000-7f12803cc000 rw-p 00000000 00:00 0
7f12803d9000-7f12803db000 rw-p 00000000 00:00 0
7f12803db000-7f12803dc000 r--p 00021000 08:02 656713 /usr/lib64/ld-2.17.so
7f12803dc000-7f12803dd000 rw-p 00022000 08:02 656713 /usr/lib64/ld-2.17.so
7f12803dd000-7f12803de000 rw-p 00000000 00:00 0
7ffc2ac7d000-7ffc2ac9e000 rw-p 00000000 00:00 0 [stack]
7ffc2acc9000-7ffc2accb000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
Aborted
一般都配置coredump,可以直接用gdb从core文件中找到报错的堆栈.
在没有coredump的情况下,可以通过addr2line结合上面的报错堆栈把函数调用打出来:
$ addr2line -e ./a.out 0x4005af -f
func2
$ addr2line -e ./a.out 0x4005e6 -f
$ addr2line -e ./a.out 0x4004b9 -f
_start
??:?
上面就是第二次free报错的堆栈,具体位置在func2中,可以检查func2的调用次数。
(2)gdb的MALLOC_CHECK_
有时double free的问题并不是那么好定位,尤其是使用tcmalloc等内存管理的library时,程序free一个已经释放的空间时,并不会立即报错(这块内存并没有返回系统),只是在做真正gc或者内存重分配时,才会报错,这个错误堆栈就和double free的位置相差甚远了。
可以通过设置MALLOC_CHECK_环境变量,实现对内存的分配和释放做一些检查:
-
MALLOC_CHECK_=0:关闭所有检查;
-
MALLOC_CHECK_=1:当有错误被探测到时,在标准错误输出(stderr)上打印错误信息;
-
MALLOC_CHECK_=2:当有错误被探测到时,不显示错误信息,直接进行中断。
可以在gdb中设置该环境变量打印不同的调试信息,如下在free的时候增加了有效内存地址的检查:
$ gdb ./a.out
(gdb) set environment MALLOC_CHECK_ 2
(gdb) r
Starting program: /home/chenjianfei/test/./a.out
Program received signal SIGABRT, Aborted.
0x00007ffff7a4d1f7 in raise () from /lib64/libc.so.6
Missing separate debuginfos, use: debuginfo-install glibc-2.17-196.el7.x86_64
(gdb) bt
#0 0x00007ffff7a4d1f7 in raise () from /lib64/libc.so.6
#1 0x00007ffff7a4e8e8 in abort () from /lib64/libc.so.6
#2 0x00007ffff7a974b9 in free_check () from /lib64/libc.so.6
#3 0x00000000004005af in func2 ()
#4 0x00000000004005e6 in main ()
(gdb)
(3)mtrace和systap
mtrace(memory trace),是 GNU Glibc 自带的内存问题检测工具。
-
基本设计原理为设计一个函数 void mtrace (),函数对 libc 库中的 malloc/free 等函数的调用进行追踪
-
但是存在一个问题,它没办法直接显示动态库所在的源码行数,而 Linux 开发动态库的使用是十分常见的,那么该篇就讲讲 mtrace 如何定位动态库中的内存泄漏问题。
-
mtrace来做检测,只是该方法对业务代码有侵入,需要在应用程序中设置环境变量
inter.c 动态库源码
#include <stdlib.h>
#include <stdio.h>
void InterTest(void)
int *p = (int *)malloc(10 * sizeof(int));
printf("InterTest: p = %p\n", p);
}
编译
gcc -g inter.c -fPIC -shared -o libinter.so
可执行文件 test.c
#include <mcheck.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
// 测试代码,没有接口头文件,所以使用 extern
extern void InterTest(void);
int main(int argc, char **argv)
mtrace(); // 开始跟踪
char *p = (char *)malloc(100);
free(p);
p = NULL;
InterTest();
p = (char *)malloc(100);
muntrace(); // 结束跟踪,并生成日志信息
int wait = 20;
//等待的目的是方便后面操作
while (wait--)
sleep(1);
printf("wait = %d\n", wait);
return 0;
}
编译:
gcc -g test.c -L"/home/const/workspace/memtest" -linter -o test
运行程序
export MALLOC_TRACE=./test.log
./test & // & 的作用是后台运行,方便在等待期间进行 ps 等操作
const@server:~/workspace/memtest$ ps
PID TTY TIME CMD
1631 pts/62 00:00:00 bash
10723 pts/62 00:00:00 test
10745 pts/62 00:00:00 ps
可以看到此时的 test 执行程序的 PID 为 10745,接着输入以下指令查看加载的动态库信息(确保 test 还在执行)
const@server:~/workspace/memtest$ cat /proc/10723/maps
00400000-00401000 r-xp 00000000 08:01 144706317 /home/const/workspace/memtest/test
00600000-00601000 r--p 00000000 08:01 144706317 /home/const/workspace/memtest/test
00601000-00602000 rw-p 00001000 08:01 144706317 /home/const/workspace/memtest/test
021f8000-02219000 rw-p 00000000 00:00 0 [heap]
7f59842e3000-7f59844a3000 r-xp 00000000 08:16 4723539 /lib/x86_64-linux-gnu/libc-2.23.so
7f59844a3000-7f59846a3000 ---p 001c0000 08:16 4723539 /lib/x86_64-linux-gnu/libc-2.23.so
7f59846a3000-7f59846a7000 r--p 001c0000 08:16 4723539 /lib/x86_64-linux-gnu/libc-2.23.so
7f59846a7000-7f59846a9000 rw-p 001c4000 08:16 4723539 /lib/x86_64-linux-gnu/libc-2.23.so
7f59846a9000-7f59846ad000 rw-p 00000000 00:00 0
7f59846ad000-7f59846ae000 r-xp 00000000 08:01 144706316 /home/const/workspace/memtest/libinter.so
7f59846ae000-7f59848ad000 ---p 00001000 08:01 144706316 /home/const/workspace/memtest/libinter.so
7f59848ad000-7f59848ae000 r--p 00000000 08:01 144706316 /home/const/workspace/memtest/libinter.so
7f59848ae000-7f59848af000 rw-p 00001000 08:01 144706316 /home/const/workspace/memtest/libinter.so
7f59848af000-7f59848d5000 r-xp 00000000 08:16 4723525 /lib/x86_64-linux-gnu/ld-2.23.so
7f5984a9e000-7f5984aa1000 rw-p 00000000 00:00 0
7f5984ad3000-7f5984ad4000 rw-p 00000000 00:00 0
7f5984ad4000-7f5984ad5000 r--p 00025000 08:16 4723525 /lib/x86_64-linux-gnu/ld-2.23.so
7f5984ad5000-7f5984ad6000 rw-p 00026000 08:16 4723525 /lib/x86_64-linux-gnu/ld-2.23.so
7f5984ad6000-7f5984ad7000 rw-p 00000000 00:00 0
7fffedca8000-7fffedcca000 rw-p 00000000 00:00 0 [stack]
7fffedde7000-7fffeddea000 r--p 00000000 00:00 0 [vvar]
7fffeddea000-7fffeddec000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
分析定位
const@server:~/workspace/memtest$ mtrace test ./test.log
Memory not freed:
-----------------
Address Size Caller
0x00000000021f8450 0x64 at /home/const/workspace/memtest/test.c:19
0x00000000021f84c0 0x28 at 0x7f59846ad702
0x00000000021f84f0 0x400 at 0x7f59843501d5
libinter.so在test程序的加载地址为 0x7f59846ad000-0x7f59848af000,这里泄露的地址0x7f59846ad702刚好在里面,减去 libinter.so 的 base 地址 0x7f59846ad000得到相对偏移地址为 0x702。
通过使用 “addr2line” 命令工具,得到动态库源文件的行数,定位内存泄漏的位置。
const@server:~/workspace/memtest$ addr2line -e libinter.so 0x702
/home/const/workspace/memtest/inter.c:6
-
至于最后一行的 0x00000000021f84f0 0x400 at 0x7f59843501d5,通过地址比较不难看出是 /lib/x86_64-linux-gnu/libc-2.23.so 中的内存信息,通过上述相同的操作打印的内容可能是 ??,原因是没有 -g 编译后的调试信息;这个其实可以忽略,标准库中基本不会存在这种问题,这种属于可执行程序运行结束后相关资源(后期自动回收处理)还没有及时释放(可以屏蔽动态库函数的调用和睡眠函数,这个就不存在了)。
SystemTap是我目前所知的最强大的内核调试工具;
六、core文件配合gfb
执行ulimit -c 1024修改限制
hurley@hurley-fedora ~$ ulimit -a
core file size (blocks, -c) 0
hurley@hurley-fedora ~$ ulimit -c 1024
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
char *p = (char *)0xB800;
*p = 'a';
return EXIT_SUCCESS;
}
-rdynamic:通知链接器将所有符号添加到动态符号表中
hurley@hurley-fedora segment-test$ gcc -Wall -g -rdynamic test.c -o test
hurley@hurley-fedora segment-test$ ./test
段错误(吐核)
hurley@hurley-fedora segment-test$ ls
core.6864 test test.c
debug:
hurley@hurley-fedora segment-test$ gdb ./test core.6864
GNU gdb (GDB) Fedora (7.3.50.20110722-16.fc16)
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-redhat-linux-gnu".
For bug reporting instructions, please see:
Reading symbols from /home/hurley/segment-test/test...done.
[New LWP 6864]
Core was generated by `./test'.
Program terminated with signal 11, Segmentation fault.
#0 0x08048524 in main (argc=1, argv=0xbfb126b4) at test.c:26
26 *p = 'a';
Missing separate debuginfos, use: debuginfo-install glibc-2.14.90-24.fc16.9.i686
七、Valgrind
Valgrind是一款用于内存调试、内存泄漏检测以及性能分析的软件开发工具。遗憾的是它只能检测到堆里的内存泄漏和越界访问,对于栈里的内存访问错误爱莫能助
valgrind --tool=memcheck --leak-check=full --error-exitcode=1 ./program
valgrind --tool=memcheck Path ./excuteFile
(1)使用未初始化内存
#include <iostream>
int main()
int* p;
*p=10;
return 0;
}
使用Valgrind查看
$ valgrind --tool=memcheck ./test1
==3978== Memcheck, a memory error detector------------------3978是进程ID
==3978== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info
==3978== Command: ./test1
==3978==
==3978== Use of uninitialised value of size 8 ----------------------关键:出使用未初始化的内存,大小为8,错误发生的位置。
==3978== at 0x4006D5: main (test1.cpp:5)
==3978==
==3978== Invalid write of size 4
==3978== at 0x4006D5: main (test1.cpp:5)
==3978== Address 0x0 is not stack'd, malloc'd or (recently) free'd
==3978==
==3978==
==3978== Process terminating with default action of signal 11 (SIGSEGV)
==3978== Access not within mapped region at address 0x0
==3978== at 0x4006D5: main (test1.cpp:5)
==3978== If you believe this happened as a result of a stack
==3978== overflow in your program's main thread (unlikely but
==3978== possible), you can try to increase the size of the
==3978== main thread stack using the --main-stacksize= flag.
==3978== The main thread stack size used in this run was 8388608.
==3978==
==3978== HEAP SUMMARY:
==3978== in use at exit: 0 bytes in 0 blocks
==3978== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==3978==
==3978== All heap blocks were freed -- no leaks are possible
==3978==
==3978== For counts of detected and suppressed errors, rerun with: -v
==3978== Use --track-origins=yes to see where uninitialised values come from
==3978== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
(2)内存泄漏
#include <iostream>
int main()
int *p=new int[10];
*(p+1)=1;
return 0;
}
wangji@script-wang:~/code/test/broudcast$ valgrind --tool=memcheck --leak-check=full ./a.out
==11546== Memcheck, a memory error detector
==11546== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==11546== Command: ./a.out
==11546==
==11546==
==11546== HEAP SUMMARY:
==11546== in use at exit: 40 bytes in 1 blocks
==11546== total heap usage: 2 allocs, 1 frees, 72,744 bytes allocated
==11546==
==11546== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
==11546== at 0x483C583: operator new[](unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==11546== by 0x10919E: main (test2.cc:4)
==11546==
==11546== LEAK SUMMARY:
==11546== definitely lost: 40 bytes in 1 blocks
==11546== indirectly lost: 0 bytes in 0 blocks
==11546== possibly lost: 0 bytes in 0 blocks
==11546== still reachable: 0 bytes in 0 blocks
==11546== suppressed: 0 bytes in 0 blocks
==11546==
==11546== For lists of detected and suppressed errors, rerun with: -s
==11546== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
(3)两次释放内存
#include <iostream>
int main()
int *p=new int[10];
*(p+1)=1;
delete[] p;
delete[] p;
return 0;
}
wangji@script-wang:~/code/test/broudcast$ valgrind --tool=memcheck ./a.out
==11565== Memcheck, a memory error detector
==11565== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==11565== Command: ./a.out
==11565==
==11565== Invalid free() / delete / delete[] / realloc()
==11565== at 0x483D74F: operator delete[](void*) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==11565== by 0x1091F6: main (test2.cc:7)
==11565== Address 0x4db6c80 is 0 bytes inside a block of size 40 free'd
==11565== at 0x483D74F: operator delete[](void*) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==11565== by 0x1091E3: main (test2.cc:6)
==11565== Block was alloc'd at
==11565== at 0x483C583: operator new[](unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==11565== by 0x1091BE: main (test2.cc:4)
==11565==
==11565==
==11565== HEAP SUMMARY:
==11565== in use at exit: 0 bytes in 0 blocks
==11565== total heap usage: 2 allocs, 3 frees, 72,744 bytes allocated
==11565==
==11565== All heap blocks were freed -- no leaks are possible
==11565==
==11565== For lists of detected and suppressed errors, rerun with: -s
==11565== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
(4)使用已经释放的内存
#include <iostream>
int main()
int *p=new int[10];
*(p+1)=1;
delete[] p;
*(p+2)=10;
return 0;
}
wangji@script-wang:~/code/test/broudcast$ valgrind --tool=memcheck ./a.out
==11576== Memcheck, a memory error detector
==11576== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==11576== Command: ./a.out
==11576==
==11576== Invalid write of size 4
==11576== at 0x1091EC: main (test2.cc:7)
==11576== Address 0x4db6c88 is 8 bytes inside a block of size 40 free'd
==11576== at 0x483D74F: operator delete[](void*) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==11576== by 0x1091E3: main (test2.cc:6)
==11576== Block was alloc'd at
==11576== at 0x483C583: operator new[](unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==11576== by 0x1091BE: main (test2.cc:4)
==11576==
==11576==
==11576== HEAP SUMMARY:
==11576== in use at exit: 0 bytes in 0 blocks
==11576== total heap usage: 2 allocs, 2 frees, 72,744 bytes allocated
==11576==
==11576== All heap blocks were freed -- no leaks are possible
==11576==
==11576== For lists of detected and suppressed errors, rerun with: -s
==11576== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
(5)不匹配使用new/delete或new[]/delete[]
#include <iostream>
int main()
int *p=new int[10];
*(p+1)=1;
delete p;
return 0;
}
wangji@script-wang:~/code/test/broudcast$ valgrind --tool=memcheck ./a.out
==11585== Memcheck, a memory error detector
==11585== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==11585== Command: ./a.out
==11585==
==11585== Mismatched free() / delete / delete []
==11585== at 0x483D1CF: operator delete(void*, unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==11585== by 0x1091E6: main (test2.cc:6)
==11585== Address 0x4db6c80 is 0 bytes inside a block of size 40 alloc'd
==11585== at 0x483C583: operator new[](unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==11585== by 0x1091BE: main (test2.cc:4)
==11585==
==11585==
==11585== HEAP SUMMARY:
==11585== in use at exit: 0 bytes in 0 blocks
==11585== total heap usage: 2 allocs, 2 frees, 72,744 bytes allocated
==11585==
==11585== All heap blocks were freed -- no leaks are possible
==11585==
==11585== For lists of detected and suppressed errors, rerun with: -s
==11585== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
八、nm、ldd、objdump
使用nm命令列出二进制文件中的符号表,包括符号地址、符号类型、符号名等,这样可以帮助定位在哪里发生了段错误。
panfeng@ubuntu:~/segfault$ nm segfault3
使用ldd命令查看二进制程序的共享链接库依赖,包括库的名称、起始地址,这样可以确定段错误到底是发生在了自己的程序中还是依赖的共享库中。
panfeng@ubuntu:~/segfault$ ldd ./segfault3
使用objdump生成二进制的相关信息,重定向到文件中,生成的segfault3Dump文件中包含了二进制文件的segfault3的汇编代码,在segfault3Dump文件中查找发生段错误的地址:
panfeng@ubuntu:~/segfault$ objdump -dS ./segfault3 > segfault3Dump
九、使用linux的GDB打印STL 容器
(1)下载gdb文件:
STL Support Tools
(2)使用
#include <iostream>
#include <map>
int main()
std::map<int,int> m;
m[1]=1;
m[2]=2;
return 0;
}
g++ -Wall -g test2.cc
(gdb) help pmap
Prints std::map<TLeft and TRight> or std::multimap<TLeft and TRight> information. Works for std::multimap as well.
Syntax: pmap <map> <TtypeLeft> <TypeRight> <valLeft> <valRight>: Prints map size, if T defined all elements or just element(s) with val(s)
Examples:
pmap m - prints map size and definition
pmap m int int - prints all elements and map size
pmap m int int 20 - prints the element(s) with left-value = 20 (if any) and map size
pmap m int int 20 200 - prints the element(s) with left-value = 20 and right-value = 200 (if any) and map size
(gdb) source ~/stl-views-1.0.3.gdb
(gdb) pmap m int int
elem[0].left: $1 = 1
elem[0].right: $2 = 1
elem[1].left: $3 = 2
elem[1].right: $4 = 2
Map size = 2
(gdb) pmap m int int 20
Number of elements found = 0
Map size = 1
(gdb) pmap m int int 1
elem[0].left: $3 = 1
elem[0].right: $4 = 1
Number of elements found = 1
Map size = 1
eg:pmap的含义。其他命令的含义使用的时候,加个help看看即可
pmap variable------------>打印variable这个map的定义和map里面的个数
pmap variable int int(就是单纯的两个int) ------------>打印pmap的元素和map的个数
pmap variable int int 20------------>打印索引是20的map的值 和map的个数
pmap variable int int 20 200------->打印索引是20 值是200的map值和map的个数