Skip to content

Commit 31d0a5c

Browse files
committed
gdb: Implement new functions to access vm structs
1 parent 88e76d1 commit 31d0a5c

File tree

4 files changed

+341
-0
lines changed

4 files changed

+341
-0
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 .nvirtmem 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/nvirtmem.py

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
import argparse
2+
import gdb
3+
4+
from .cmd import UserCommand, CommandDispatcher
5+
from .proc import Process
6+
from .utils import TextTable
7+
from .vm_map import Anon
8+
9+
10+
class VmInfo(CommandDispatcher):
11+
"""Examine virtual memory data structures."""
12+
13+
def __init__(self):
14+
super().__init__('vminfo', [UserPmap(),
15+
KernPmap(),
16+
VmMapDump(),
17+
AnonInfo(),
18+
SegmentInfo(),
19+
DumpAnon(),
20+
])
21+
22+
23+
class UserPmap(UserCommand):
24+
"""List active page entries in user pmap"""
25+
26+
def __init__(self):
27+
super().__init__('upmap')
28+
29+
def __call__(self, args):
30+
# TODO
31+
print(f'Dump user pmap: {args}')
32+
print('Not implemented yet!')
33+
34+
35+
class KernPmap(UserCommand):
36+
"""List active page entries in kernel pmap"""
37+
38+
def __init__(self):
39+
super().__init__('kpmap')
40+
41+
def __call__(self, args):
42+
# TODO
43+
print(f'Dump user pmap: {args}')
44+
print('Not implemented yet!')
45+
46+
47+
class VmMapDump(UserCommand):
48+
"""List segments describing virtual address space"""
49+
50+
def __init__(self):
51+
super().__init__('map')
52+
53+
def __call__(self, args):
54+
parser = argparse.ArgumentParser(
55+
description='Launch kernel in a board simulator.')
56+
parser.add_argument('-p', '--pid', type=int, default='-1',
57+
help='Pid of proc to display semgent.')
58+
parser.add_argument('-s', '--segment', type=int, default='-1',
59+
help='Number of segment to display.')
60+
args = parser.parse_args(args=args.split())
61+
62+
if args.pid == -1:
63+
proc = Process.from_current()
64+
else:
65+
proc = Process.find_by_pid(args.pid)
66+
67+
entries = proc.p_uspace.get_entries()
68+
69+
table = TextTable(types='ittttt', align='rrrrrr')
70+
table.header(['segment', 'start', 'end', 'prot', 'flags', 'amap'])
71+
for idx, seg in enumerate(entries):
72+
table.add_row([idx, hex(seg.start), hex(seg.end), seg.prot,
73+
seg.flags, seg.aref])
74+
print(table)
75+
76+
77+
class SegmentInfo(UserCommand):
78+
"""Show info about i-th segment in proc vm_map"""
79+
80+
def __init__(self):
81+
super().__init__('seg')
82+
83+
def _print_segment(self, seg, pid, id):
84+
print('------------------------------')
85+
print('Segment {} in proc {}'.format(id, pid))
86+
print('Range: {:#08x}-{:#08x} ({:d} pages)'.format(seg.start,
87+
seg.end,
88+
seg.pages))
89+
print('Prot: {}'.format(seg.prot))
90+
print('Flags: {}'.format(seg.flags))
91+
amap = seg.amap
92+
if amap:
93+
print('Amap: {}'.format(seg.amap_ptr))
94+
print('Amap offset: {}'.format(seg.amap_offset))
95+
print('Amap slots: {}'.format(amap.slots))
96+
print('Amap refs: {}'.format(amap.ref_cnt))
97+
else:
98+
print('Amap: NULL')
99+
100+
def __call__(self, args):
101+
parser = argparse.ArgumentParser(
102+
description='Launch kernel in a board simulator.')
103+
parser.add_argument('-p', '--pid', type=int, default='-1',
104+
help='Pid of proc to display semgent.')
105+
parser.add_argument('-s', '--segment', type=int, default='-1',
106+
help='Number of segment to display.')
107+
args = parser.parse_args(args=args.split())
108+
109+
if args.pid == -1:
110+
proc = Process.from_current()
111+
else:
112+
proc = Process.find_by_pid(args.pid)
113+
114+
entries = proc.p_uspace.get_entries()
115+
116+
if args.segment > len(entries):
117+
print(f'Segment {id} does not exist!')
118+
return
119+
120+
if args.segment == -1:
121+
for idx, seg in enumerate(entries):
122+
self._print_segment(seg, proc.p_pid, idx)
123+
else:
124+
self._print_segment(entries[args.segment],
125+
proc.p_pid, args.segment)
126+
127+
128+
class AnonInfo(UserCommand):
129+
"""Dump info about given anon"""
130+
131+
def __init__(self):
132+
super().__init__('anon')
133+
134+
def __call__(self, args):
135+
parser = argparse.ArgumentParser(
136+
description='Launch kernel in a board simulator.')
137+
parser.add_argument('anon', type=lambda x: int(x, 0),
138+
default='-1', help='Addr of anon to find.')
139+
args = parser.parse_args(args=args.split())
140+
141+
an = Anon(gdb.Value(args.anon)
142+
.cast(gdb.lookup_type('struct vm_anon').pointer()))
143+
144+
print('Anon page: {}'.format(an.page))
145+
print('Anon refs: {}'.format(an.ref_cnt))
146+
print('Refs:')
147+
148+
table = TextTable(types='iiti', align='cccc')
149+
table.header(['proc', 'entry', 'range', 'offset'])
150+
procs = list(Process.list_all())
151+
cnt = 0
152+
for p in procs:
153+
entries = p.p_uspace.get_entries()
154+
for i, e in enumerate(entries):
155+
off = e.find_anon(an)
156+
if off != -1:
157+
cnt += 1
158+
table.add_row([p.p_pid, i,
159+
'{:#0x}-{:#0x}'.format(e.start, e.end),
160+
off])
161+
print(table)
162+
163+
if an.ref_cnt != cnt:
164+
print('Anon should be in {} entries. Is in {}.'.format(an.ref_cnt,
165+
cnt))
166+
167+
168+
class DumpAnon(UserCommand):
169+
"""Dump info about given anon"""
170+
171+
def __init__(self):
172+
super().__init__('dump_anon')
173+
174+
def _dump_anons(self, seg, pid, id):
175+
print('------------------------------')
176+
print('Segment {} in proc {}'.format(id, pid))
177+
print('Range: {:#08x}-{:#08x} ({:d} pages)'.format(seg.start,
178+
seg.end,
179+
seg.pages))
180+
print('Prot: {}'.format(seg.prot))
181+
print('Flags: {}'.format(seg.flags))
182+
print('Anons:')
183+
184+
anons = seg.get_anons()
185+
for i, a in anons:
186+
print(' {:>3}: {}'.format(i, a))
187+
188+
def __call__(self, args):
189+
parser = argparse.ArgumentParser(
190+
description='Launch kernel in a board simulator.')
191+
parser.add_argument('-p', '--pid', type=int, default='-1',
192+
help='Pid of proc to display semgent.')
193+
parser.add_argument('-s', '--segment', type=int, default='-1',
194+
help='Number of segment to display.')
195+
args = parser.parse_args(args=args.split())
196+
197+
if args.pid == -1:
198+
proc = Process.from_current()
199+
else:
200+
proc = Process.find_by_pid(args.pid)
201+
202+
entries = proc.p_uspace.get_entries()
203+
204+
if args.segment > len(entries):
205+
print(f'Segment {id} does not exist!')
206+
return
207+
208+
if args.segment == -1:
209+
for idx, seg in enumerate(entries):
210+
self._dump_anons(seg, proc.p_pid, idx)
211+
else:
212+
self._dump_anons(entries[args.segment],
213+
proc.p_pid, args.segment)

