Skip to content

Commit 4a8d1e7

Browse files
committed
GDB: Improve debugging options for VM subsystem
1 parent 6383814 commit 4a8d1e7

File tree

5 files changed

+202
-50
lines changed

5 files changed

+202
-50
lines changed

sys/debug/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from .sync import CondVar, Mutex
1111
from .thread import Kthread, Thread, CurrentThread
1212
from .events import stop_handler
13+
from .virtmem import VmInfo
1314

1415

1516
def addPrettyPrinters():
@@ -32,6 +33,7 @@ def addPrettyPrinters():
3233
Kthread()
3334
Ktrace()
3435
Kgmon()
36+
VmInfo()
3537

3638
# Functions
3739
CurrentThread()

sys/debug/kdump.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
from .virtmem import VmPhysSeg, VmFreePages, VmMapSeg, PhysMap
21
from .memory import Vmem, MallocStats, PoolStats
32
from .cmd import CommandDispatcher
43

@@ -7,6 +6,7 @@ class Kdump(CommandDispatcher):
76
"""Examine kernel data structures."""
87

98
def __init__(self):
10-
super().__init__('kdump', [VmPhysSeg(), VmFreePages(), VmMapSeg(),
11-
PhysMap(), Vmem(), MallocStats(),
12-
PoolStats()])
9+
super().__init__('kdump', [Vmem(),
10+
MallocStats(),
11+
PoolStats(),
12+
])

sys/debug/proc.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import gdb
22

3-
from .cmd import SimpleCommand, AutoCompleteMixin
3+
from .cmd import SimpleCommand
44
from .utils import TextTable, global_var
55
from .struct import GdbStructMeta, TailQueue, enum
66
from .thread import Thread
7+
from .vm_map import VmMap
78
from .sync import Mutex
89

910

@@ -12,6 +13,7 @@ class Process(metaclass=GdbStructMeta):
1213
__cast__ = {'p_pid': int,
1314
'p_lock': Mutex,
1415
'p_thread': Thread,
16+
'p_uspace': VmMap,
1517
'p_state': enum}
1618

1719
@staticmethod
@@ -32,6 +34,12 @@ def list_all(cls):
3234
dead = TailQueue(global_var('zombie_list'), 'p_all')
3335
return map(cls, list(alive) + list(dead))
3436

37+
@classmethod
38+
def find_by_pid(cls, pid):
39+
for p in cls.list_all():
40+
if p.p_pid == pid:
41+
return p
42+
3543
def __repr__(self):
3644
return 'proc{pid=%d}' % self.p_pid
3745

sys/debug/utils.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ def cast(value, typename):
99
return value.cast(gdb.lookup_type(typename))
1010

1111

12+
def cast_ptr(value, typename):
13+
return value.cast(gdb.lookup_type(typename).pointer())
14+
15+
1216
def local_var(name):
1317
return gdb.newest_frame().read_var(name)
1418

@@ -21,6 +25,29 @@ def relpath(path):
2125
return path.rsplit('sys/')[-1]
2226

2327

28+
def get_arch():
29+
try:
30+
_ = gdb.parse_and_eval('aarch64_init')
31+
return 'aarch64'
32+
except gdb.error:
33+
pass
34+
35+
try:
36+
_ = gdb.parse_and_eval('riscv_init')
37+
return 'riscv'
38+
except gdb.error:
39+
pass
40+
41+
try:
42+
_ = gdb.parse_and_eval('mips_init')
43+
return 'mips'
44+
except gdb.error:
45+
pass
46+
47+
print('Current architecture is not supported')
48+
raise KeyError
49+
50+
2451
# calculates address of ret instruction within function body (MIPS specific)
2552
def func_ret_addr(name):
2653
s = gdb.execute('disass thread_create', to_string=True)

sys/debug/virtmem.py

Lines changed: 160 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,179 @@
1-
import gdb
2-
31
from .struct import TailQueue
4-
from .cmd import UserCommand
2+
from .cmd import UserCommand, CommandDispatcher
53
from .cpu import TLBLo
6-
from .utils import TextTable, global_var, cast
4+
from .utils import TextTable, global_var, cast_ptr, get_arch
5+
from .proc import Process
76

87

98
PM_NQUEUES = 16
109

1110

