检查变量在堆内存中的分配方式(用于调试运行时错误)
Examine how the variables are allocated in the heap memory (for debugging runtime errors)
例如,下面的代码导致munmap_chunk(): invalid pointer
#include <vector>
int main(int argc, char* argv[]) {
std::vector<int> foo(10, 0);
std::vector<int> bar(10, 1);
for(int i = 0; i < 20; i++) {
foo[i] = 42;
}
bar.clear(); // causes munmap_chunk(): invalid pointer
}
在这个简单的例子中,我很容易猜到bar
分配在堆内存中的foo
之后,所以可以猜到一些对foo
的操作“破坏”了内存bar
。所以修复错误相当容易。
然而在实际应用中,情况可能要复杂得多,我们不能轻易猜测堆内存分配。
所以我的问题是:
- 有没有办法显示变量在堆中的分配方式?
- 是否可以监控哪个函数不小心破坏了某些内存?
Is there way to show how variables are allocated in the heap?
是:您可以检查 vector
将在调试器中使用的位置。例如(使用你的程序)和 GDB:
(gdb) start
Temporary breakpoint 1 at 0x1185: file t.cc, line 3.
Starting program: /tmp/a.out
Temporary breakpoint 1, main (argc=1, argv=0x7fffffffdbb8) at t.cc:3
3 std::vector<int> foo(10, 0);
(gdb) n
4 std::vector<int> bar(10, 1);
(gdb) p/r foo
= {<std::_Vector_base<int, std::allocator<int> >> = {_M_impl = {<std::allocator<int>> = {<__gnu_cxx::new_allocator<int>> = {<No data fields>}, <No data fields>}, <std::_Vector_base<int, std::allocator<int> >::_Vector_impl_data> = {_M_start = 0x55555556aeb0, _M_finish = 0x55555556aed8, _M_end_of_storage = 0x55555556aed8}, <No data fields>}}, <No data fields>}
(gdb) n
5 for(int i = 0; i < 20; i++) {
(gdb) p/r bar
= {<std::_Vector_base<int, std::allocator<int> >> = {_M_impl = {<std::allocator<int>> = {<__gnu_cxx::new_allocator<int>> = {<No data fields>}, <No data fields>}, <std::_Vector_base<int, std::allocator<int> >::_Vector_impl_data> = {_M_start = 0x55555556aee0, _M_finish = 0x55555556af08, _M_end_of_storage = 0x55555556af08}, <No data fields>}}, <No data fields>}
在这里您可以看到 foo
将使用 0x55555556aeb0
到 0x55555556aed8
的位置,而 bar
将使用 0x55555556aee0
到 0x55555556af08
的位置。
但是请注意,这些位置可能会从 运行 运行 改变,尤其是在多线程程序中,这使得该技术 非常 难以使用。
如果 Address Sanitizer 可以找到您的问题,那将是一种更快、更可靠的方法。
Is it possible to monitor which function break certain memory accidentally?
是的:这就是观察点的用途。比如我们不希望foo._M_impl._M_end_of_storage
指向的位置发生变化,那么我们可以在上面设置一个观察点:
(gdb) start
Temporary breakpoint 1 at 0x1185: file t.cc, line 3.
Starting program: /tmp/a.out
Temporary breakpoint 1, main (argc=1, argv=0x7fffffffdbb8) at t.cc:3
3 std::vector<int> foo(10, 0);
(gdb) n
4 std::vector<int> bar(10, 1);
(gdb) n
5 for(int i = 0; i < 20; i++) {
(gdb) watch *(int*)0x55555556aed8
Hardware watchpoint 2: *(int*)0x55555556aed8
(gdb) c
Continuing.
Hardware watchpoint 2: *(int*)0x55555556aed8
Old value = 49
New value = 42
main (argc=1, argv=0x7fffffffdbb8) at t.cc:5
5 for(int i = 0; i < 20; i++) {
(gdb) p i
= 10 <-- voila, found the place where overflow happened.
例如,下面的代码导致munmap_chunk(): invalid pointer
#include <vector>
int main(int argc, char* argv[]) {
std::vector<int> foo(10, 0);
std::vector<int> bar(10, 1);
for(int i = 0; i < 20; i++) {
foo[i] = 42;
}
bar.clear(); // causes munmap_chunk(): invalid pointer
}
在这个简单的例子中,我很容易猜到bar
分配在堆内存中的foo
之后,所以可以猜到一些对foo
的操作“破坏”了内存bar
。所以修复错误相当容易。
然而在实际应用中,情况可能要复杂得多,我们不能轻易猜测堆内存分配。
所以我的问题是:
- 有没有办法显示变量在堆中的分配方式?
- 是否可以监控哪个函数不小心破坏了某些内存?
Is there way to show how variables are allocated in the heap?
是:您可以检查 vector
将在调试器中使用的位置。例如(使用你的程序)和 GDB:
(gdb) start
Temporary breakpoint 1 at 0x1185: file t.cc, line 3.
Starting program: /tmp/a.out
Temporary breakpoint 1, main (argc=1, argv=0x7fffffffdbb8) at t.cc:3
3 std::vector<int> foo(10, 0);
(gdb) n
4 std::vector<int> bar(10, 1);
(gdb) p/r foo
= {<std::_Vector_base<int, std::allocator<int> >> = {_M_impl = {<std::allocator<int>> = {<__gnu_cxx::new_allocator<int>> = {<No data fields>}, <No data fields>}, <std::_Vector_base<int, std::allocator<int> >::_Vector_impl_data> = {_M_start = 0x55555556aeb0, _M_finish = 0x55555556aed8, _M_end_of_storage = 0x55555556aed8}, <No data fields>}}, <No data fields>}
(gdb) n
5 for(int i = 0; i < 20; i++) {
(gdb) p/r bar
= {<std::_Vector_base<int, std::allocator<int> >> = {_M_impl = {<std::allocator<int>> = {<__gnu_cxx::new_allocator<int>> = {<No data fields>}, <No data fields>}, <std::_Vector_base<int, std::allocator<int> >::_Vector_impl_data> = {_M_start = 0x55555556aee0, _M_finish = 0x55555556af08, _M_end_of_storage = 0x55555556af08}, <No data fields>}}, <No data fields>}
在这里您可以看到 foo
将使用 0x55555556aeb0
到 0x55555556aed8
的位置,而 bar
将使用 0x55555556aee0
到 0x55555556af08
的位置。
但是请注意,这些位置可能会从 运行 运行 改变,尤其是在多线程程序中,这使得该技术 非常 难以使用。
如果 Address Sanitizer 可以找到您的问题,那将是一种更快、更可靠的方法。
Is it possible to monitor which function break certain memory accidentally?
是的:这就是观察点的用途。比如我们不希望foo._M_impl._M_end_of_storage
指向的位置发生变化,那么我们可以在上面设置一个观察点:
(gdb) start
Temporary breakpoint 1 at 0x1185: file t.cc, line 3.
Starting program: /tmp/a.out
Temporary breakpoint 1, main (argc=1, argv=0x7fffffffdbb8) at t.cc:3
3 std::vector<int> foo(10, 0);
(gdb) n
4 std::vector<int> bar(10, 1);
(gdb) n
5 for(int i = 0; i < 20; i++) {
(gdb) watch *(int*)0x55555556aed8
Hardware watchpoint 2: *(int*)0x55555556aed8
(gdb) c
Continuing.
Hardware watchpoint 2: *(int*)0x55555556aed8
Old value = 49
New value = 42
main (argc=1, argv=0x7fffffffdbb8) at t.cc:5
5 for(int i = 0; i < 20; i++) {
(gdb) p i
= 10 <-- voila, found the place where overflow happened.