2

I have the following loop and my code breaks but I don't know at which iteration it breaks exactly.

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;
                                }
                        }
                }
        }

How can I find at which iteration of n the code breaks and how can I put a breakpoint at that iteration?

P.S.: Is there any other better debugger for this purpose in Linux? (If I don't want to use Eclipse!)

Here's the error I am receiving in 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"),
Mona Jalal
  • 34,860
  • 64
  • 239
  • 408

5 Answers5

4

How do you know the code is "breaking" in the first place? Usually it's because some variable suddenly takes on a value you don't expect. In this case, you can set a watchpoint rather than a breakpoint, and it'll break when and only when that variable goes outside of expectations.

For instance, with this program:

#include <stdio.h>

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

we can get gdb to stop when b hits or exceeds a certain value, and find out on exactly which iteration of the loop it occurred:

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)

Here we can tell that b went above 20 when i was 4, i.e. on the fifth iteration of the loop. You can watch for whole expressions, such as watch b > 20 && i > 10, to look for combinations of values that you don't expect to be simultaneously true. gdb is pretty powerful when you get into it.

You can watch for a variable becoming a particular value, or a pointer becoming NULL, or a range counter going past the last element of your array, or whatever other condition is resulting in your code being broken. Once it stops, you'll know exactly the point at which your error occurs, and you can poke around looking at other variables to figure out what's going wrong.

In general, a debugger wouldn't be all that useful if you had to know where and when an error was occurring before you could use it.

EDIT: Since updating your post, in your particular case, you can just use backtrace and get right to the iteration, e.g.

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)

In your case, you'd go to whatever frame corresponds to the function your loop is in, and just do print i to get the loop index.

Crowman
  • 25,242
  • 5
  • 48
  • 56
  • Can you check my edited post? I wrote what `backtrace` gives me. Do you know what causes the problem? – Mona Jalal Oct 26 '13 at 01:42
  • 1
    Yes, the problem is that this assertion fails: `assert(ptr[slot] != NULL);`. At that point in your program, `ptr[slot]` evidently equals `NULL`, so your program aborts because you told it to. Since it comes immediately after `ptr[slot] = Mem_Alloc(size[slot], BESTFIT);`, it would appear your memory allocation function is returning `NULL`. – Crowman Oct 26 '13 at 01:58
  • 1
    Using gdb wasn't necessary to tell you that, by the way. The message `mymain: mymain.c:104: main: Assertion 'ptr[slot] != ((void *)0)' failed.` told you everything you needed to know to figure out just what was going wrong. – Crowman Oct 26 '13 at 02:13
2

take a look at this: GDB Tutorial. You can use break (to set a breakpoint) and continue / next to do what you want:

  1. Don't forget to compile with -g option: gcc -g source.c
  2. gdb ./a.out
  3. break linenumber
  4. continue or next (to proceed to the next breakpoint)
  5. print variable (to print the value of variable)

Hope it helps.

kukinsula
  • 322
  • 4
  • 8
  • 1
    the problem is that I don't know at which iteration the code execution breaks! Other than I found how to put a breakpoint at a specific line in say 500th iteration but it doesn't help my case. – Mona Jalal Oct 25 '13 at 23:03
  • What error message do you get ? GDB should display explicit error message such as "Program received signal SIGSEGV, Segmentation fault. 0x08048464 in main () at test-gdb.c:12" => so there is a problem at line 12 (for exemple). You could add printfs in order to trace where the code breaks: printf("i = %d", i); //at the end of your loop. – kukinsula Oct 25 '13 at 23:10
  • thank you. I edited my post and added the error I am receiving. Also thanks for the hint. I am simply printing the iteration at which it breaks. – Mona Jalal Oct 26 '13 at 00:00
2

From gdb's documentation 5.1.7 "Breakpoint Command Lists":

You can give any breakpoint (or watchpoint or catchpoint) a series of commands to execute when your program stops due to that breakpoint. For example, you might want to print the values of certain expressions, or enable other breakpoints.

So you can set a breakpoint in the loop that displays the iteration value, i, each time it is hit. That way when you crash you can see the last value printed:

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

Of course there are other (probably more efficient) ways of debugging this problem, but the technique of using a breakpoint to display information or perform other scripted actions then continue running is a valuable thing to have in your debugging toolbox.

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
1

If I want to set a breakpoint at line 94 when I am in the 500th iteration I should do it like this:

b 94 if i=500

generally you would say:

break line_number if condition

Mona Jalal
  • 34,860
  • 64
  • 239
  • 408
1

You seem to be hung up on finding the iteration on which it breaks, but the answer from nos, above, clearly states how to do this.

Run your program in GDB, wait for the code to crash (at which point GDB will grab it), and then work out which iteration it's crashed in by printing the value of the index variable using print i at the GDB prompt.

Edit: Ok, I think I understand. When you say the code "breaks", you mean it's breaking in such a way that allows it to continue to be executed: it's not crashing, and GDB isn't automatically catching it.

In this case, there's no way to determine where to set the breakpoint you want. You simply don't know when the problem is occurring. How are you determining that the program is breaking? Are there any variables you could print the value of to show when the breakage occurs? If so, you could have GDB print the values during each iteration (rather than writing debug directly into the code).

You can do this using the commands option. There's an example of how to do this in this thread.

On each iteration print the value of i and also the value of whichever variable you're using to track the breakage. This should then give you the iteration on which the breakage occurs, and you can go back and set a breakpoint in the right place.

Community
  • 1
  • 1
Richard Horrocks
  • 419
  • 3
  • 19