12-
class PhysMap(UserCommand):
13-
"""List active page entries in kernel pmap"""
11+
class VmInfo(CommandDispatcher):
12+
"""Examine virtual memory data structures."""
13+
1414
def __init__(self):
15-
super().__init__('pmap')
15+
super().__init__('vm', [DumpPmap('kernel'),
16+
DumpPmap('user'),
17+
VmMapDump(),
18+
SegmentInfo(''),
19+
SegmentInfo('proc'),
20+
VmPhysSeg(),
21+
VmFreePages(),
22+
])
23+
24+
25+
def _print_mips_pmap(pmap):
26+
pdp = cast_ptr(pmap['pde'], 'pde_t')
27+
table = TextTable(types='ttttt', align='rrrrr')
28+
table.header(['vpn', 'pte0', 'pte1', 'pte2', 'pte3'])
29+
for i in range(1024):
30+
pde = TLBLo(pdp[i])
31+
if not pde.valid:
32+
continue
33+
ptp = cast_ptr(pde.ppn, 'pte_t')
34+
pte = [TLBLo(ptp[j]) for j in range(1024)]
35+
for j in range(0, 1024, 4):
36+
if not any(pte.valid for pte in pte[j:j+4]):
37+
continue
38+
pte4 = [str(pte) if pte.valid else '-' for pte in pte[j:j+4]]
39+
table.add_row(['{:8x}'.format((i << 22) + (j << 12)),
40+
pte4[0], pte4[1], pte4[2], pte4[3]])
41+
print(table)
42+
43+
44+
class DumpPmap(UserCommand):
45+
"""List active page entries in user pmap"""
46+
47+
def __init__(self, typ):
48+
command = 'pmap_' + typ
49+
if command not in ('pmap_user', 'pmap_kernel'):
50+
print(f'{command} command not supported')
51+
return
52+
self.command = command
53+
super().__init__(command)
1654

1755
def __call__(self, args):
18-
pdp = global_var('kernel_pmap')['pde']
19-
table = TextTable(types='ttttt', align='rrrrr')
20-
table.header(['vpn', 'pte0', 'pte1', 'pte2', 'pte3'])
21-
for i in range(1024):
22-
pde = TLBLo(pdp[i])
23-
if not pde.valid:
24-
continue
25-
ptp = pde.ppn.cast(gdb.lookup_type('pte_t').pointer())
26-
pte = [TLBLo(ptp[j]) for j in range(1024)]
27-
for j in range(0, 1024, 4):
28-
if not any(pte.valid for pte in pte[j:j+4]):
29-
continue
30-
pte4 = [str(pte) if pte.valid else '-' for pte in pte[j:j+4]]
31-
table.add_row(['{:8x}'.format((i << 22) + (j << 12)),
32-
pte4[0], pte4[1], pte4[2], pte4[3]])
56+
if self.command == 'pmap_kernel':
57+
pmap = global_var('kernel_pmap')
58+
else:
59+
args = args.split()
60+
if len(args) == 0:
61+
proc = Process.from_current()
62+
else:
63+
pid = int(args[0])
64+
proc = Process.find_by_pid(pid)
65+
if proc is None:
66+
print(f'Process {pid} not found')
67+
return
68+
pmap = proc.p_uspace.pmap
69+
70+
arch = get_arch()
71+
if arch == 'mips':
72+
_print_mips_pmap(pmap)
73+
else:
74+
print(f"Can't print {arch} pmap")
75+
76+
77+
class VmMapDump(UserCommand):
78+
"""List segments describing virtual address space"""
79+
80+
def __init__(self):
81+
super().__init__('map')
82+
83+
def __call__(self, args):
84+
args = args.split()
85+
if len(args) == 0:
86+
proc = Process.from_current()
87+
else:
88+
pid = int(args[0])
89+
proc = Process.find_by_pid(pid)
90+
if proc is None:
91+
print(f'Process {pid} not found')
92+
return
93+
94+
entries = proc.p_uspace.get_entries()
95+
96+
table = TextTable(types='ittttt', align='rrrrrr')
97+
table.header(['segment', 'start', 'end', 'prot', 'flags', 'amap'])
98+
for idx, seg in enumerate(entries):
99+
table.add_row([idx, hex(seg.start), hex(seg.end), seg.prot,
100+
seg.flags, seg.aref])
33101
print(table)
34102

