2

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

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 的哪个迭代中中断以及如何在该迭代处设置断点?

PS:在 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"),
4

5 回答 5

4

你怎么知道代码是“破坏”的?通常是因为某个变量突然出现了一个你意想不到的值。在这种情况下,您可以设置观察点而不是断点,并且仅当该变量超出预期时才会中断。

例如,使用此程序:

#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)

在这里,我们可以看出,当是,即在循环的第五次迭代时,它b已经超过了。您可以观察整个表达式,例如, 以查找您不希望同时为真的值的组合。当你进入它时,gdb 非常强大。20i4watch b > 20 && i > 10

您可以观察变量是否变为特定值,或指针变为 NULL,或范围计数器越过数组的最后一个元素,或任何其他导致代码被破坏的情况。一旦它停止,您将确切地知道错误发生的时间点,并且您可以四处查看其他变量以找出问题所在。

一般来说,如果在使用调试器之前必须知道错误发生的地点和时间,调试器就不会那么有用。

编辑:由于更新您的帖子,在您的特定情况下,您可以使用backtrace并直接进行迭代,例如

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获取循环索引。

于 2013-10-25T23:55:54.697 回答
2

看看这个:GDB 教程。您可以使用break(设置断点)并continue / next做你想做的事:

  1. 不要忘记使用 -g 选项进行编译:gcc -g source.c
  2. gdb ./a.out
  3. 行号
  4. 继续下一个(继续到下一个断点)
  5. 打印 变量(打印变量的

希望能帮助到你。

于 2013-10-25T22:43:13.963 回答
2

gdb 的文档 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

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

于 2013-10-26T00:59:39.253 回答
1

如果我想在第 500 次迭代时在第 94 行设置断点,我应该这样做:

b 94 如果 i=500

通常你会说:

如果条件中断 line_number

于 2013-10-25T22:44:13.723 回答
1

您似乎很想找到它中断的迭代,但是上面nos的答案清楚地说明了如何做到这一点。

在 GDB 中运行您的程序,等待代码崩溃(此时 GDB 将抓取它),然后通过print i在 GDB 提示符下打印索引变量的值来确定它在哪个迭代中崩溃。

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

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

您可以使用该commands选项执行此操作。在这个线程中有一个如何做到这一点的例子。

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

于 2013-10-25T23:09:26.570 回答