Skip to content

Commit 74e873f

Browse files
committed
gdb rbreak_directory
1 parent d81e38f commit 74e873f

15 files changed

+224
-59
lines changed

c/function_pointer.c

+7-6
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ int main() {
3131
{
3232
int (*f)(int, int);
3333
f = add_int;
34-
assert((*f)(1, 2) == 3);
34+
assert(f(1, 2) == 3);
3535
f = sub_int;
36-
assert((*f)(1, 2) == -1);
36+
assert(f(1, 2) == -1);
3737
}
3838

3939
/* We can also add argument names if we are on a verbose mood. */
@@ -56,14 +56,15 @@ int main() {
5656
*/
5757
{
5858
int (*fs[])(int, int) = {add_int, sub_int};
59-
assert((*fs[0])(1, 2) == 3);
60-
assert((*fs[1])(1, 2) == -1);
59+
assert(fs[0](1, 2) == 3);
60+
assert(fs[1](1, 2) == -1);
6161
}
6262

6363
/*
6464
There are multiple ways to initialize and use function pointers because of implicit conversions.
6565
66-
http://stackoverflow.com/questions/6893285/why-do-function-pointer-definitions-work-with-any-number-of-ampersands-or-as
66+
- http://stackoverflow.com/questions/6893285/why-do-function-pointer-definitions-work-with-any-number-of-ampersands-or-as
67+
- http://stackoverflow.com/questions/23960436/is-the-asterisk-optional-when-calling-a-function-pointer
6768
*/
6869
{
6970
/* Alternative initialization methods. */
@@ -84,7 +85,7 @@ int main() {
8485

8586
/* ERROR no alternative for the declaration. */
8687
{
87-
/*int (f)(int n, int m) = add_int;*/
88+
/*int f(int n, int m) = add_int;*/
8889
}
8990
}
9091

c/struct.c

+8-12
Original file line numberDiff line numberDiff line change
@@ -338,11 +338,6 @@ int main() {
338338
It is possible to create structs which don't have a name.
339339
340340
Only the structs declared immediatiely after definition can be used.
341-
342-
In theory only standardized in C11, but I am yet to be able to make GCC generate a warning.
343-
even with `-std=89 -pedantic -Wall`.
344-
345-
http://stackoverflow.com/questions/14248044/are-anonymous-structs-standard-and-really-what-are-they
346341
*/
347342
{
348343
/* Basic. */
@@ -415,6 +410,13 @@ int main() {
415410
/*
416411
# typedef struct combo
417412
413+
TL;DR best practice: whenever possible use:
414+
415+
typedef struct {} S;
416+
417+
Some people, notably the Linux kernel, disagree:
418+
http://stackoverflow.com/questions/252780/why-should-we-typedef-a-struct-so-often-in-c
419+
418420
Advantages:
419421
420422
- avoid typing struct all over
@@ -430,10 +432,6 @@ int main() {
430432
- put all declaration information into one single place.
431433
No more "Should the typedef be before or after?" doubts.
432434
433-
TL;DR best practice: whenever possible use:
434-
435-
typedef struct {} S;
436-
437435
Unfortunately this cannot be done if you need to declare the struct elsewhere to:
438436
439437
- you use a pointer to a struct of the same type inside it.
@@ -487,9 +485,7 @@ int main() {
487485
/*
488486
The typedef and the struct can have the same name.
489487
490-
So the common C89 pattern is `typedef struct S {...} S`.
491-
492-
C11 adds anonymous structs which is even better.
488+
A common C89 pattern is `typedef struct S {...} S`.
493489
*/
494490
{
495491
typedef struct S {

c_one.makefile

-34
This file was deleted.

gdb/README.md

+2
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,11 @@ Tested on 7.7.1 unless mentioned otherwise.
3434
1. [argv-wrapper](argv-wrapper)
3535
1. [Call graph](call_graph.py)
3636
1. [Call graph rbreak](call_graph_rbreak.py)
37+
1. [Call graph walk filter](call_graph_walk_filter.py)
3738
1. [Continue until instruction](continue_instruction.py)
3839
1. [Continue until return](continue_return.py)
3940
1. [Break on return](break_return.py)
4041
1. [Disassemble minimal example](disassemble.py)
4142
1. [disas](disas.py)
43+
1. [rbreak_directory.py](rbreak_directory.py)
4244
1. [Internals](internals.md)

gdb/call_graph.py renamed to gdb/call_graph_walk.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
and adds a breakpoint at the functions it calls.
66
77
This is slower runtime than initial `rbreak .`,
8-
but typically faster as we move locally.
8+
but typically faster for large code bases as it avoids the huge initial rbreak overhead.
99
1010
- http://stackoverflow.com/questions/9549693/gdb-list-of-all-function-calls-made-in-an-application
1111
- http://stackoverflow.com/questions/311948/make-gdb-print-control-flow-of-functions-as-they-are-called

gdb/call_graph_walk_filter.py

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
"""
2+
## Call graph walk
3+
4+
TODO this is currenty broken. I half gave up on it when I noticed that
5+
the bottleneck was filtering functions by directory, which is harder on this walk implementation
6+
because we have to break at the callq instructions.
7+
I'm keeping this file as some of the walking code might be reused.
8+
9+
This implementation disassembles every function it stops at,
10+
and adds a breakpoint at the functions callq commands.
11+
12+
This is slower runtime than initial `rbreak .`,
13+
but typically faster as we move locally.
14+
15+
- http://stackoverflow.com/questions/9549693/gdb-list-of-all-function-calls-made-in-an-application
16+
- http://stackoverflow.com/questions/311948/make-gdb-print-control-flow-of-functions-as-they-are-called
17+
"""
18+
19+
import re
20+
21+
path_filter_re = re.compile(r'/gcc/')
22+
23+
gdb.execute('file cc1', to_string=True)
24+
gdb.execute('set args hello_world.c', to_string=True)
25+
gdb.execute('start', to_string=True)
26+
depth_string = 4 * ' '
27+
thread = gdb.inferiors()[0].threads()[0]
28+
# Function addresses that have already been disassembled.
29+
disassembled_functions = set()
30+
# Function addresses whose path does not match the filter regex.
31+
function_blacklist = set()
32+
while True:
33+
path_filter_re_matches = path_filter_re.search(source_path)
34+
frame = gdb.selected_frame()
35+
36+
if not path_filter_re_matches:
37+
pass
38+
# TODO
39+
40+
stack_depth = 0
41+
f = frame
42+
while f:
43+
stack_depth += 1
44+
f = f.older()
45+
46+
# Not present for files without debug symbols.
47+
source_path = '???'
48+
symtab = frame.find_sal().symtab
49+
if symtab:
50+
source_path = symtab.filename
51+
52+
# Not present for files without debug symbols.
53+
args = '???'
54+
block = None
55+
try:
56+
block = frame.block()
57+
except:
58+
pass
59+
if block:
60+
args = ''
61+
for symbol in block:
62+
if symbol.is_argument:
63+
args += '{} = {}, '.format(symbol.name, symbol.value(frame))
64+
65+
if path_filter_re_matches:
66+
# Put a breakpoint on the address of every funtion called from this function.
67+
# Only do that the first time we enter a function (TODO implement.)
68+
start = block.start
69+
if not start in disassembled_functions:
70+
disassembled_functions.add(start)
71+
end = block.end
72+
arch = frame.architecture()
73+
pc = gdb.selected_frame().pc()
74+
instructions = arch.disassemble(start, end - 1)
75+
for instruction in instructions:
76+
# This is UGLY. I wish there was a disassembly Python interface to GDB,
77+
# like https://github.com/aquynh/capstone which allows to extract
78+
# the opcode without parsing.
79+
instruction_parts = instruction['asm'].split()
80+
opcode = instruction_parts[0]
81+
if opcode == 'callq':
82+
# fails for instructions that start with *, basically rip (could be resolved to an address statically)
83+
# or rax (must be broken upon every time since we don't know where it will jump to).
84+
try:
85+
target_address = int(instruction_parts[1][2:], 16)
86+
except ValueError:
87+
target_address = None
88+
if not (target_address and target_address in function_blacklist):
89+
gdb.Breakpoint('*{}'.format(instruction['addr']), internal=True)
90+
91+
print('{}{} : {} : {}'.format(
92+
stack_depth * depth_string,
93+
source_path,
94+
frame.name(),
95+
args
96+
))
97+
98+
# We are at the call instruction.
99+
gdb.execute('continue', to_string=True)
100+
if thread.is_valid():
101+
# We are at the first instruction of the called function.
102+
gdb.execute('stepi', to_string=True)
103+
else:
104+
break

gdb/command.py

+4
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,16 @@ def __init__(self):
1515
False # Take subcommand?
1616
)
1717
def invoke(self, arg, from_tty):
18+
"""
19+
Everything printed here to stdout is captured by `execute to_string=True`.
20+
"""
1821
print('newcmd')
1922
assert type(arg) is str
2023
print('arg = ' + arg)
2124
print('from_tty = ' + str(from_tty))
2225
# This already registers it.
2326
NewCmd()
27+
2428
gdb.execute('help newcmd')
2529
print()
2630
gdb.execute('newcmd a b')

gdb/multifile/Makefile

-1
This file was deleted.

gdb/multifile/Makefile

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
.POSIX:
2+
3+
-include params.makefile
4+
5+
DIRS ?= . d e
6+
G ?= gdb3
7+
O ?= 0
8+
STD ?= c11
9+
CCC ?= gcc
10+
CCFLAGS ?= -DIMPLEMENTATION_SIGNAL -DUNDEFINED_BEHAVIOUR -g$(G) -pedantic-errors -std=$(STD) -O$(O) -Wextra -Wno-ignored-qualifiers -Wno-sign-compare -Wno-unused-variable -Wno-unused-label -Wno-unused-but-set-variable
11+
IN_EXT ?= .c
12+
LIBS ?= -lm
13+
OUT_EXT ?= .out
14+
RUN ?= main
15+
TEST ?= test
16+
TMP_EXT ?= .o
17+
18+
INS := $(foreach DIR,$(DIRS),$(wildcard $(DIR)/*$(IN_EXT)))
19+
OUTS_NOEXT := $(basename $(INS))
20+
OUTS := $(addsuffix $(TMP_EXT), $(OUTS_NOEXT))
21+
RUN_BASENAME := $(RUN)$(OUT_EXT)
22+
23+
.PHONY: clean run
24+
25+
$(RUN_BASENAME): $(OUTS)
26+
$(CCC) $(CCFLAGS) -o '$@' $+ $(LIBS)
27+
28+
%$(TMP_EXT): %$(IN_EXT)
29+
$(CCC) $(CCFLAGS) -c '$<' -o '$@' $(LIBS)
30+
31+
clean:
32+
for d in $(DIRS); do rm -f "$$d"/*'$(TMP_EXT)'; done
33+
rm -f '$(RUN_BASENAME)'
34+
35+
run: $(RUN_BASENAME)
36+
./'$(RUN_BASENAME)'

gdb/multifile/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# Multifile
22

3-
Test GDB with multiple source files.
3+
Test GDB with multiple source files and multiple directories.

gdb/multifile/a.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
int a = 1;
1+
int a_i = 1;
2+
int a_f() { return 1; }
3+
int a_g() { return 1; }

gdb/multifile/d/a.c

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
int d_a_i = 1;
2+
int d_a_f() { return 1; }
3+
int d_a_g() { return 1; }

gdb/multifile/d/b.c

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
int d_b_i = 1;
2+
int d_b_f() { return 1; }
3+
int d_b_g() { return 1; }

gdb/multifile/e/a.c

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
int e_a_i = 1;
2+
int e_a_f() { return 1; }
3+
int e_a_g() { return 1; }

gdb/multifile/main.c

+21-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,27 @@
11
#include <assert.h>
22

3-
int i;
4-
extern int a;
3+
extern int a_i, d_a_i, d_b_i, e_a_i;
4+
int a_f(), d_a_f(), e_a_f();
5+
int a_g(), d_a_g(), e_a_g();
6+
int d_b_f();
7+
int d_b_g();
8+
9+
int main_a = 1;
10+
int main_f() { return 1; }
11+
int main_g() { return 1; }
512

613
int main() {
7-
assert(a == 1);
14+
assert(main_a == 1);
15+
assert(main_f() == 1);
16+
assert(main_g() == 1);
17+
assert(d_a_i == 1);
18+
assert(d_a_f() == 1);
19+
assert(d_a_g() == 1);
20+
assert(d_b_i == 1);
21+
assert(d_b_f() == 1);
22+
assert(d_b_g() == 1);
23+
assert(e_a_i == 1);
24+
assert(e_a_f() == 1);
25+
assert(e_a_g() == 1);
826
return 0;
927
}

gdb/rbreak_directory.py

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""
2+
## rbreak all files in a directory
3+
4+
This method is dirty because it uses the gdb-only command to do the job, but works.
5+
6+
http://stackoverflow.com/questions/29437138/can-gdb-set-break-at-every-function-inside-a-directory
7+
"""
8+
9+
import os
10+
11+
class RbreakDir(gdb.Command):
12+
def __init__(self):
13+
super().__init__(
14+
'rbreak-dir',
15+
gdb.COMMAND_BREAKPOINTS,
16+
gdb.COMPLETE_NONE,
17+
False
18+
)
19+
def invoke(self, arg, from_tty):
20+
for root, dirs, files in os.walk(arg):
21+
for basename in files:
22+
path = os.path.join(root, basename)
23+
gdb.execute('rbreak {}:.'.format(path), to_string=True)
24+
RbreakDir()
25+
26+
gdb.execute('file multifile/main.out', to_string=True)
27+
gdb.execute('rbreak-dir multifile/d')
28+
gdb.execute('info breakpoints')

0 commit comments

Comments
 (0)