35103

104+
class SegmentInfo(UserCommand):
105+
"""Show info about i-th segment in proc vm_map"""
106+
107+
def __init__(self, typ):
108+
command = f"segment{'_' if typ != '' else ''}{typ}"
109+
if command not in ['segment', 'segment_proc']:
110+
print(f'{command} command not supported')
111+
return
112+
self.command = command
113+
super().__init__(command)
114+
115+
def _print_segment(self, seg, pid, id):
116+
print('Segment {} in proc {}'.format(id, pid))
117+
print('Range: {:#08x}-{:#08x} ({:d} pages)'.format(seg.start,
118+
seg.end,
119+
seg.pages))
120+
print('Prot: {}'.format(seg.prot))
121+
print('Flags: {}'.format(seg.flags))
122+
amap = seg.amap
123+
if amap:
124+
print('Amap: {}'.format(seg.amap_ptr))
125+
print('Amap offset: {}'.format(seg.amap_offset))
126+
print('Amap slots: {}'.format(amap.slots))
127+
print('Amap refs: {}'.format(amap.ref_cnt))
128+
129+
# TODO: show used pages/anons
130+
else:
131+
print('Amap: NULL')
132+
133+
def __call__(self, args):
134+
args = args.split()
135+
if self.command == 'segment':
136+
if len(args) < 1:
137+
print('require argument (segment)')
138+
return
139+
proc = Process.from_current()
140+
else:
141+
if len(args) < 2:
142+
print('require 2 arguments (pid and segment)')
143+
return
144+
145+
pid = int(args[0])
146+
proc = Process.find_by_pid(pid)
147+
if proc is None:
148+
print(f'Process {pid} not found')
149+
return
150+
args = args[1:]
151+
152+
entries = proc.p_uspace.get_entries()
153+
154+
segment = int(args[0], 0)
155+
156+
if segment < 4096:
157+
# Lookup by id
158+
if segment > len(entries):
159+
print(f'Segment {segment} does not exist!')
160+
return
161+
self._print_segment(entries[segment], proc.p_pid, segment)
162+
else:
163+
# Lookup by address
164+
addr = segment
165+
for idx, e in enumerate(entries):
166+
if e.start <= addr and addr < e.end:
167+
self._print_segment(e, proc.p_pid, idx)
168+
return
169+
print(f'Segment with address {addr} not found')
170+
171+
36172
class VmPhysSeg(UserCommand):
37173
"""List physical memory segments managed by vm subsystem"""
38174

39175
def __init__(self):
40-
super().__init__('vm_physseg')
176+
super().__init__('physseg')
41177

42178
def __call__(self, args):
43179
table = TextTable(types='ittit', align='rrrrr')
@@ -53,7 +189,7 @@ class VmFreePages(UserCommand):
53189
"""List free pages known to vm subsystem"""
54190

55191
def __init__(self):
56-
super().__init__('vm_freepages')
192+
super().__init__('freepages')
57193

58194
def __call__(self, args):
59195
table = TextTable(align='rrl', types='iit')
@@ -71,24 +207,3 @@ def __call__(self, args):
71207
segments = TailQueue(global_var('seglist'), 'seglink')
72208
pages = int(sum(seg['npages'] for seg in segments if not seg['used']))
73209
print('Used pages count: {}'.format(pages - free_pages))
74-
75-
76-
class VmMapSeg(UserCommand):
77-
"""List segments describing virtual address space"""
78-
79-
def __init__(self):
80-
super().__init__('vm_map')
81-
82-
def __call__(self, args):
83-
vm_map = gdb.parse_and_eval('vm_map_user()')
84-
if vm_map == 0:
85-
print('No active user vm_map!')
86-
return
87-
entries = vm_map['entries']
88-
table = TextTable(types='ittttt', align='rrrrrr')
89-
table.header(['segment', 'start', 'end', 'prot', 'flags', 'amap'])
90-
segments = TailQueue(entries, 'link')
91-
for idx, seg in enumerate(segments):
92-
table.add_row([idx, seg['start'], seg['end'], seg['prot'],
93-
seg['flags'], seg['aref']])
94-
print(table)

0 commit comments

Comments
 (0)