mapuche 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mapuche/__init__.py +0 -0
- mapuche/map_parse.py +196 -0
- mapuche/mapuche.py +36 -0
- mapuche/tree_table.py +2742 -0
- mapuche-0.0.1.dist-info/LICENSE +20 -0
- mapuche-0.0.1.dist-info/METADATA +53 -0
- mapuche-0.0.1.dist-info/RECORD +10 -0
- mapuche-0.0.1.dist-info/WHEEL +5 -0
- mapuche-0.0.1.dist-info/entry_points.txt +2 -0
- mapuche-0.0.1.dist-info/top_level.txt +1 -0
mapuche/__init__.py
ADDED
|
File without changes
|
mapuche/map_parse.py
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
INFO = "*info*" # TODO remove
|
|
6
|
+
|
|
7
|
+
sections = {}
|
|
8
|
+
sections_diff = {}
|
|
9
|
+
|
|
10
|
+
def pretty_print_dict(d):
|
|
11
|
+
# print pretty json
|
|
12
|
+
import json
|
|
13
|
+
print(json.dumps(d, indent=4))
|
|
14
|
+
|
|
15
|
+
def parse_lib_and_objfile(source):
|
|
16
|
+
if source == None:
|
|
17
|
+
return (None, None)
|
|
18
|
+
objfile = None
|
|
19
|
+
|
|
20
|
+
lib_end = source.rfind('(')
|
|
21
|
+
if lib_end > 0:
|
|
22
|
+
objfile = source[lib_end + 1:-1]
|
|
23
|
+
else:
|
|
24
|
+
lib_end = len(source)
|
|
25
|
+
lib_start = source.rfind('/') + 1
|
|
26
|
+
lib = source[lib_start:lib_end]
|
|
27
|
+
return (lib, objfile)
|
|
28
|
+
|
|
29
|
+
def parse_mapfile_entry(f, line):
|
|
30
|
+
split = line.split()
|
|
31
|
+
name = split[0]
|
|
32
|
+
# remove the first element from split
|
|
33
|
+
split.pop(0)
|
|
34
|
+
# check if one word in line
|
|
35
|
+
if len(split) == 0:
|
|
36
|
+
# read new line and print
|
|
37
|
+
line = f.readline()
|
|
38
|
+
split = line.split()
|
|
39
|
+
|
|
40
|
+
if len(split) == 1:
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
source = split[2] if len(split) == 3 else None
|
|
44
|
+
|
|
45
|
+
return {'name': name, 'address': split[0], 'size': int(split[1], 16), 'source': source}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def fill_all_fields_with_info(data):
|
|
49
|
+
size = 0
|
|
50
|
+
for k in data['entries']:
|
|
51
|
+
size += fill_all_fields_with_info(data['entries'][k])
|
|
52
|
+
|
|
53
|
+
if data['size'] == None:
|
|
54
|
+
data['size'] = size
|
|
55
|
+
return data['size']
|
|
56
|
+
|
|
57
|
+
def convert_to_textual_rows(json_data, is_diff=False, add_header=True):
|
|
58
|
+
rows = []
|
|
59
|
+
if add_header:
|
|
60
|
+
if is_diff:
|
|
61
|
+
rows += [tuple(['name', 'address', 'size', 'diff', 'delta'])]
|
|
62
|
+
else:
|
|
63
|
+
rows += [tuple(['name', 'address', 'size'])]
|
|
64
|
+
for k, v in json_data['entries'].items():
|
|
65
|
+
if is_diff:
|
|
66
|
+
rows.append(tuple([v['name'], v['address'], v['size'], v['diff'], v['delta']]))
|
|
67
|
+
else:
|
|
68
|
+
rows.append(tuple([v['name'], v['address'], v['size']]))
|
|
69
|
+
if len(v['entries']) == 0:
|
|
70
|
+
continue
|
|
71
|
+
rows += tuple([convert_to_textual_rows(v, is_diff, False)])
|
|
72
|
+
return rows
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def add_entry_to_entries(entries, name, entry=None):
|
|
76
|
+
if name in entries:
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
if entry != None:
|
|
80
|
+
entries[name] = entry
|
|
81
|
+
else:
|
|
82
|
+
entries[name] = {'name': name, 'address': None, 'size': None, 'source': None}
|
|
83
|
+
entries[name]['entries'] = {}
|
|
84
|
+
|
|
85
|
+
def _parse_map_file(map_file):
|
|
86
|
+
# open file in argv[0] and read until string "Linker script and memory map"
|
|
87
|
+
# array of sections. Contains section name, address and size
|
|
88
|
+
sections = {}
|
|
89
|
+
parse = False
|
|
90
|
+
root_entry_name = ''
|
|
91
|
+
with open(map_file, 'r') as f:
|
|
92
|
+
while True:
|
|
93
|
+
line = f.readline()
|
|
94
|
+
if not line:
|
|
95
|
+
break
|
|
96
|
+
if not parse:
|
|
97
|
+
if "Linker script and memory map" in line:
|
|
98
|
+
parse = True
|
|
99
|
+
continue
|
|
100
|
+
if "Cross Reference Table" in line:
|
|
101
|
+
break
|
|
102
|
+
|
|
103
|
+
if line.startswith('.'):
|
|
104
|
+
is_root_entry = True
|
|
105
|
+
elif line.startswith(' .'):
|
|
106
|
+
is_root_entry = False
|
|
107
|
+
else:
|
|
108
|
+
continue
|
|
109
|
+
|
|
110
|
+
map_entry = parse_mapfile_entry(f, line)
|
|
111
|
+
|
|
112
|
+
if map_entry is None:
|
|
113
|
+
continue
|
|
114
|
+
|
|
115
|
+
if is_root_entry:
|
|
116
|
+
root_entry_name = map_entry['name']
|
|
117
|
+
add_entry_to_entries(sections, root_entry_name, map_entry)
|
|
118
|
+
else:
|
|
119
|
+
# append last sections element with entry
|
|
120
|
+
lib, obj_file = parse_lib_and_objfile(map_entry['source'])
|
|
121
|
+
add_entry_to_entries(sections[root_entry_name]['entries'], lib)
|
|
122
|
+
add_entry_to_entries(sections[root_entry_name]['entries'][lib]['entries'], obj_file)
|
|
123
|
+
add_entry_to_entries(sections[root_entry_name]['entries'][lib]['entries'][obj_file]['entries'], map_entry['name'], map_entry)
|
|
124
|
+
sections = {'name': 'total', 'size': 0, 'entries': sections}
|
|
125
|
+
fill_all_fields_with_info(sections)
|
|
126
|
+
return sections
|
|
127
|
+
|
|
128
|
+
def parse_map_file(map_file):
|
|
129
|
+
p = _parse_map_file(map_file)
|
|
130
|
+
return convert_to_textual_rows(p, False)
|
|
131
|
+
|
|
132
|
+
def get_all_entries_keys(d1, d2):
|
|
133
|
+
result = []
|
|
134
|
+
if not d1 is None:
|
|
135
|
+
result += d1['entries'].keys()
|
|
136
|
+
if not d2 is None:
|
|
137
|
+
result += d2['entries'].keys()
|
|
138
|
+
return list(dict.fromkeys(result))
|
|
139
|
+
|
|
140
|
+
def _collect_diff(diff_entries, leaf, m1, m2):
|
|
141
|
+
has_diff = False
|
|
142
|
+
|
|
143
|
+
diff_entries[leaf] = {'name': leaf, 'address': None, 'size': 0, 'diff': 0, 'delta': 0, 'source': None, 'entries': {}}
|
|
144
|
+
|
|
145
|
+
for k in get_all_entries_keys(m1, m2):
|
|
146
|
+
if m2 == None or not k in m2['entries']:
|
|
147
|
+
has_diff |= _collect_diff(diff_entries[leaf]['entries'], k, m1['entries'][k], None)
|
|
148
|
+
continue
|
|
149
|
+
|
|
150
|
+
if m1 == None or not k in m1['entries']:
|
|
151
|
+
has_diff |= _collect_diff(diff_entries[leaf]['entries'], k, None, m2['entries'][k])
|
|
152
|
+
continue
|
|
153
|
+
|
|
154
|
+
has_diff |= _collect_diff(diff_entries[leaf]['entries'], k, m1['entries'][k], m2['entries'][k])
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
s1 = 0 if m1 == None else m1['size']
|
|
158
|
+
s2 = 0 if m2 == None else m2['size']
|
|
159
|
+
has_diff |= s1 != s2
|
|
160
|
+
|
|
161
|
+
if not has_diff:
|
|
162
|
+
del diff_entries[leaf]
|
|
163
|
+
return has_diff
|
|
164
|
+
|
|
165
|
+
reference = m1
|
|
166
|
+
if m1 is None:
|
|
167
|
+
reference = m2
|
|
168
|
+
|
|
169
|
+
diff_entries[leaf]['name'] = reference['name']
|
|
170
|
+
diff_entries[leaf]['size'] = s1
|
|
171
|
+
diff_entries[leaf]['address'] = reference['address'] if 'address' in reference else None
|
|
172
|
+
diff_entries[leaf]['source'] = reference['source'] if 'source' in reference else None
|
|
173
|
+
diff_entries[leaf]['diff'] = s1 - s2
|
|
174
|
+
diff_entries[leaf]['delta'] = 100 if s2 == 0 else round((s1 - s2 ) / s2 * 100, 1)
|
|
175
|
+
|
|
176
|
+
return has_diff
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def generate_diff_table(map_1, map_2):
|
|
180
|
+
m1 = _parse_map_file(map_1)
|
|
181
|
+
m2 = _parse_map_file(map_2)
|
|
182
|
+
|
|
183
|
+
_collect_diff(sections_diff, 'total', m1, m2)
|
|
184
|
+
|
|
185
|
+
return convert_to_textual_rows(sections_diff['total'], True)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
if __name__ == "__main__":
|
|
189
|
+
if len(sys.argv) == 2:
|
|
190
|
+
rows_file = parse_map_file(sys.argv[1])
|
|
191
|
+
print(rows_file)
|
|
192
|
+
elif len(sys.argv) == 3:
|
|
193
|
+
rows_diff = generate_diff_table(sys.argv[1], sys.argv[2])
|
|
194
|
+
else:
|
|
195
|
+
print("Wrong number of arguments. Please provide one or two files.")
|
|
196
|
+
exit()
|
mapuche/mapuche.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
from itertools import cycle
|
|
4
|
+
from os import sys
|
|
5
|
+
from textual.app import App, ComposeResult
|
|
6
|
+
from .tree_table import DataTable
|
|
7
|
+
from .map_parse import parse_map_file, generate_diff_table
|
|
8
|
+
|
|
9
|
+
class TableApp(App):
|
|
10
|
+
def compose(self) -> ComposeResult:
|
|
11
|
+
yield DataTable()
|
|
12
|
+
|
|
13
|
+
def on_mount(self) -> None:
|
|
14
|
+
table = self.query_one(DataTable)
|
|
15
|
+
table.cursor_type = next(cycle(["row"]))
|
|
16
|
+
table.zebra_stripes = True
|
|
17
|
+
if len(sys.argv) == 2:
|
|
18
|
+
rows = parse_map_file(sys.argv[1])
|
|
19
|
+
elif len(sys.argv) == 3:
|
|
20
|
+
rows = generate_diff_table(sys.argv[1], sys.argv[2])
|
|
21
|
+
table.add_columns(*rows[0])
|
|
22
|
+
table.add_rows(rows[1:])
|
|
23
|
+
|
|
24
|
+
def key_c(self):
|
|
25
|
+
table = self.query_one(DataTable)
|
|
26
|
+
table.cursor_type = next(cursors)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def main():
|
|
30
|
+
if len(sys.argv) != 2 and len(sys.argv) != 3:
|
|
31
|
+
print("Wrong parameters count!")
|
|
32
|
+
app = TableApp()
|
|
33
|
+
app.run()
|
|
34
|
+
|
|
35
|
+
if __name__ == "__main__":
|
|
36
|
+
main()
|