Skip to content

Instantly share code, notes, and snippets.

@thejh
Created July 29, 2018 20:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save thejh/6eddd199d71f5e5a221b827c7aa2a271 to your computer and use it in GitHub Desktop.
Save thejh/6eddd199d71f5e5a221b827c7aa2a271 to your computer and use it in GitHub Desktop.
GDB script for checking memory usage of completely unused pages in free glibc malloc chunks
import os
import struct
# flags are encoded into chunk size
FLAGS_MASK = 0x7
PAGEMAP_PRESENT = 1<<63
PAGEMAP_SWAPPED = 1<<62
main_arena = gdb.parse_and_eval('&main_arena')
bins_per_arena = int(gdb.parse_and_eval(
'sizeof(main_arena.bins)/sizeof(main_arena.bins[0])/2'))
chunk_type = gdb.lookup_type('mchunkptr')
meta_size = int(gdb.parse_and_eval('sizeof(struct malloc_chunk)'))
pagemap_fd = os.open('/proc/'+str(gdb.selected_inferior().pid)+'/pagemap',
os.O_RDONLY)
def pagemap_read(page_addr):
assert (page_addr & 0xfff) == 0
bs = os.pread(pagemap_fd, 8, page_addr / 0x1000 * 8)
if len(bs) != 8:
raise 'pagemap'
return struct.unpack('=Q', bs)[0]
arenap = main_arena
# make sure all the arenas are safe to access
while True:
arena = arenap.dereference()
if arena['mutex'] != 0:
print('ERROR: locked arena')
break
arenap = arena['next']
if arenap == main_arena:
break
# scan arenas for free stuff
present_pages = 0
swapped_pages = 0
def check_chunk(chunkp):
global present_pages
global swapped_pages
chunk = chunkp.dereference()
real_size = chunk['size'] & ~FLAGS_MASK
if real_size > 0x1000+meta_size:
free_start_page = (int(chunkp) + meta_size + 0xfff) & ~0xfff
free_end_page = (int(chunkp) + real_size) & ~0xfff
for page in range(free_start_page, free_end_page, 0x1000):
page_state = pagemap_read(page)
if page_state & PAGEMAP_PRESENT:
present_pages += 1
if page_state & PAGEMAP_SWAPPED:
swapped_pages += 1
while True:
arena = arenap.dereference()
top = arena['top']
if top != 0:
check_chunk(top)
bins = arena['bins'][0].address
for i in range(0, bins_per_arena):
head_chunk = (bins + i*2 - 2).cast(chunk_type)
chunkp = head_chunk.dereference()['fd']
while chunkp != head_chunk and chunkp != 0:
check_chunk(chunkp)
chunkp = chunkp.dereference()['fd']
arenap = arena['next']
if arenap == main_arena:
break
# dump results
print('present garbage: '+str(present_pages*4)+' KiB')
print('swapped garbage: '+str(swapped_pages*4)+' KiB')
import os
import struct
# flags are encoded into chunk size
FLAGS_MASK = 0x7
PAGEMAP_PRESENT = 1<<63
PAGEMAP_SWAPPED = 1<<62
main_arena = gdb.parse_and_eval('&main_arena')
bins_per_arena = int(gdb.parse_and_eval(
'sizeof(main_arena.bins)/sizeof(main_arena.bins[0])/2'))
chunk_type = gdb.lookup_type('mchunkptr')
meta_size = int(gdb.parse_and_eval('sizeof(struct malloc_chunk)'))
pagemap_fd = os.open('/proc/'+str(gdb.selected_inferior().pid)+'/pagemap',
os.O_RDONLY)
def pagemap_read(page_addr):
assert (page_addr & 0xfff) == 0
bs = os.pread(pagemap_fd, 8, page_addr / 0x1000 * 8)
if len(bs) != 8:
raise 'pagemap'
return struct.unpack('=Q', bs)[0]
arenap = main_arena
# make sure all the arenas are safe to access
while True:
arena = arenap.dereference()
if arena['mutex'] != 0:
print('ERROR: locked arena')
break
arenap = arena['next']
if arenap == main_arena:
break
# scan arenas for free stuff
present_pages = 0
swapped_pages = 0
def check_chunk(chunkp):
global present_pages
global swapped_pages
chunk = chunkp.dereference()
real_size = chunk['size'] & ~FLAGS_MASK
if real_size > 0x1000+meta_size:
free_start_page = (int(chunkp) + meta_size + 0xfff) & ~0xfff
free_end_page = (int(chunkp) + real_size) & ~0xfff
for page in range(free_start_page, free_end_page, 0x1000):
page_state = pagemap_read(page)
if page_state & PAGEMAP_PRESENT:
present_pages += 1
if page_state & PAGEMAP_SWAPPED:
swapped_pages += 1
if free_start_page != free_end_page:
gdb.execute('print madvise('+hex(free_start_page)+', '+
hex(free_end_page - free_start_page)+', 4)')
while True:
arena = arenap.dereference()
top = arena['top']
if top != 0:
check_chunk(top)
bins = arena['bins'][0].address
for i in range(0, bins_per_arena):
head_chunk = (bins + i*2 - 2).cast(chunk_type)
chunkp = head_chunk.dereference()['fd']
while chunkp != head_chunk and chunkp != 0:
check_chunk(chunkp)
chunkp = chunkp.dereference()['fd']
arenap = arena['next']
if arenap == main_arena:
break
# dump results
print('present garbage: '+str(present_pages*4)+' KiB')
print('swapped garbage: '+str(swapped_pages*4)+' KiB')
# cat /tmp/procs12 | while read line; do pid="$(echo "$line" | cut -d' ' -f2)"; echo "==== $line ===="; egrep 'RssAnon|VmSwap' /proc/$pid/status; gdb -batch -ex 'source ./gdb-anaheap.py' -p $pid 2>/dev/null | grep garbage; done
==== daemon 471 /usr/sbin/atd ====
RssAnon: 48 kB
VmSwap: 160 kB
present garbage: 0 KiB
swapped garbage: 4 KiB
==== Debian-+ 1232 /usr/sbin/exim4 ====
RssAnon: 224 kB
VmSwap: 508 kB
present garbage: 0 KiB
swapped garbage: 0 KiB
==== debian-+ 7346 /usr/bin/python ====
RssAnon: 53916 kB
VmSwap: 0 kB
present garbage: 3768 KiB
swapped garbage: 0 KiB
==== libvirt+ 18020 qemu-system-x86_64 ====
RssAnon: 698892 kB
VmSwap: 656 kB
present garbage: 43096 KiB
swapped garbage: 0 KiB
==== message+ 480 /usr/bin/dbus-daemon ====
RssAnon: 260 kB
VmSwap: 248 kB
present garbage: 40 KiB
swapped garbage: 0 KiB
==== ntp 19351 /usr/sbin/ntpd ====
RssAnon: 580 kB
VmSwap: 0 kB
present garbage: 24 KiB
swapped garbage: 0 KiB
==== postgres 638 /usr/lib/postgresql/9.6/bin/postgres ====
RssAnon: 248 kB
VmSwap: 1524 kB
present garbage: 0 KiB
swapped garbage: 20 KiB
==== postgres 694 postgres: ====
RssAnon: 196 kB
VmSwap: 1576 kB
present garbage: 0 KiB
swapped garbage: 0 KiB
==== postgres 697 postgres: ====
RssAnon: 228 kB
VmSwap: 1544 kB
present garbage: 0 KiB
swapped garbage: 16 KiB
==== postgres 699 postgres: ====
RssAnon: 184 kB
VmSwap: 1588 kB
present garbage: 0 KiB
swapped garbage: 4 KiB
==== postgres 700 postgres: ====
RssAnon: 284 kB
VmSwap: 1760 kB
present garbage: 16 KiB
swapped garbage: 0 KiB
==== postgres 701 postgres: ====
RssAnon: 252 kB
VmSwap: 1520 kB
present garbage: 0 KiB
swapped garbage: 0 KiB
==== root 11355 /usr/bin/atop ====
RssAnon: 4464 kB
VmSwap: 0 kB
present garbage: 924 KiB
swapped garbage: 0 KiB
==== root 1338 /sbin/dhclient ====
RssAnon: 228 kB
VmSwap: 820 kB
present garbage: 12 KiB
swapped garbage: 0 KiB
==== root 1430 /sbin/dhclient ====
RssAnon: 224 kB
VmSwap: 836 kB
present garbage: 12 KiB
swapped garbage: 0 KiB
==== root 1 /sbin/init ====
RssAnon: 1504 kB
VmSwap: 392 kB
present garbage: 88 KiB
swapped garbage: 44 KiB
==== root 224 /lib/systemd/systemd-journald ====
RssAnon: 248 kB
VmSwap: 284 kB
present garbage: 16 KiB
swapped garbage: 4 KiB
==== root 258 /sbin/lvmetad ====
RssAnon: 116 kB
VmSwap: 136 kB
present garbage: 8 KiB
swapped garbage: 0 KiB
==== root 261 /lib/systemd/systemd-udevd ====
RssAnon: 224 kB
VmSwap: 652 kB
present garbage: 4 KiB
swapped garbage: 364 KiB
==== root 458 /sbin/rpcbind ====
RssAnon: 260 kB
VmSwap: 308 kB
present garbage: 68 KiB
swapped garbage: 0 KiB
==== root 467 /usr/sbin/acpid ====
RssAnon: 0 kB
VmSwap: 92 kB
present garbage: 0 KiB
swapped garbage: 12 KiB
==== root 469 /usr/sbin/rsyslogd ====
RssAnon: 1156 kB
VmSwap: 476 kB
present garbage: 20 KiB
swapped garbage: 156 KiB
==== root 470 /usr/sbin/irqbalance ====
RssAnon: 156 kB
VmSwap: 216 kB
present garbage: 4 KiB
swapped garbage: 32 KiB
==== root 472 /lib/systemd/systemd-logind ====
RssAnon: 280 kB
VmSwap: 316 kB
present garbage: 60 KiB
swapped garbage: 12 KiB
==== root 478 /usr/sbin/cron ====
RssAnon: 132 kB
VmSwap: 160 kB
present garbage: 0 KiB
swapped garbage: 12 KiB
==== root 484 /usr/sbin/atopacctd ====
RssAnon: 68 kB
VmSwap: 44 kB
present garbage: 0 KiB
swapped garbage: 4 KiB
==== root 510 /usr/sbin/openvpn ====
RssAnon: 704 kB
VmSwap: 316 kB
present garbage: 60 KiB
swapped garbage: 0 KiB
==== root 790 /usr/sbin/libvirtd ====
RssAnon: 6648 kB
VmSwap: 6696 kB
present garbage: 3804 KiB
swapped garbage: 3856 KiB
==== root 800 /usr/sbin/sshd ====
RssAnon: 196 kB
VmSwap: 584 kB
present garbage: 0 KiB
swapped garbage: 12 KiB
==== root 803 /sbin/agetty ====
RssAnon: 24 kB
VmSwap: 108 kB
present garbage: 0 KiB
swapped garbage: 4 KiB
==== root 846 /usr/sbin/dhcpd ====
RssAnon: 644 kB
VmSwap: 9192 kB
present garbage: 8 KiB
swapped garbage: 0 KiB
==== root 959 /usr/sbin/virtlogd ====
RssAnon: 240 kB
VmSwap: 900 kB
present garbage: 12 KiB
swapped garbage: 8 KiB
# free
total used free shared buff/cache available
Mem: 8091692 1093692 228020 92304 6769980 6635080
Swap: 8300540 38468 8262072
# cat /tmp/procs12 | while read line; do pid="$(echo "$line" | cut -d' ' -f2)"; echo "==== $line ===="; egrep 'RssAnon|VmSwap' /proc/$pid/status; gdb -batch -ex 'source ./gdb-cleanheap.py' -p $pid 2>/dev/null | grep garbage; done
==== daemon 471 /usr/sbin/atd ====
RssAnon: 48 kB
VmSwap: 160 kB
present garbage: 0 KiB
swapped garbage: 4 KiB
==== Debian-+ 1232 /usr/sbin/exim4 ====
RssAnon: 224 kB
VmSwap: 508 kB
present garbage: 0 KiB
swapped garbage: 0 KiB
==== debian-+ 7346 /usr/bin/python ====
RssAnon: 53916 kB
VmSwap: 0 kB
present garbage: 3772 KiB
swapped garbage: 0 KiB
==== libvirt+ 18020 qemu-system-x86_64 ====
RssAnon: 698892 kB
VmSwap: 656 kB
present garbage: 43096 KiB
swapped garbage: 0 KiB
==== message+ 480 /usr/bin/dbus-daemon ====
RssAnon: 260 kB
VmSwap: 248 kB
present garbage: 40 KiB
swapped garbage: 0 KiB
==== ntp 19351 /usr/sbin/ntpd ====
RssAnon: 580 kB
VmSwap: 0 kB
present garbage: 24 KiB
swapped garbage: 0 KiB
==== postgres 638 /usr/lib/postgresql/9.6/bin/postgres ====
RssAnon: 248 kB
VmSwap: 1524 kB
present garbage: 0 KiB
swapped garbage: 20 KiB
==== postgres 694 postgres: ====
RssAnon: 196 kB
VmSwap: 1576 kB
present garbage: 0 KiB
swapped garbage: 0 KiB
==== postgres 697 postgres: ====
RssAnon: 228 kB
VmSwap: 1544 kB
present garbage: 0 KiB
swapped garbage: 16 KiB
==== postgres 699 postgres: ====
RssAnon: 184 kB
VmSwap: 1588 kB
present garbage: 0 KiB
swapped garbage: 4 KiB
==== postgres 700 postgres: ====
RssAnon: 284 kB
VmSwap: 1760 kB
present garbage: 16 KiB
swapped garbage: 0 KiB
==== postgres 701 postgres: ====
RssAnon: 252 kB
VmSwap: 1520 kB
present garbage: 0 KiB
swapped garbage: 0 KiB
==== root 11355 /usr/bin/atop ====
RssAnon: 4988 kB
VmSwap: 0 kB
present garbage: 1508 KiB
swapped garbage: 0 KiB
==== root 1338 /sbin/dhclient ====
RssAnon: 228 kB
VmSwap: 820 kB
present garbage: 12 KiB
swapped garbage: 0 KiB
==== root 1430 /sbin/dhclient ====
RssAnon: 224 kB
VmSwap: 836 kB
present garbage: 12 KiB
swapped garbage: 0 KiB
==== root 1 /sbin/init ====
RssAnon: 1504 kB
VmSwap: 392 kB
Message from syslogd@{machine} at Jul 29 21:58:44 ...
kernel:[7907110.612190] systemd[1]: segfault at 7ffcd83e1bef ip 00007ffcd83e1bef sp 00007ffcd83e1be0 error 15
Message from syslogd@{machine} at Jul 29 21:58:44 ...
kernel:[7907110.616936] systemd[1]: segfault at 7ffcd83e1bef ip 00007ffcd83e1bef sp 00007ffcd83e1be0 error 15
present garbage: 88 KiB
swapped garbage: 44 KiB
Message from syslogd@{machine} at Jul 29 21:58:44 ...
kernel:[7907110.617903] systemd[1]: segfault at 7ffcd83e1bef ip 00007ffcd83e1bef sp 00007ffcd83e1be0 error 15
Message from syslogd@{machine} at Jul 29 21:58:44 ...
kernel:[7907110.618937] systemd[1]: segfault at 7ffcd83e1bef ip 00007ffcd83e1bef sp 00007ffcd83e1be0 error 15
Message from syslogd@{machine} at Jul 29 21:58:44 ...
kernel:[7907110.619810] systemd[1]: segfault at 7ffcd83e1bef ip 00007ffcd83e1bef sp 00007ffcd83e1be0 error 15
==== root 224 /lib/systemd/systemd-journald ====
RssAnon: 248 kB
VmSwap: 284 kB
present garbage: 16 KiB
swapped garbage: 4 KiB
==== root 258 /sbin/lvmetad ====
RssAnon: 116 kB
VmSwap: 136 kB
present garbage: 8 KiB
swapped garbage: 0 KiB
==== root 261 /lib/systemd/systemd-udevd ====
RssAnon: 224 kB
VmSwap: 652 kB
present garbage: 4 KiB
swapped garbage: 364 KiB
==== root 458 /sbin/rpcbind ====
RssAnon: 260 kB
VmSwap: 308 kB
present garbage: 68 KiB
swapped garbage: 0 KiB
==== root 467 /usr/sbin/acpid ====
RssAnon: 0 kB
VmSwap: 92 kB
present garbage: 0 KiB
swapped garbage: 12 KiB
==== root 469 /usr/sbin/rsyslogd ====
RssAnon: 1156 kB
VmSwap: 476 kB
present garbage: 20 KiB
swapped garbage: 156 KiB
==== root 470 /usr/sbin/irqbalance ====
RssAnon: 156 kB
VmSwap: 216 kB
present garbage: 4 KiB
swapped garbage: 32 KiB
==== root 472 /lib/systemd/systemd-logind ====
RssAnon: 280 kB
VmSwap: 316 kB
present garbage: 60 KiB
swapped garbage: 12 KiB
==== root 478 /usr/sbin/cron ====
RssAnon: 132 kB
VmSwap: 160 kB
present garbage: 0 KiB
swapped garbage: 12 KiB
==== root 484 /usr/sbin/atopacctd ====
RssAnon: 68 kB
VmSwap: 44 kB
present garbage: 0 KiB
swapped garbage: 4 KiB
==== root 510 /usr/sbin/openvpn ====
RssAnon: 704 kB
VmSwap: 316 kB
present garbage: 64 KiB
swapped garbage: 0 KiB
==== root 790 /usr/sbin/libvirtd ====
RssAnon: 6648 kB
VmSwap: 6696 kB
present garbage: 3804 KiB
swapped garbage: 3856 KiB
==== root 800 /usr/sbin/sshd ====
RssAnon: 196 kB
VmSwap: 584 kB
present garbage: 0 KiB
swapped garbage: 12 KiB
==== root 803 /sbin/agetty ====
RssAnon: 24 kB
VmSwap: 108 kB
present garbage: 0 KiB
swapped garbage: 4 KiB
==== root 846 /usr/sbin/dhcpd ====
RssAnon: 644 kB
VmSwap: 9192 kB
present garbage: 8 KiB
swapped garbage: 0 KiB
==== root 959 /usr/sbin/virtlogd ====
RssAnon: 240 kB
VmSwap: 900 kB
present garbage: 12 KiB
swapped garbage: 8 KiB
# free
total used free shared buff/cache available
Mem: 8091692 1045112 276220 92336 6770360 6683552
Swap: 8300540 33968 8266572
#
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment