forked from zeldaret/tmc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
progress.py
executable file
·153 lines (122 loc) · 4.8 KB
/
progress.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#!/usr/bin/env python3
import argparse
import json
import git
import os
import re
def collect_non_matching_funcs():
result = []
for root, dirs, files in os.walk('src'):
for file in files:
if file.endswith('.c'):
with open(os.path.join(root, file), 'r') as f:
data = f.read()
# Find all NONMATCH and ASM_FUNC macros
for match in re.findall(r'(NONMATCH|ASM_FUNC)\(".*",\W*\w*\W*(\w*).*\)', data):
result.append(match)
return result
def parse_map(non_matching_funcs):
src = 0
asm = 0
src_data = 0
data = 0
non_matching = 0
with open('tmc.map', 'r') as map:
# Skip to the linker script section
line = map.readline()
while not line.startswith('Linker script and memory map'):
line = map.readline()
while not line.startswith('rom'):
line = map.readline()
prev_symbol = None
prev_addr = 0
for line in map:
if line.startswith(' .'):
arr = line.split()
section = arr[0]
size = int(arr[2], 16)
filepath = arr[3]
dir = filepath.split('/')[0]
if section == '.text':
if dir == 'src':
src += size
elif dir == 'asm':
if filepath.find("asm/src/") != -1 or filepath.find("asm/lib/") != -1:
src += size
else:
asm += size
elif dir == 'data':
# scripts
src_data += size
elif dir == '..':
# libc
src += size
elif section == '.rodata':
if dir == 'src':
src_data += size
elif dir == 'data':
data += size
elif line.startswith(' '):
arr = line.split()
if len(arr) == 2 and arr[1] != '': # It is actually a symbol
if prev_symbol in non_matching_funcs:
# Calculate the length for non matching function
non_matching += int(arr[0], 16) - prev_addr
prev_symbol = arr[1]
prev_addr = int(arr[0], 16)
elif line.strip() == '':
# End of linker script section
break
src -= non_matching
asm += non_matching
return (src, asm, src_data, data)
def main():
parser = argparse.ArgumentParser()
parser = argparse.ArgumentParser(description="Computes current progress throughout the whole project.")
parser.add_argument("format", nargs="?", default="text", choices=["text", "csv", "shield-json"])
parser.add_argument("-m", "--matching", dest='matching', action='store_true',
help="Output matching progress instead of decompilation progress")
args = parser.parse_args()
matching = args.matching
non_matching_funcs = []
funcs = collect_non_matching_funcs()
if matching:
# Remove all non matching funcs from count
non_matching_funcs = [x[1] for x in funcs]
else:
# Only remove ASM_FUNC functions from count
for func in funcs:
if func[0] == 'ASM_FUNC':
non_matching_funcs.append(func[1])
(src, asm, src_data, data) = parse_map(non_matching_funcs)
total = src + asm
data_total = src_data + data
src_percent = 100 * src / total
asm_percent = 100 * asm / total
src_data_percent = 100 * src_data / data_total
data_percent = 100 * data / data_total
if args.format == 'csv':
version = 2
git_object = git.Repo().head.object
timestamp = str(git_object.committed_date)
git_hash = git_object.hexsha
csv_list = [str(version), timestamp, git_hash, str(src),
str(total), str(src_data), str(data_total)]
print(','.join(csv_list))
elif args.format == 'shield-json':
# https://shields.io/endpoint
print(json.dumps({
"schemaVersion": 1,
"label": "progress",
"message": f"{src_percent:.3g}%",
"color": 'yellow',
}))
elif args.format == 'text':
adjective = "decompiled" if not args.matching else "matched"
print("src: {:>9} / {:>8} total bytes {:<10} {:>9.4f}%".format(src, total, adjective, round(src_percent, 4)))
# print()
print("data: {:>9} / {:>8} total bytes analysed {:>9.4f}%".format(src_data, data_total, round(src_data_percent, 4)))
else:
print("Unknown format argument: " + args.format)
if __name__ == '__main__':
main()