How to break in gdb based on array contents?

Viewed 334

I am trying to place a conditional breakpoint on a function that would check the contents of an array. My idea was to use memcmp() in the condition:

typedef struct {
    uint8_t arr[4];
} arg_t;

Then in gdb (declaring an array inline):

b func() if memcmp(arg.arr, (uint8_t[]){1, 2, 3, 4}, sizeof(arg.arr)) == 0

However, this does not work:

(gdb) c
Continuing.
Error in testing breakpoint condition:
Too many array elements

I can do it with if arg.arr[0] == 1 && arg.arr[1] == 2 && ... but in my real case access to the array (containing an IPv6 address) is rather convoluted, so it becomes unwieldy rather quickly.

UPDATE: After Mark's comment, I tried the following test program:

#include <stdio.h>
#include <stdint.h>
#include <string.h>

int main(void) {
  uint8_t a[] = { 1, 2, 3, 4 };
  printf("memcmp returns %d\n", memcmp(a, (uint8_t[4]){1,2,3,4}, sizeof(a)));
  return 0;
}                

After compiling it, I placed a conditional break on the return using the explicit array size syntax, and it worked but it does not seem to stop as expected:

(gdb) br hello.c:8 if memcmp(a, (uint8_t[4]){1,2,3,4}, sizeof(a)) == 0
Breakpoint 1 at 0x75c: file hello.c, line 8.
(gdb) run
Starting program: /mnt/c/stuff/src/test/hello
memcmp returns 0
[Inferior 1 (process 153) exited normally]

I was trying to evaluate the return value of the function at the break site manually:

(gdb) br hello.c:8
Note: breakpoint 1 also set at pc 0x800075c.
Breakpoint 2 at 0x800075c: file hello.c, line 8.
(gdb) dele 1
(gdb) run
Starting program: /mnt/c/stuff/src/test/hello
memcmp returns 0

Breakpoint 2, main () at hello.c:8
8       return 0;
(gdb) p  memcmp(a, (uint8_t[4]){1,2,3,4}, sizeof(a))
$1 = (int (*)(const void *, const void *, size_t)) 0x7fffff18aba0 <__memcmp_avx2_movbe>

I was surprised to see this, I suspect this may be due to memcmp() being a compiler intrinsic to the avx2 instruction, in which case I might need to cast it somehow?

2 Answers

This may not be what you want, but for complex tests, I get the program to help gdb by adding some check routines.


Method 1:

I create a void checkon (void) { if (expr_to_stop_on) badnews(); }

And then have: void badnews { stopme = 1; }

I then instrument the code with calls to checkon

I then tell gdb to do b badnews.


Method 2:

An alternative may be to create: int checkfail(void) { return expr_to_stop_on; }

Then, tell gdb to watch myarray.

This creates a watchpoint (using H/W assist). The watchpoint is like a breakpoint.

You can then do: cond 1 checkfail()


Method 3:

Similar to method 2, but instead of a watchpoint, use a tracepoint [with the same cond command (Actually, you may need to use actions instead).

This essentially single steps the program and allows a set of commands to be executed at every tracepoint.

This can be slow because every line has to do these things.

I think method 2 is probably the best bet.

(Posting my own answer to be able to close this, but credit goes to @mark-plotnick)

It works with the gdb builtin $_memeq instead of using memcmp - suspecting because of the latter not being a real function in my environment but YMMV:

(gdb) br arrbreak.c:8 if $_memeq(a, (uint8_t[4]){1,2,3,4}, sizeof(a))
Breakpoint 2 at 0x800075c: file arrbreak.c, line 8.
(gdb) run
Starting program: /mnt/c/stuff/src/test/arrbreak
memcmp returns 0

Breakpoint 2, main () at arrbreak.c:8
8         return 0;
(gdb) p $_memeq(a, (uint8_t[4]){1,2,3,4}, sizeof(a))
$1 = 1
(gdb) p $_memeq(a, (uint8_t[4]){1,2,3,5}, sizeof(a))
$2 = 0
(gdb)
Related