Skip to content

Commit 777b742

Browse files
committed
More gdb python
1 parent 86e6e43 commit 777b742

30 files changed

+634
-45
lines changed

README.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ Relies on [C++ boilerplate](https://github.com/cirosantilli/cpp-boilerplate) to
2828
1. [gprof](gprof.md)
2929
1. [Valgrind](valgrind.md)
3030
1. [perf](perf.md)
31-
1. [GDB](gdb/)
31+
1. Analysis tools
32+
1. [GDB](gdb/)
33+
1. [cflow](cflow.md)
3234
1. [Boost](boost/)
3335
1. [CMake](cmake.md)
3436
1. [Flex and Bison](flex-bison/)

c/extern/Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
../c_one.makefile
1+
../../c_one.makefile

c/implementations.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ Then there are several implementations that target Linux-only embedded systems,
7575
- musl libc. Comparison: <http://www.etalabs.net/compare_libcs.html>. Alpine Linux moved to it, as it is partially binary compatible with glibc.
7676
- Bionic (Android): <https://en.wikipedia.org/wiki/Bionic_%28software%29>
7777
- dietlibc <http://www.fefe.de/dietlibc/>
78-
- newlibc <https://en.wikipedia.org/wiki/Newlib>
78+
- newlibc <https://en.wikipedia.org/wiki/Newlib>. GNU.
7979

8080
### ulibc
8181

c/operator.c

+8-1
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,14 @@ int main() {
592592
assert((i &= (char)0x00) == (char)0x00);
593593
assert((char)i == (char)0x00);
594594

595-
/* same others bitwise, except ~= */
595+
/* Same for others bitwise, except ~= which does not exist. */
596+
{
597+
unsigned char i = 0xFF;
598+
i = ~i;
599+
/* ? */
600+
/*i~=;*/
601+
assert((i & 0xFF) == 0);
602+
}
596603
}
597604

598605
/*

c_one.makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ LIBS ?= -lm
1212
OUT_EXT ?= .out
1313
RUN ?= main
1414
TEST ?= test
15-
TMP_EXT ?= .tmp
15+
TMP_EXT ?= .o
1616

1717
INS := $(wildcard *$(IN_EXT))
1818
OUTS_NOEXT := $(basename $(INS))

cflow.md

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# cflow
2+
3+
Make static call graphs.
4+
5+
GNU.
6+
7+
<https://en.wikipedia.org/wiki/GNU_cflow>
8+
9+
<http://www.gnu.org/software/cflow/>
10+
11+
Usage:
12+
13+
cflow main.c
14+
15+
Sample output:
16+
17+
main() <int main () at main.c:49>:
18+
printA()
19+
printMain() <void printMain () at main.c:40>:
20+
printf()
21+
printf()
22+
23+
TODO C++ method? Does not seem to work.

gdb/README.md

+27-18
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,33 @@ GNU debugger.
77
1. [Invocation](invocation.md)
88
1. [Commands](commands.md)
99
1. Examples
10-
1. [run](run)
11-
1. [nested.c](nested.c)
12-
1. [count_infinite.c](count_infinite.c)
13-
1. [big_function.c](big_function.c)
14-
1. [String](string.c)
15-
1. [C++ function overload](overload.cpp)
16-
1. [Segmentation Fault](segv.c)
10+
1. [run](run)
11+
1. [nested.c](nested.c)
12+
1. [count_infinite.c](count_infinite.c)
13+
1. [big_function.c](big_function.c)
14+
1. [String](string.c)
15+
1. [C++ function overload](overload.cpp)
16+
1. [Segmentation Fault](segv.c)
17+
1. [Multifile](multifile/)
1718
1. GDB scripts
18-
1. [step_all](step_all.gdb)
19-
1. [si_all](si_all.gdb)
19+
1. [step_all](step_all.gdb)
20+
1. [si_all](si_all.gdb)
2021
1. [Python interface](python-interface.md)
21-
[Multiple Python invocations](multiple_python_invocations)
22-
[help.py](help.py)
23-
[hello_world.py](hello_world.py)
24-
[Visible variables](visible-variables.py)
25-
[Block, Symbol](block.py)
26-
[print_lines.py](print_lines.py)
27-
[Value](value.py)
28-
[read_var()](read_var.py)
29-
[argv-wrapper](argv-wrapper)
22+
1. [Multiple Python invocations](multiple_python_invocations)
23+
1. [help.py](help.py)
24+
1. [hello_world.py](hello_world.py)
25+
1. [Visible variables](visible-variables.py)
26+
1. [Globals](globals.py)
27+
1. [Block, Symbol](block.py)
28+
1. [print_lines.py](print_lines.py)
29+
1. [Value](value.py)
30+
1. [read_var()](read_var.py)
31+
1. [Command](command)
32+
1. [argv-wrapper](argv-wrapper)
33+
1. [Call graph](call_graph_rbreak.py)
34+
1. [Continue until instruction](continue_instruction.py)
35+
1. [Continue until return](continue_return.py)
36+
1. [Break on return](break_return.py)
37+
1. [Disassemble minimal example](disassemble.py)
38+
1. [disas](disas.py)
3039
1. [Internals](internals.md)

gdb/break_return.py

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""
2+
## Break return
3+
4+
Add a breakpoint to every return instruction of the current function.
5+
6+
http://stackoverflow.com/questions/3649468/setting-breakpoint-in-gdb-where-the-function-returns
7+
"""
8+
9+
class BreakReturn(gdb.Command):
10+
def __init__(self):
11+
super().__init__(
12+
'break-return',
13+
gdb.COMMAND_RUNNING,
14+
gdb.COMPLETE_NONE,
15+
False
16+
)
17+
def invoke(self, arg, from_tty):
18+
frame = gdb.selected_frame()
19+
block = frame.block()
20+
while block:
21+
if block.function:
22+
break
23+
start = block.start
24+
end = block.end
25+
arch = frame.architecture()
26+
pc = gdb.selected_frame().pc()
27+
instructions = arch.disassemble(start, end - 1)
28+
for instruction in instructions:
29+
if instruction['asm'].startswith('retq '):
30+
gdb.Breakpoint('*{}'.format(instruction['addr']))
31+
BreakReturn()
32+
33+
gdb.execute('file continue_return_py.out', to_string=True)
34+
gdb.execute('start', to_string=True)
35+
gdb.execute('disas')
36+
print()
37+
gdb.execute('break-return')
38+
gdb.execute('continue')
39+
gdb.execute('disas')

gdb/call_graph.py

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
"""
2+
## Call graph
3+
4+
This implementation disassembles every function it stops at,
5+
and adds a breakpoint at the functions it calls.
6+
7+
This is slower runtime than initial `rbreak .`,
8+
but typically faster as we move locally.
9+
10+
- http://stackoverflow.com/questions/9549693/gdb-list-of-all-function-calls-made-in-an-application
11+
- http://stackoverflow.com/questions/311948/make-gdb-print-control-flow-of-functions-as-they-are-called
12+
"""
13+
14+
gdb.execute('file call_graph_py.out', to_string=True)
15+
# rbreak before start to ignore dynamically linked stdlib functions.
16+
gdb.execute('set confirm off')
17+
gdb.execute('start', to_string=True)
18+
depth_string = 4 * ' '
19+
thread = gdb.inferiors()[0].threads()[0]
20+
while thread.is_valid():
21+
frame = gdb.selected_frame()
22+
symtab = frame.find_sal().symtab
23+
24+
stack_depth = 0
25+
f = frame
26+
while f:
27+
stack_depth += 1
28+
f = f.older()
29+
30+
# Not present for files without debug symbols.
31+
source_path = '???'
32+
if symtab:
33+
#source_path = symtab.fullname()
34+
source_path = symtab.filename
35+
36+
# Not present for files without debug symbols.
37+
args = '???'
38+
block = None
39+
try:
40+
block = frame.block()
41+
except:
42+
pass
43+
if block:
44+
args = ''
45+
for symbol in block:
46+
if symbol.is_argument:
47+
args += '{} = {}, '.format(symbol.name, symbol.value(frame))
48+
49+
# Mark new breakpoints.
50+
while block:
51+
if block.function:
52+
break
53+
start = block.start
54+
end = block.end
55+
arch = frame.architecture()
56+
pc = gdb.selected_frame().pc()
57+
instructions = arch.disassemble(start, end - 1)
58+
for instruction in instructions:
59+
print('{:x} {}'.format(instruction['addr'], instruction['asm']))
60+
61+
print('{}{} : {} : {}'.format(
62+
stack_depth * depth_string,
63+
source_path,
64+
frame.name(),
65+
args
66+
))
67+
gdb.execute('continue', to_string=True)

gdb/call_graph_py.c

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
Stack trace tests: bt, f, whe
3+
*/
4+
5+
#include <stdio.h>
6+
7+
int f0_0_0(int i) {
8+
return i + 3;
9+
}
10+
11+
int f0_0_1(int i) {
12+
return i + 2;
13+
}
14+
15+
int f0_1(int i) {
16+
return i + 1;
17+
}
18+
19+
int f0_0(int i) {
20+
i += f0_0_0(i);
21+
i += f0_0_1(i);
22+
i += f0_0_0(i);
23+
return i;
24+
}
25+
26+
int f0(int i) {
27+
i += f0_0(i);
28+
i += f0_0(i);
29+
i += f0_1(i);
30+
return i;
31+
}
32+
33+
int main() {
34+
printf("%d\n", f0(1));
35+
return 0;
36+
}

gdb/call_graph_rbreak.py

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
"""
2+
## Call graph rbreak
3+
4+
Build a function call graph of the non-dynamic functions of an executable.
5+
6+
This implementation is very slow for very large projects, where `rbreak .` takes forever to complete.
7+
8+
- http://stackoverflow.com/questions/9549693/gdb-list-of-all-function-calls-made-in-an-application
9+
- http://stackoverflow.com/questions/311948/make-gdb-print-control-flow-of-functions-as-they-are-called
10+
"""
11+
12+
gdb.execute('file cc1', to_string=True)
13+
gdb.execute('rbreak .', to_string=True)
14+
gdb.execute('set args hello_world.c', to_string=True)
15+
# rbreak before start to ignore dynamically linked stdlib functions.
16+
gdb.execute('set confirm off')
17+
gdb.execute('start', to_string=True)
18+
depth_string = 4 * ' '
19+
thread = gdb.inferiors()[0].threads()[0]
20+
while thread.is_valid():
21+
frame = gdb.selected_frame()
22+
symtab = frame.find_sal().symtab
23+
24+
stack_depth = 0
25+
f = frame
26+
while f:
27+
stack_depth += 1
28+
f = f.older()
29+
30+
# Not present for files without debug symbols.
31+
source_path = '???'
32+
if symtab:
33+
#source_path = symtab.fullname()
34+
source_path = symtab.filename
35+
36+
# Not present for files without debug symbols.
37+
args = '???'
38+
block = None
39+
try:
40+
block = frame.block()
41+
except:
42+
pass
43+
if block:
44+
args = ''
45+
for symbol in block:
46+
if symbol.is_argument:
47+
args += '{} = {}, '.format(symbol.name, symbol.value(frame))
48+
49+
print('{}{} : {} : {}'.format(
50+
stack_depth * depth_string,
51+
source_path,
52+
frame.name(),
53+
args
54+
))
55+
gdb.execute('continue', to_string=True)

gdb/command.py

+14-9
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,26 @@
11
"""
22
Define a new GDB command with Python.
3+
4+
https://sourceware.org/gdb/onlinedocs/gdb/Commands-In-Python.html
35
"""
46

5-
class NewCmd (gdb.Command):
6-
"newcmd docstring."
7-
def __init__ (self):
7+
class NewCmd(gdb.Command):
8+
# Will appear on help newcmd.
9+
"docstring"
10+
def __init__(self):
811
super().__init__(
9-
'newcmd',
10-
gdb.COMMAND_SUPPORT,
11-
gdb.COMPLETE_NONE,
12-
True
12+
'newcmd', # Name.
13+
gdb.COMMAND_NONE, # Category when `help` is entered.
14+
gdb.COMPLETE_NONE, # No autocomplete.
15+
False # Take subcommand?
1316
)
1417
def invoke(self, arg, from_tty):
1518
print('newcmd')
16-
print('arg = ' + str(arg))
19+
assert type(arg) is str
20+
print('arg = ' + arg)
1721
print('from_tty = ' + str(from_tty))
22+
# This already registers it.
1823
NewCmd()
1924
gdb.execute('help newcmd')
20-
gdb.execute('newcmd')
25+
print()
2126
gdb.execute('newcmd a b')

0 commit comments

Comments
 (0)