sys/debug/proc.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
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/vm_map.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import gdb
2+
from .struct import GdbStructMeta
3+
from .struct import TailQueue
4+
5+
6+
class VmMap(metaclass=GdbStructMeta):
7+
__ctype__ = 'struct vm_map'
8+
9+
def __repr__(self):
10+
return 'vm_map[entries=[{} {}], pmap={}]'.format(
11+
self.entries, self.nentries, self.pmap)
12+
13+
def get_entries(self):
14+
entries = TailQueue(self.entries, 'link')
15+
return [VmMapEntry(e) for e in entries]
16+
17+
18+
class VmMapEntry(metaclass=GdbStructMeta):
19+
__ctype__ = 'struct vm_map_entry'
20+
__cast__ = {'start': int,
21+
'end': int}
22+
23+
def __repr__(self):
24+
return 'vm_map_entry[{:#08x}-{:#08x}]'.format(self.start, self.end)
25+
26+
@property
27+
def amap_ptr(self):
28+
return self.aref['amap']
29+
30+
@property
31+
def pages(self):
32+
size = self.end - self.start
33+
return int(size / int(gdb.parse_and_eval('PAGESIZE')))
34+
35+
@property
36+
def amap(self):
37+
if int(self.aref['amap']) == 0:
38+
return None
39+
else:
40+
return Amap(self.aref['amap'])
41+
42+
@property
43+
def amap_offset(self):
44+
return self.aref['offset']
45+
46+
def amap_bitmap_str(self):
47+
return self.amap.str_bitmap(self.amap_offset, self.pages)
48+
49+
def get_anon(self, offset):
50+
if self.amap is None:
51+
return None
52+
53+
return self.amap.get_anon(self.amap_offset + offset)
54+
55+
def get_anons(self):
56+
if self.amap is None:
57+
return []
58+
59+
return self.amap.get_anons(self.amap_offset, self.pages)
60+
61+
def find_anon(self, anon):
62+
if not self.amap:
63+
return -1
64+
65+
anons = self.amap.get_anons(self.amap_offset, self.pages)
66+
for i, a in anons:
67+
if a == anon:
68+
return i
69+
70+
return -1
71+
72+
73+
def _bitmap_get(bitmap, offset):
74+
byte = offset / 8
75+
bit = offset % 8
76+
return ((bitmap[byte] >> bit) & 1) == 1
77+
78+
79+
class Anon(metaclass=GdbStructMeta):
80+
__ctype__ = 'struct vm_anon'
81+
__cast__ = {'ref_cnt': int}
82+
83+
def __repr__(self):
84+
return 'vm_anon[refs={}, page={:#0x}, addr={:#0x}]'.format(
85+
int(self.ref_cnt),
86+
int(self.page),
87+
int(self._obj))
88+
89+
@property
90+
def addr(self):
91+
return int(self._obj)
92+
93+
def __eq__(self, other):
94+
return int(self.addr) == int(other.addr)
95+
96+
97+
class Amap(metaclass=GdbStructMeta):
98+
__ctype__ = 'struct vm_amap'
99+
__cast__ = {'slots': int,
100+
'ref_cnt': int}
101+
102+
def __repr__(self):
103+
return 'vm_amap[slots={}, refs={}]'.format(self.slots, self.ref_cnt)
104+
105+
def _get_bitmap(self):
106+
bitmap = []
107+
bitssize = ((self.slots) + 7) >> 3
108+
for i in range(bitssize):
109+
bitmap.append(self.anon_bitmap[i])
110+
return bitmap
111+
112+
def get_anons(self, offset, slots):
113+
bitmap = self._get_bitmap()
114+
anons = []
115+
for i in range(slots):
116+
if _bitmap_get(bitmap, offset + i):
117+
anons.append((i, Anon(self.anon_list[offset + i])))
118+
return anons

0 commit comments

Comments
 (0)