Skip to content

Commit b2e0738

Browse files
committed
Adds a sample TUI extension.
1 parent edb5a80 commit b2e0738

File tree

6 files changed

+220
-0
lines changed

6 files changed

+220
-0
lines changed

python/tree-tui/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
tree-tui

python/tree-tui/Makefile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
CC = clang
3+
4+
all: tree-tui
5+
6+
tree-tui: tree-tui.c
7+
$(CC) -O0 -ggdb3 tree-tui.c -o tree-tui
8+
9+
clean:
10+
$(RM) tree-tui
11+
12+
.PHONY: all clean

python/tree-tui/README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# tree-tui
2+
3+
A sample extension which provides a TUI window that renders the first tree found
4+
in the current frame.
5+
6+
## Usage
7+
8+
Run gdb and either use the tree-tui.gdb file to load the extension, or do so manually.
9+
Breaking in main and stepping through it will show how the tree is build. Breaking in
10+
`break_here` will show the full tree.
11+
12+
A new TUI layout needs to be created like `tui new-layout tree tree 1 cmd 1`.
13+
Then, switch to the new layout with `layout tree`. The tree-tui.gdb file
14+
will do that and sets breakpoints in `main` and `break_here`.
15+
16+
Example output:
17+
18+
```
19+
$ DEBUGINFOD_URLS= gdb -x ./tree-tui.gdb -ex run -ex continue ./tree-tui
20+
└── Root
21+
├── Child
22+
│ ├── Grandchild
23+
│ ├── Grandchild
24+
│ ├── Grandchild
25+
│ ├── Grandchild
26+
│ ├── Grandchild
27+
│ ├── Grandchild
28+
│ └── Grandchild
29+
├── Child
30+
```

python/tree-tui/tree-tui.c

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#include <stdlib.h>
2+
#include <string.h>
3+
4+
struct Node {
5+
char* name;
6+
struct Node* childs[10];
7+
};
8+
9+
typedef struct Node* Node;
10+
11+
Node tree_create_child(struct Node* parent, const char* name)
12+
{
13+
for (int i = 0; i < 10; ++i) {
14+
if (NULL == parent->childs[i]) {
15+
parent->childs[i] = calloc(1, sizeof(struct Node));
16+
parent->childs[i]->name = strdup(name);
17+
return parent->childs[i];
18+
}
19+
}
20+
return NULL;
21+
}
22+
23+
Node create_tree(const char* name)
24+
{
25+
Node result = calloc(1, sizeof(struct Node));
26+
result->name = strdup(name);
27+
return result;
28+
}
29+
30+
void tree_free(Node tree)
31+
{
32+
free(tree->name);
33+
for (int i = 0; i < 10; ++i) {
34+
if (NULL != tree->childs[i]) {
35+
tree_free(tree->childs[i]);
36+
}
37+
}
38+
free(tree);
39+
}
40+
41+
void break_here(Node tree)
42+
{
43+
// Do nothing, set breakpoint here
44+
}
45+
46+
int main(void)
47+
{
48+
Node tree = create_tree("Root");
49+
for (int i = 0; i < 5; ++i) {
50+
Node child = tree_create_child(tree, "Child");
51+
for (int j = 0; j < 7; ++j) {
52+
tree_create_child(child, "Grandchild");
53+
}
54+
}
55+
break_here(tree);
56+
tree_free(tree);
57+
return 0;
58+
}

python/tree-tui/tree-tui.gdb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
source tree-tui.py
3+
tui new-layout tree tree 1 cmd 1
4+
tui new-layout tree-src tree 1 src 1 cmd 1
5+
layout tree
6+
break main
7+
break break_here

python/tree-tui/tree-tui.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import gdb
2+
3+
4+
class TreeTui:
5+
"""A TUI window showing the first Node found on the stack"""
6+
def __init__(self, window):
7+
# The current TUI window
8+
self.__window = window
9+
self.__window.title = 'Tree View'
10+
# The tree node found in __on_stop
11+
self.__tree = None
12+
# vertical position for scrolling
13+
self.__vpos = 0
14+
# horizontal position for scrolling
15+
self.__hpos = 0
16+
# content holding the whole tree as a string
17+
self.__content = ''
18+
# current line while rendering
19+
self.__current_line = 0
20+
# Create an event listener, which triggers rendering and
21+
# discovers the tree
22+
gdb.events.before_prompt.connect(self.__on_stop)
23+
24+
def __on_stop(self):
25+
"""Callback which searches for a Node object and renders the window"""
26+
self.__tree = None
27+
frame = gdb.selected_frame()
28+
try:
29+
block = frame.block()
30+
except RuntimeError:
31+
# Can happen if there are no debugging information
32+
return
33+
# Go through all the blocks until a Node is found
34+
while not self.__tree and block:
35+
# Go through this block and search for a Node
36+
for x in block:
37+
if not x.is_valid():
38+
continue
39+
typ = x.type.strip_typedefs()
40+
try:
41+
val = x.value(frame)
42+
except Exception:
43+
continue
44+
if typ.code == gdb.TYPE_CODE_PTR:
45+
if val and typ.target() == gdb.lookup_type("struct Node"):
46+
self.__tree = val.dereference()
47+
break
48+
block = block.superblock
49+
# Always render again!
50+
self.render()
51+
52+
def close(self):
53+
"""Callback for window close event, disconnect the even listener"""
54+
gdb.events.before_prompt.disconnect(self.__on_stop)
55+
56+
def vscroll(self, amount):
57+
"""Callback when the user scrolls vertically"""
58+
self.__vpos = max(0, self.__vpos + amount)
59+
# Always render again!
60+
self.render()
61+
62+
def hscroll(self, amount):
63+
"""Callback when the user scrolls horizontally"""
64+
self.__hpos = max(0, self.__hpos + amount)
65+
# Always render again!
66+
self.render()
67+
68+
def __render_line(self, content):
69+
"""Render a single line, handles scroll position and width/height"""
70+
self.__current_line += 1
71+
if self.__current_line > self.__window.height + self.__vpos:
72+
return
73+
if self.__current_line <= self.__vpos:
74+
return
75+
content = content[self.__hpos:]
76+
content = content[:self.__window.width]
77+
self.__content += content + '\n'
78+
79+
def __render(self, tree, last, header):
80+
"""Render the tree recursively"""
81+
header_line = header
82+
header_line += '└── ' if last else '├── '
83+
try:
84+
header_line += tree['name'].string()
85+
except gdb.MemoryError:
86+
return
87+
self.__render_line(header_line)
88+
try:
89+
children = [tree['childs'][i] for i in range(10) if tree['childs'][i]]
90+
except gdb.MemoryError:
91+
children = []
92+
for i in range(len(children)):
93+
next_header = ' ' if last else '│ '
94+
next_last = (i == len(children) - 1)
95+
self.__render(tree['childs'][i], next_last, header + next_header)
96+
97+
def render(self):
98+
"""Render window callback"""
99+
self.__content = ''
100+
self.__current_line = 0
101+
self.__window.erase()
102+
if self.__tree:
103+
self.__render(self.__tree, True, '')
104+
self.__window.write(self.__content)
105+
else:
106+
self.__window.write('\n\n')
107+
self.__window.write("No tree found".center(self.__window.width))
108+
self.__window.write('\n')
109+
110+
111+
# Register the new TUI window
112+
gdb.register_window_type("tree", TreeTui)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy