如何在巨型循环中使用GDB

时间:2013-10-25 22:19:56

标签: c linux debugging gdb breakpoints

我有以下循环和我的代码中断,但我不知道它在哪个迭代中完全破坏。

int n=1000;
for (i=0; i<n; i++) {
                slot = random() % max_allocs;
                doAlloc = random() % 4;
                doWrite = writeData;

                if (!doAlloc || ptr[slot] != NULL) {
                        if (ptr[slot] == NULL)
                                ;//assert(Mem_Free(ptr[slot]) == -1);
                        else
                        {
                                printf("I got here \n");
                                printf("mem free ptr slot is %d \n",Mem_Free(ptr[slot]));
                        }
                        free(shadow[slot]);
                        ptr[slot] = NULL;
                        shadow[slot] = NULL;
                }

                if (doAlloc) {
                        size[slot] = min_alloc_size +
                                (random() % (max_alloc_size - min_alloc_size + 1));
                        printf("size[slot] :%d\n", size[slot]);
                        ptr[slot] = Mem_Alloc(size[slot], BESTFIT);
                        printf("ptr slot is %p \n",ptr[slot]);
                        assert(ptr[slot] != NULL);
                        if (doWrite) {
                                shadow[slot] = malloc(size[slot]);
                                int j;
                                for (j=0; j<size[slot]; j++) {
                                        char data = random();
                                        *((char*)(ptr[slot] + j)) = data;
                                        *((char*)(shadow[slot] + j)) = data;
                                }
                        }
                }
        }

如何找到代码中断的n次迭代以及如何在该迭代中放置断点?

P.S。:在Linux中是否还有其他更好的调试器用于此目的? (如果我不想使用Eclipse!)

这是我在gdb中收到的错误:

mymain: mymain.c:104: main: Assertion `ptr[slot] != ((void *)0)' failed.

Program received signal SIGABRT, Aborted.
0x000000368da328e5 in raise (sig=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
64    return INLINE_SYSCALL (tgkill, 3, pid, selftid, sig);
(gdb) backtrace
#0  0x000000368da328e5 in raise (sig=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
#1  0x000000368da340c5 in abort () at abort.c:92
#2  0x000000368da2ba0e in __assert_fail_base (fmt=<value optimized out>, assertion=0x40114b "ptr[slot] != ((void *)0)", file=0x401142 "mymain.c", line=<value optimized out>, function=<value optimized out>)
    at assert.c:96
#3  0x000000368da2bad0 in __assert_fail (assertion=0x40114b "ptr[slot] != ((void *)0)", file=0x401142 "mymain.c", line=104, function=0x401199 "main") at assert.c:105
#4  0x0000000000400e2a in main (argc=4, argv=0x7fffffffdb68) at mymain.c:104
(gdb) frame 1
#1  0x000000368da340c5 in abort () at abort.c:92
92        raise (SIGABRT);
(gdb) frame 3
#3  0x000000368da2bad0 in __assert_fail (assertion=0x40114b "ptr[slot] != ((void *)0)", file=0x401142 "mymain.c", line=104, function=0x401199 "main") at assert.c:105
105   __assert_fail_base (_("%s%s%s:%u: %s%sAssertion `%s' failed.\n%n"),

5 个答案:

答案 0 :(得分:3)

你怎么知道代码首先是“破解”?通常这是因为某些变量突然出现了你不期望的值。在这种情况下,您可以设置观察点而不是断点,只有当该变量超出预期时才会中断。

例如,使用此程序:

#include <stdio.h>

int main(void) {
    int b = 0;
    for ( int i = 0; i < 20; ++i ) {
        b += 5;
    }
    return 0;
}

我们可以让gdb在b达到或超过某个值时停止,并找出它发生的循环的确切迭代:

paul@local:~/src/c/scratch$ gdb testwatch
GNU gdb (GDB) 7.4.1-debian
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
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 "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/paul/src/c/scratch/testwatch...done.
(gdb) list
1       #include <stdio.h>
2
3       int main(void) {
4           int b = 0;
5           for ( int i = 0; i < 20; ++i ) {
6               b += 5;
7           }
8           return 0;
9       }
(gdb) break 5
Breakpoint 1 at 0x400567: file testwatch.c, line 5.
(gdb) run
Starting program: /home/paul/src/c/scratch/testwatch

