Skip to content

Commit 74e873f

Browse files
committed
gdb rbreak_directory
1 parent d81e38f commit 74e873f

File tree

15 files changed

+224
-59
lines changed

15 files changed

+224
-59
lines changed

c/function_pointer.c

Lines changed: 7 additions & 6 deletions
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

Lines changed: 8 additions & 12 deletions
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

Lines changed: 0 additions & 34 deletions
This file was deleted.

gdb/README.md

Lines changed: 2 additions & 0 deletions
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)
Lines changed: 1 addition & 1 deletion
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

Lines changed: 104 additions & 0 deletions
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

Lines changed: 4 additions & 0 deletions
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

Lines changed: 0 additions & 1 deletion
This file was deleted.

gdb/multifile/Makefile

Lines changed: 36 additions & 0 deletions
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

Lines changed: 1 addition & 1 deletion
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.

0 commit comments

Comments
 (0)