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 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()