Skip to content

Commit 74079f1

Browse files
committed
chore(scripts): Add memdiff to nicely compare two memory dumps
1 parent 72b3cc0 commit 74079f1

File tree

1 file changed

+85
-0
lines changed

1 file changed

+85
-0
lines changed

scripts/memdiff.py

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#!/usr/bin/python3
2+
3+
import colorama
4+
import argparse
5+
from textwrap import dedent
6+
from colorama import init as colorama_init, Fore, Back, Style
7+
8+
9+
parser = argparse.ArgumentParser(
10+
description=dedent("""
11+
Colourful memory diff tool
12+
"""),
13+
epilog="(c) 2023 Marek Koza <qyx@krtko.org>",
14+
formatter_class=argparse.RawDescriptionHelpFormatter
15+
)
16+
17+
# Generic parameters
18+
parser.add_argument('a', type=str)
19+
parser.add_argument('b', type=str)
20+
parser.add_argument('-g', '--granularity', type=int, default=16, help='Granularity of comparison (the smallest chunk in the resulting graph)')
21+
parser.add_argument('-w', '--wide', type=int, default=1, help='Generate wide graph (2 or 4 squares)')
22+
parser.add_argument('--delimiter', type=str, default=' ')
23+
parser.add_argument('--space', type=str, default=' ')
24+
parser.add_argument('--hl', type=str, default=[], nargs='+')
25+
26+
27+
28+
args = parser.parse_args()
29+
highlights = []
30+
for hl in args.hl:
31+
(addr, size) = hl.split(':', 2)
32+
highlights.append((int(addr, 0), int(size, 0)))
33+
34+
colorama_init()
35+
36+
37+
def gen_diff(addr, size):
38+
nbytes = 0
39+
for i in range(size):
40+
if content_a[addr + i] != content_b[addr + i]:
41+
nbytes += 1
42+
43+
bg = ''
44+
for (hl_addr, hl_size) in highlights:
45+
if addr >= hl_addr and addr < (hl_addr + hl_size):
46+
bg = Back.LIGHTBLACK_EX
47+
48+
if nbytes == 0:
49+
return [f'{bg}{Fore.WHITE}.{Style.RESET_ALL}']
50+
elif (nbytes / size) <= 0.1:
51+
return [f'{bg}{Fore.BLUE}{Style.BRIGHT}o{Style.RESET_ALL}']
52+
elif (nbytes / size) <= 0.5:
53+
return [f'{bg}{Fore.GREEN}{Style.BRIGHT}o{Style.RESET_ALL}']
54+
elif (nbytes / size) < 1:
55+
return [f'{bg}{Fore.YELLOW}{Style.BRIGHT}o{Style.RESET_ALL}']
56+
else:
57+
return [f'{bg}{Fore.RED}{Style.BRIGHT}o{Style.RESET_ALL}']
58+
59+
60+
def gen_split(addr, size, minsize, wide):
61+
if size <= minsize:
62+
r = gen_diff(addr, size)
63+
else:
64+
if wide == 2:
65+
size //= 2
66+
r = [a + args.delimiter + b for a, b in zip(gen_split(addr, size, minsize, 1), gen_split(addr + size, size, minsize, 1))]
67+
elif wide == 4:
68+
size //= 4
69+
r1 = [a + args.delimiter + b for a, b in zip(gen_split(addr, size, minsize, 1), gen_split(addr + size, size, minsize, 1))]
70+
r2 = [a + args.delimiter + b for a, b in zip(gen_split(addr + size * 2, size, minsize, 1), gen_split(addr + size * 3, size, minsize, 1))]
71+
r = [a + args.delimiter + b for a, b in zip(r1, r2)]
72+
else:
73+
size //= 4
74+
r = [a + args.space + b for a, b in zip(gen_split(addr, size, minsize, 1), gen_split(addr + size, size, minsize, 1))]
75+
r += [a + args.space + b for a, b in zip(gen_split(addr + size * 2, size, minsize, 1), gen_split(addr + size * 3, size, minsize, 1))]
76+
77+
return r
78+
79+
80+
content_a = open(args.a, 'rb').read()
81+
content_b = open(args.b, 'rb').read()
82+
83+
for l in gen_split(0, len(content_a), args.granularity, args.wide):
84+
print(l)
85+

0 commit comments

Comments
 (0)