Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GDB: Improve tools to debug vm structures #1396

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions sys/debug/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from .sync import CondVar, Mutex
from .thread import Kthread, Thread, CurrentThread
from .events import stop_handler
from .virtmem import VmInfo


def addPrettyPrinters():
Expand All @@ -32,6 +33,7 @@ def addPrettyPrinters():
Kthread()
Ktrace()
Kgmon()
VmInfo()

# Functions
CurrentThread()
Expand Down
8 changes: 4 additions & 4 deletions sys/debug/kdump.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from .virtmem import VmPhysSeg, VmFreePages, VmMapSeg, PhysMap
from .memory import Vmem, MallocStats, PoolStats
from .cmd import CommandDispatcher

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

def __init__(self):
super().__init__('kdump', [VmPhysSeg(), VmFreePages(), VmMapSeg(),
PhysMap(), Vmem(), MallocStats(),
PoolStats()])
super().__init__('kdump', [Vmem(),
MallocStats(),
PoolStats(),
])
10 changes: 9 additions & 1 deletion sys/debug/proc.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import gdb

from .cmd import SimpleCommand, AutoCompleteMixin
from .cmd import SimpleCommand
from .utils import TextTable, global_var
from .struct import GdbStructMeta, TailQueue, enum
from .thread import Thread
from .vm_map import VmMap
from .sync import Mutex


Expand All @@ -12,6 +13,7 @@ class Process(metaclass=GdbStructMeta):
__cast__ = {'p_pid': int,
'p_lock': Mutex,
'p_thread': Thread,
'p_uspace': VmMap,
'p_state': enum}

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

@classmethod
def find_by_pid(cls, pid):
for p in cls.list_all():
if p.p_pid == pid:
return p

def __repr__(self):
return 'proc{pid=%d}' % self.p_pid

Expand Down
27 changes: 27 additions & 0 deletions sys/debug/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ def cast(value, typename):
return value.cast(gdb.lookup_type(typename))


def cast_ptr(value, typename):
return value.cast(gdb.lookup_type(typename).pointer())


def local_var(name):
return gdb.newest_frame().read_var(name)

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


def get_arch():
try:
franciscozdo marked this conversation as resolved.
Show resolved Hide resolved
_ = gdb.parse_and_eval('aarch64_init')
return 'aarch64'
except gdb.error:
pass

try:
_ = gdb.parse_and_eval('riscv_init')
franciscozdo marked this conversation as resolved.
Show resolved Hide resolved
return 'riscv'
except gdb.error:
pass

try:
_ = gdb.parse_and_eval('mips_init')
return 'mips'
except gdb.error:
pass

print('Current architecture is not supported')
raise KeyError
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should not it be NotImplementedError ?



# calculates address of ret instruction within function body (MIPS specific)
def func_ret_addr(name):
s = gdb.execute('disass thread_create', to_string=True)
Expand Down
231 changes: 187 additions & 44 deletions sys/debug/virtmem.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,207 @@
import gdb

from .struct import TailQueue
from .cmd import UserCommand
from .cmd import UserCommand, CommandDispatcher
from .cpu import TLBLo
from .utils import TextTable, global_var, cast
from .utils import TextTable, global_var, cast_ptr, get_arch
from .proc import Process


PM_NQUEUES = 16


class PhysMap(UserCommand):
class VmInfo(CommandDispatcher):
"""Examine virtual memory data structures."""

def __init__(self):
super().__init__('vm', [KernelPmap(),
UserPmap(),
VmMapDump(),
CurrentSegmentInfo(),
ProcSegmentInfo(),
VmPhysSeg(),
VmFreePages(),
])


def _print_mips_pmap(pmap):
franciscozdo marked this conversation as resolved.
Show resolved Hide resolved
pdp = cast_ptr(pmap['pde'], 'pde_t')
table = TextTable(types='ttttt', align='rrrrr')
table.header(['vpn', 'pte0', 'pte1', 'pte2', 'pte3'])
for i in range(1024):
pde = TLBLo(pdp[i])
if not pde.valid:
continue
ptp = cast_ptr(pde.ppn, 'pte_t')
pte = [TLBLo(ptp[j]) for j in range(1024)]
for j in range(0, 1024, 4):
if not any(pte.valid for pte in pte[j:j+4]):
continue
pte4 = [str(pte) if pte.valid else '-' for pte in pte[j:j+4]]
table.add_row(['{:8x}'.format((i << 22) + (j << 12)),
pte4[0], pte4[1], pte4[2], pte4[3]])
print(table)


class KernelPmap(UserCommand):
"""List active page entries in kernel pmap"""

def __init__(self):
super().__init__('pmap')
super().__init__('pmap_kernel')

def __call__(self, args):
pdp = global_var('kernel_pmap')['pde']
table = TextTable(types='ttttt', align='rrrrr')
table.header(['vpn', 'pte0', 'pte1', 'pte2', 'pte3'])
for i in range(1024):
pde = TLBLo(pdp[i])
if not pde.valid:
continue
ptp = pde.ppn.cast(gdb.lookup_type('pte_t').pointer())
pte = [TLBLo(ptp[j]) for j in range(1024)]
for j in range(0, 1024, 4):
if not any(pte.valid for pte in pte[j:j+4]):
continue
pte4 = [str(pte) if pte.valid else '-' for pte in pte[j:j+4]]
table.add_row(['{:8x}'.format((i << 22) + (j << 12)),
pte4[0], pte4[1], pte4[2], pte4[3]])
pmap = global_var('kernel_pmap')
arch = get_arch()
if arch == 'mips':
_print_mips_pmap(pmap)
else:
print(f"Can't print {arch} pmap")


class UserPmap(UserCommand):
"""List active page entries in user pmap

vm pmap_user [pid]

Arguments:
pid pid of process to show pmap for

If argument is not specifed the pmap of current process is dumped.
"""

def __init__(self):
super().__init__('pmap_user')

def __call__(self, args):
args = args.split()
if len(args) == 0:
proc = Process.from_current()
else:
pid = int(args[0])
proc = Process.find_by_pid(pid)
if proc is None:
print(f'Process {pid} not found')
return
pmap = proc.p_uspace.pmap
arch = get_arch()
if arch == 'mips':
_print_mips_pmap(pmap)
else:
print(f"Can't print {arch} pmap")


class VmMapDump(UserCommand):
"""List segments describing virtual address space

vm map [pid]

Arguments:
pid pid of process to list vm_map for

If argument is not specifed the vm_map of current process is listed.
"""

def __init__(self):
super().__init__('map')

def __call__(self, args):
args = args.split()
if len(args) == 0:
proc = Process.from_current()
else:
pid = int(args[0])
proc = Process.find_by_pid(pid)
if proc is None:
print(f'Process {pid} not found')
return

entries = proc.p_uspace.get_entries()

table = TextTable(types='ittttt', align='rrrrrr')
table.header(['segment', 'start', 'end', 'prot', 'flags', 'amap'])
for idx, seg in enumerate(entries):
table.add_row([idx, hex(seg.start), hex(seg.end), seg.prot,
seg.flags, seg.aref])
print(table)


def _print_segment(seg, pid, id):
print('Segment {} in proc {}'.format(id, pid))
franciscozdo marked this conversation as resolved.
Show resolved Hide resolved
print('Range: {:#08x}-{:#08x} ({:d} pages)'.format(seg.start,
seg.end,
seg.pages))
print('Prot: {}'.format(seg.prot))
print('Flags: {}'.format(seg.flags))
amap = seg.amap
if amap:
print('Amap: {}'.format(seg.amap_ptr))
print('Amap offset: {}'.format(seg.amap_offset))
print('Amap slots: {}'.format(amap.slots))
print('Amap refs: {}'.format(amap.ref_cnt))

amap.print_pages(seg.amap_offset, seg.pages)
else:
print('Amap: NULL')


def _segment_info(proc, segment):
entries = proc.p_uspace.get_entries()
if segment < 4096:
franciscozdo marked this conversation as resolved.
Show resolved Hide resolved
# Lookup by id
if segment > len(entries):
print(f'Segment {segment} does not exist!')
return
_print_segment(entries[segment], proc.p_pid, segment)
else:
# Lookup by address
addr = segment
for idx, e in enumerate(entries):
if e.start <= addr and addr < e.end:
_print_segment(e, proc.p_pid, idx)
return
print(f'Segment with address {addr} not found')


class CurrentSegmentInfo(UserCommand):
"""Show info about i-th segment in curent proc vm_map"""

def __init__(self):
super().__init__('segment')

def __call__(self, args):
args = args.split()
if len(args) < 1:
print('require argument (segment)')
return
proc = Process.from_current()
segment = int(args[0], 0)
_segment_info(proc, segment)


class ProcSegmentInfo(UserCommand):
"""Show info about i-th segment in proc vm_map"""

def __init__(self):
super().__init__('segment_proc')

def __call__(self, args):
args = args.split()
if len(args) < 2:
print('require 2 arguments (pid and segment)')
return

pid = int(args[0])
proc = Process.find_by_pid(pid)
if proc is None:
print(f'Process {pid} not found')
return
segment = int(args[1], 0)
_segment_info(proc, segment)


class VmPhysSeg(UserCommand):
"""List physical memory segments managed by vm subsystem"""

def __init__(self):
super().__init__('vm_physseg')
super().__init__('physseg')

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

def __init__(self):
super().__init__('vm_freepages')
super().__init__('freepages')

def __call__(self, args):
table = TextTable(align='rrl', types='iit')
Expand All @@ -71,24 +235,3 @@ def __call__(self, args):
segments = TailQueue(global_var('seglist'), 'seglink')
pages = int(sum(seg['npages'] for seg in segments if not seg['used']))
print('Used pages count: {}'.format(pages - free_pages))


class VmMapSeg(UserCommand):
"""List segments describing virtual address space"""

def __init__(self):
super().__init__('vm_map')

def __call__(self, args):
vm_map = gdb.parse_and_eval('vm_map_user()')
if vm_map == 0:
print('No active user vm_map!')
return
entries = vm_map['entries']
table = TextTable(types='ittttt', align='rrrrrr')
table.header(['segment', 'start', 'end', 'prot', 'flags', 'amap'])
segments = TailQueue(entries, 'link')
for idx, seg in enumerate(segments):
table.add_row([idx, seg['start'], seg['end'], seg['prot'],
seg['flags'], seg['aref']])
print(table)
Loading