gdb script performance

Jean-Marc Saffroy
2006-10-05T12:16:58Z

[-- Attachment #1: Type: TEXT/PLAIN, Size: 3480 bytes --]

Hello,

I'm using gdb (6.5) to analyse a Linux kernel kdump (really an ELF core
file), and I find that a simple script is awwwfully slow. Here is an
example:

define cntmaps
shell date
set $map = vmlist
set $gcount = 0
while $map != 0
set $gcount = $gcount + 1
set $map = $map->next
end
printf "vmlist count: %d\n", $gcount
shell date
end

(gdb) cntmaps
Thu Oct 5 13:44:04 CEST 2006
vmlist count: 99
Thu Oct 5 13:44:14 CEST 2006

So it takes about 10 seconds to walk a list with ~100 elements, without
doing anything really useful (the host is a 2GHz dual core!). I have
another script that does more complex processing with the same list, and
it takes about 12 minutes to complete! It's the lsmaps macro below, which
ends up calling the vmap macro 2381 times.

So I rebuilt gdb with profiling (-pg), attached is the complete output
from gprof after running lsmaps in batch mode. A quick look suggests that
some symbol lookups are problematic, and I suspect that gdb easily ends up
doing linear searches in some big table (I'm working on a Linux kernel
with full debug infos).

Any suggestion for performance improvement? Maybe gdb could use hash
tables to speed up things a bit? :-)

Cheers,

--
saffroy@gmail.com

# a few useful(?) macros for x86-64 VMM hacks

# useful constants
set $PAGE_SIZE = (1<<12)
set $__PHYSICAL_MASK = (1 << 46)-1
set $PTE_MASK = ~($PAGE_SIZE-1) & $__PHYSICAL_MASK
set $__PAGE_OFFSET = 0xffff810000000000
set $_PAGE_PSE = 0x80

define vmap
set $addr = (long)$arg0
# index in each of the 4 levels of page directories
set $pgd = $addr >> 39 & (1<<9)-1
set $pud = $addr >> 30 & (1<<9)-1
set $pmd = $addr >> 21 & (1<<9)-1
set $pte = $addr >> 12 & (1<<9)-1
# offset in page
set $off = $addr & (1<<12)-1
#printf "%03x %03x %03x %03x %03x\n", $pgd, $pud, $pmd, $pte, $off

set $pgd_off = (pgd_t *) &init_level4_pgt + $pgd
#printf "pgd_off: %lx pgd: %lx\n", $pgd_off, (long)$pgd_off->pgd
set $pgd_page = ((long)$pgd_off->pgd & $PTE_MASK) + $__PAGE_OFFSET
#printf "pgd_page: %lx\n", $pgd_page

set $pud_off = ((pud_t *) $pgd_page) + $pud
#printf "pud_off: %lx pud: %lx\n", $pud_off, (long)$pud_off->pud
set $pud_page = ((long)$pud_off->pud & $PTE_MASK) + $__PAGE_OFFSET
#printf "pud_page: %lx\n", $pud_page

set $pmd_off = ((pmd_t *) $pud_page) + $pmd
#printf "pmd_off: %lx pmd: %lx\n", $pmd_off, (long)$pmd_off->pmd
set $pmd_page = ((long)$pmd_off->pmd & $PTE_MASK) + $__PAGE_OFFSET
#printf "pmd_page: %lx\n", $pmd_page

if ((long)$pmd_off->pmd & $_PAGE_PSE) != 0
#printf "PSE page! "
set $paddr = $pmd_page + ($addr & (1<<21)-1)
else
set $pte_off = ((pte_t *) $pmd_page) + $pte
#printf "pte_off: %lx pte: %lx\n", $pte_off, (long)$pte_off->pte
set $pte_page = ((long)$pte_off->pte & $PTE_MASK) + $__PAGE_OFFSET
#printf "pte_page: %lx\n", $pte_page
set $paddr = $pte_page + $off
end
#printf "remapped physical addr: %lx\n", $paddr
printf "%lx -> %lx\n", $addr, $paddr
end

define lsmaps
set $map = vmlist
set $gcount = 0
while $map != 0
if $map->pages != 0
set $vaddr = (long)$map->addr
set $count = (long)$map->size / $PAGE_SIZE
set $gcount = $gcount + $count -1
while $count > 1
vmap $vaddr
set $vaddr = $vaddr + $PAGE_SIZE
set $count = $count - 1
end
end
set $map = $map->next
end
printf "page count: %d\n", $gcount
end

[-- Attachment #2: Type: APPLICATION/octet-stream, Size: 63566 bytes --]