Breakpoint 1, main () at testwatch.c:5
5           for ( int i = 0; i < 20; ++i ) {
(gdb) watch b > 20
Hardware watchpoint 2: b > 20
(gdb) continue
Continuing.
Hardware watchpoint 2: b > 20

Old value = 0
New value = 1
main () at testwatch.c:5
5           for ( int i = 0; i < 20; ++i ) {
(gdb) print b
$1 = 25
(gdb) print i
$2 = 4
(gdb)

我们可以告诉b20i4超过watch b > 20 && i > 10,即在循环的第五次迭代中。您可以观察整个表达式,例如backtrace,以查找您不希望同时为真的值的组合。当你进入它时,gdb非常强大。

您可以观察变量变为特定值,或指针变为NULL,或者超出数组最后一个元素的范围计数器,或者导致代码被破坏的任何其他条件。一旦它停止,你就会确切地知道你的错误发生的时间点,你可以四处寻找其他变量以找出出错的地方。

一般情况下,如果在使用错误之前必须知道错误发生的位置和时间,调试器就不会那么有用了。

编辑:自从更新您的帖子后,在您的特定情况下,您可以使用paul@local:~/src/c/scratch$ gdb segfault GNU gdb (GDB) 7.4.1-debian Copyright (C) 2012 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> 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 "x86_64-linux-gnu". For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>... Reading symbols from /home/paul/src/c/scratch/segfault...done. (gdb) list 1,16 1 #include <stdlib.h> 2 3 void segfault(int * p) { 4 int n = *p; 5 } 6 7 int main(void) { 8 int n = 0; 9 int * parray[] = {&n, &n, &n, &n, NULL}; 10 11 for ( int i = 0; i < 10; ++i ) { 12 segfault(parray[i]); 13 } 14 15 return 0; 16 } (gdb) run Starting program: /home/paul/src/c/scratch/segfault Program received signal SIGSEGV, Segmentation fault. 0x0000000000400568 in segfault (p=0x0) at segfault.c:4 4 int n = *p; (gdb) backtrace #0 0x0000000000400568 in segfault (p=0x0) at segfault.c:4 #1 0x00000000004005c1 in main () at segfault.c:12 (gdb) frame 1 #1 0x00000000004005c1 in main () at segfault.c:12 12 segfault(parray[i]); (gdb) print i $1 = 4 (gdb) 并直接进行迭代,例如

frame

在你的情况下,你会转到与你的循环所在函数对应的print i,并且只需要{{1}}来获取循环索引。

答案 1 :(得分:2)

看看这个:GDB Tutorial。 您可以使用中断(设置断点)和继续 / 下一步来执行您想要的操作:

  1. 不要忘记使用-g选项进行编译: gcc -g source.c
  2. gdb ./a.out
  3. 休息 亚麻布
  4. 继续下一步(进入下一个断点)
  5. 打印 变量(打印变量的值)
  6. 希望它有所帮助。

答案 2 :(得分:1)

如果我想在第94行设置断点,那么我应该这样做:

b 94如果i = 500

通常你会说:

如果条件

,则打破line_number

答案 3 :(得分:1)

你好像在找到它破坏的迭代时会挂断,但上面nos的答案清楚地说明了如何做到这一点。

在GDB中运行程序,等待代码崩溃(此时GDB将抓取它),然后通过使用print i打印索引变量的值来计算崩溃的迭代次数。 GDB提示。

编辑:好的,我想我明白了。当你说代码“中断”时,你的意思是它以一种允许它继续执行的方式打破:它没有崩溃,并且GDB没有自动捕获它。

在这种情况下,无法确定设置所需断点的位置。您根本不知道问题何时发生。你如何确定该计划正在破产?是否有任何变量可以打印出破损发生时显示的值?如果是这样,您可以让GDB在每次迭代期间打印值(而不是直接将调试编写到代码中)。

您可以使用commands选项执行此操作。有一个如何在this线程中执行此操作的示例。

在每次迭代时,打印i的值以及用于跟踪破损的变量值。然后,这应该为您提供发生破损的迭代,然后您可以返回并在正确的位置设置断点。

答案 4 :(得分:1)

来自gdb's documentation 5.1.7 "Breakpoint Command Lists"

  

当程序因该断点而停止时,您可以为任何断点(或观察点或捕获点)提供一系列要执行的命令。例如,您可能希望打印某些表达式的值,或启用其他断点。

因此,您可以在循环中设置一个断点,每次点击时都会显示迭代值i。这样,当你崩溃时,你可以看到最后打印的值:

break <line number just after start of the loop> 
commands 
silent
printf "i == %d\n", i
continue
end

当然还有其他(可能更有效)的方法来调试这个问题,但是使用断点来显示信息或执行其他脚本操作然后继续运行的技术在调试工具箱中是有价值的。