Created
July 29, 2018 20:11
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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