1#!/usr/bin/env python3 2# coding=utf-8 3# Copyright (c) 2020 HiSilicon (Shanghai) Technologies CO., LIMITED. 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16import os 17import re 18import sys 19from collections import defaultdict 20from pathlib import Path 21 22 23def raise_string_exit(raise_string): 24 # raise Exception(raise_string) 25 sys.exit(0) 26 27 28def get_section_state(lines_split_set, name): 29 """ 30 name: 要判断section状态的 段名 31 g_state_lst: 需要判断的字符的列表 32 state: 二进制位表示, bit 0 表示load bit 1 表示debug 33 none -> 00 34 load -> 01 35 debug -> 10 36 laod + debug -> 11 37 """ 38 state = 0 39 default_debug_lst = ['.comment', '.riscv.attributes'] # 默认debug的列表 40 # 在debug默认列表中直接返回 10 41 if name in default_debug_lst: 42 return state | 1 << 1 43 lines_split_set = set(lines_split_set) 44 for i, st in enumerate(g_state_lst): 45 if st in lines_split_set: 46 state |= 1 << i 47 return state 48 49 50def conver_to_utf8(input_file, output_file): 51 """ 52 判断文件编码是否是uft-8, 否则将文件转化utf-8重新保存 53 """ 54 try: 55 with open(input_file, 'r', encoding='utf-8') as f: 56 f.read() 57 except: 58 try: 59 with open(input_file, 'rb') as infile: 60 raw_data = infile.read() 61 decode_txt = raw_data.decode(errors='replace') 62 except Exception as e: 63 print(f"无法解析读取的文件或编码: {e}") 64 try: 65 with open(output_file, 'w', encoding='utf-8') as outfile: 66 outfile.write(decode_txt) 67 except Exception as e: 68 print(f"无法保存文件: {e}") 69 70 71def parse_lst_file(lst_file): 72 conver_to_utf8(lst_file, lst_file) 73 record_flag = 0 74 section_name_dict = {} 75 record_line = ['Idx', 'Name', 'Size', 'VMA', 'LMA', 'File', 'off', 'Algn'] 76 with open(lst_file, 'r', encoding='utf-8') as f: 77 lines = f.readlines() 78 i = 0 79 while i < len(lines): 80 # 字符串以 "Sections:" 开始 且 下一行等于 record_line 则开始记录 81 if lines[i].startswith("Sections:"): 82 if lines[i + 1].split()[:3] == record_line[:3]: 83 record_flag = 1 84 i += 2 85 continue 86 else: 87 raise_string_exit(f"Not found {record_line}") 88 if record_flag: 89 name = lines[i].split()[1] 90 section_state = get_section_state(lines[i + 1].split(), name) 91 section_name_dict[name] = section_state 92 i += 2 93 # 不是以空格 开头 则Sections 解析完毕 跳出 94 if not lines[i].startswith(' '): 95 break 96 else: 97 i += 1 98 return section_name_dict 99 100 101def get_mem_address_len_lst_append_idx(name): 102 # ram, flash, rom 关键字特性列表 103 ram_feat_lst = [ 104 'CT_SRAM', 'PRESERVE_SHARE_MEM', 'EM_MUX_RAM', 'ITCM', 'DTCM', 'SRAM', 'APP_ITCM', 'APP_DTCM', 105 'CPUTRACE_RAM' 106 ] 107 flash_feat_lst = ['CT_FLASH', 'PROGRAM_STARTUP', 'PROGRAM', 'FLASH_STARTUP', 'FLASH_PROGRAM'] 108 rom_feat_lst = ['CT_ROM', 'ROM', 'ACORE_ROM'] 109 # 返回 get_mem_address_len_lst 中的行索引 110 if any(feat in name for feat in ram_feat_lst): 111 return 0 112 elif any(feat in name for feat in flash_feat_lst): 113 return 1 114 elif any(feat in name for feat in rom_feat_lst): 115 return 2 116 else: 117 raise_string_exit(f"Unknown not in ['RAM', 'FLASH', 'ROM'] -- {name}") 118 119 120def get_space_type_state(section_name, start_address, mem_address_range_lst): #### 函数名 根据地址查找占用空间类型 121 """ 122 start_address: 段名 开始地址 123 state: 124 none -> 000 125 ram -> 001 126 flash -> 010 127 rom -> 100 128 """ 129 state = 0 130 for j, lst in enumerate(mem_address_range_lst): 131 for start, end, _ in lst: 132 if start <= start_address < end: 133 state |= 1 << j 134 # state & 1 表示是否在ram中, 且 含有 LOAD 则添加 flash状态 135 if (state & 1) and (lst_info_dict[section_name] >> g_state_lst.index('LOAD,')) & 1: 136 state |= 1 << 1 137 return state 138 139 140def state2space_type(state): 141 """ 142 占用空间类型状态码 转 字符描述 143 """ 144 space_type = [] 145 space_type_lst = ['RAM', 'FLASH', 'ROM'] # 占用空间类型列表 146 for i, x in enumerate(space_type_lst): 147 if state >> i & 1: 148 space_type.append(x) 149 return ' + '.join(space_type) 150 151 152def parse_section_info(lines, i, debug_flag, mem_address_range_lst): 153 # 解析段名行的信息 154 line_info_lst = lines[i].split() 155 if len(line_info_lst) == 1: 156 # 段名下为空的跳出 不解析 157 if not lines[i + 1].strip(): 158 return "continue", debug_flag 159 # 存在段名太长 信息换行的情况 160 line_info_lst += lines[i + 1].split() 161 # 段名下不包含长度信息 不解析 162 if len(line_info_lst) < 3: 163 return "continue", debug_flag 164 # 段名 165 section_name = line_info_lst[0] 166 if section_name in lst_info_dict: 167 # 如果段名是debugging 跳过不处理 168 if (lst_info_dict[section_name] >> g_state_lst.index('DEBUGGING,')) & 1: 169 debug_flag = 1 170 return "continue", debug_flag 171 else: 172 debug_flag = 0 173 else: 174 # 不在 lst_info_dict 里 且 段名长度为 0, 不处理 175 if int(line_info_lst[2], 16) == 0: 176 debug_flag = 1 177 return "continue", debug_flag 178 else: 179 raise_string_exit(f"Not found {section_name} in lst_info_dict -- {i}") 180 # 起始地址 181 start_address = int(line_info_lst[1], 16) 182 # 段长度 183 section_len = int(line_info_lst[2], 16) 184 state = get_space_type_state(section_name, start_address, mem_address_range_lst) 185 if state & 1 and (state >> 1) & 1: 186 # 用于判断 ram+flash情况下 flash地址与ram地址的差值 187 flash_start_address = int(line_info_lst[-1], 16) 188 diff_address = flash_start_address - start_address 189 else: 190 diff_address = 0 191 # 键名 192 map_info_key = (section_name, start_address, section_len, state) 193 return map_info_key, diff_address, debug_flag 194 195 196def parse_symb_info(lines, i, map_info_key, diff_address, symb_info): 197 """ 198 解析符号信息 199 """ 200 pattern1 = r'\((.*?)\)' # 寻找库名(o_name)正则匹配 201 pattern2 = r'(\w+\.a)' # 寻找文件名(a_name)正则匹配 202 o_name_lst = ('S.obj)', 's.obj)', '.o)', 'c.obj)') # 库名的后缀匹配 203 special_fill_threshold = 24 # fill地址内存的阈值,小于等于24字节的字段被认为是自动对齐的填充字段,不需要解释 204 symbol_type_lst = ('.text', '.rodata', '.data', '.bss') # 符号类型的元组,不在里面则为 .other 205 line_info_lst = lines[i].split() 206 if len(line_info_lst) == 1: 207 if lines[i + 1].strip().endswith(o_name_lst): 208 line_info_lst += lines[i + 1].split() 209 if line_info_lst[0] == '*fill*': 210 symb_name = line_info_lst[0] 211 symb_start_address = int(line_info_lst[1], 16) 212 symb_len = int(line_info_lst[2], 16) 213 symb_end_address = symb_start_address + symb_len 214 # fill 大小大于 special_fill_threshold 赋值段名 215 if symb_len > special_fill_threshold: 216 a_name = o_name = map_info_key[0] 217 else: 218 a_name, o_name = symb_info[-2], symb_info[-1] 219 symb_info = [symb_start_address, symb_end_address, symb_len, symb_name, '.fill', a_name, o_name] 220 elif not line_info_lst[-1].endswith(o_name_lst): 221 raise_string_exit(f"Not found {o_name_lst} -- {i}") 222 else: 223 symb_name = line_info_lst[0] 224 symb_start_address = int(line_info_lst[1], 16) 225 symb_len = int(line_info_lst[2], 16) 226 symb_end_address = symb_start_address + symb_len 227 symb_type = '.other' 228 for st in symbol_type_lst: 229 if symb_name.startswith(st) or symb_name.endswith(st): 230 symb_type = st 231 break 232 o_name = re.findall(pattern1, line_info_lst[-1])[-1] 233 a_name = re.findall(pattern2, line_info_lst[-1])[0] 234 symb_info = [symb_start_address, symb_end_address, symb_len, symb_name, symb_type, a_name, o_name] 235 # 根据 state 把 symb_info 加入ram_dict, g_flash_dict, g_rom_dict 236 if map_info_key[-1] & 1: 237 if symb_info[2] != 0: 238 g_ram_dict[map_info_key].append(symb_info) 239 if (map_info_key[-1] >> 1) & 1: 240 # 加入flash_dict 的起始地址需要加 diff_address 241 # ram+flash情况下 diff_address!=0 242 # 单flash 情况下 diff_address=0 243 map_info_key_lst = list(map_info_key) 244 map_info_key_lst[1] += diff_address 245 tmp_key = tuple(map_info_key_lst) 246 tmp_symb_info = symb_info.copy() 247 tmp_symb_info[0] += diff_address 248 tmp_symb_info[1] += diff_address 249 if tmp_symb_info[2] != 0: 250 g_flash_dict[tmp_key].append(tmp_symb_info) 251 if (map_info_key[-1] >> 2) & 1: 252 if symb_info[2] != 0: 253 g_rom_dict[map_info_key].append(symb_info) 254 return symb_info 255 256 257def sort_data_dict(data_dict): 258 """ 259 data_dict 按键值的 起始地址大小 从小到大排序 260 """ 261 data_dict = dict(sorted(data_dict.items(), key=lambda x: x[0][1])) 262 return data_dict 263 264 265def check_is_overlap(data_dict): 266 """ 267 检查同一个段名下的 地址是否重叠 首地址 a 尾地址 b 268 a-b<0 -> 调整 269 a=b -> 跳过 270 a-b>0 -> 报错 271 """ 272 for k, lst in data_dict.items(): 273 for i in range(1, len(lst)): 274 a, b = lst[i][0], lst[i - 1][1] 275 if a - b < 0: 276 lst[i][0] = b 277 lst[i][2] = lst[i][1] - b 278 lst[i].append('调整!') 279 elif a - b > 0: 280 raise_string_exit("There is a gap between the start address and the end address.") 281 return data_dict 282 283 284def get_tab_len_lst(data_dict): 285 """ 286 获得 段与段之间的 段长度差值 287 """ 288 tab_len_lst = [] 289 for i, k in enumerate(data_dict.keys()): 290 if i == 0: 291 tab_len = [k[0], k[1], k[2], 0] 292 else: 293 tab_len = [k[0], k[1], k[2], k[1] - end_address] 294 end_address = k[1] + k[2] 295 tab_len_lst.append(tab_len) 296 return tab_len_lst 297 298 299def save_dict_csv(data_dict, file_name): 300 """ 301 data_dict: 要保存的数据, 保存为csv 302 file_name: 保存csv的文件名 file_name.csv 303 """ 304 # data_dict 按键值的 起始地址大小 从小到大排序 305 data_dict = sort_data_dict(data_dict) 306 # 检查同一个段名下的 地址是否重叠 307 data_dict = check_is_overlap(data_dict) 308 with open(OUTPUT_DIR / f'{file_name}.csv', 'w', encoding='utf_8_sig') as f: 309 # data_dict的表头名 310 header = ['起始地址', '结束地址', '符号大小', '符号名', '符号类型', '库名', '文件名', '段名', '段起始地址', '段大小', '占用空间类型', ''] 311 f.write(','.join(header) + '\n') 312 for i, (k, info_lst) in enumerate(data_dict.items()): 313 k = list(k) 314 k[-1] = state2space_type(k[-1]) 315 k = ','.join(map(str, k)) 316 for info in info_lst: 317 if info[-1] == '调整!': 318 f.write(f"{','.join(map(str, info[:-1]))},{k},调整!\n") 319 else: 320 f.write(f"{','.join(map(str, info))},{k},\n") 321 # 获得 段与段之间的 段长度差值 322 tab_len_lst = get_tab_len_lst(data_dict) 323 with open(OUTPUT_DIR / f'{file_name}_tab_len.csv', 'w', encoding='utf_8_sig') as f: 324 header = ['段名', '段起始地址', '段大小', 'Tab_Len'] 325 f.write(','.join(header) + '\n') 326 for lst in tab_len_lst: 327 f.write(','.join(map(str, lst)) + '\n') 328 329 330def parse_map_file(map_file): 331 with open(map_file, 'r', encoding='utf-8') as f: 332 lines = f.readlines() 333 # [symb_start_address, symb_end_address, symb_len, symb_name, symb_type, a_name, o_name] 334 symb_info = [] 335 # 初始化 flag 336 mem_record_flag = link_flag = debug_flag = link_if_flag = 0 337 ## mem_if_flag 是否 判断 字符串以Memory Configuration开头 338 mem_if_flag = 1 339 mem_conf_flag_lst = ['Name', 'Origin', 'Length', 'Attributes'] 340 # 记录地址范围的列表,用于判断是否 ram rom flash, 下标0: ram, 1: flash, 2: rom 341 mem_address_range_lst = [[] for _ in range(3)] 342 i = 0 343 while i < len(lines): 344 if not lines[i].strip(): 345 i += 1 346 continue 347 # 开始解析 Memory Configuration 348 if mem_if_flag and lines[i].startswith("Memory Configuration"): 349 if lines[i + 2].split() == mem_conf_flag_lst: 350 mem_record_flag = 1 351 mem_if_flag = 0 352 i += 3 353 continue 354 else: 355 raise_string_exit(f"Not found {mem_conf_flag_lst} -- {i}") 356 if mem_record_flag: 357 # 不记录 *default* 358 if lines[i].startswith('*default*'): 359 link_if_flag = 1 360 mem_record_flag = 0 361 i += 2 362 continue 363 elif lines[i].startswith('Linker'): 364 raise_string_exit("Not found *default*") 365 mem_lst = lines[i].split() 366 # mem_info = (起始地址, 结束地址, 地址长度) 367 mem_info = (int(mem_lst[1], 16), int(mem_lst[1], 16) + int(mem_lst[2], 16), int(mem_lst[2], 16)) 368 idx = get_mem_address_len_lst_append_idx(mem_lst[0]) 369 mem_address_range_lst[idx].append(mem_info) 370 # 开始解析 Linker script and memory map 下的信息 371 if link_if_flag: 372 if lines[i].startswith("Linker script and memory map"): 373 link_flag = 1 374 link_if_flag = 0 375 i += 1 376 continue 377 else: 378 raise_string_exit("Not found Linker script and memory map") 379 if link_flag: 380 # 解析段名行的信息 381 if any(lines[i].startswith(key) for key in lst_info_dict.keys()) or (lines[i].startswith('.') and 382 lines[i][1] != '.'): 383 tmp_info = parse_section_info(lines, i, debug_flag, mem_address_range_lst) 384 if tmp_info[0] == "continue": 385 debug_flag = tmp_info[1] 386 i += 1 387 continue 388 else: 389 map_info_key, diff_address, debug_flag = tmp_info 390 # 解析段名下的符号信息 391 elif debug_flag == 0 and lines[i].startswith((' .', ' *fill')): 392 # 解析符号信息 393 symb_info = parse_symb_info(lines, i, map_info_key, diff_address, symb_info) 394 i += 1 395 save_dict_csv(g_ram_dict, f"{map_file.name}_ram_dict") 396 save_dict_csv(g_flash_dict, f"{map_file.name}_flash_dict") 397 save_dict_csv(g_rom_dict, f"{map_file.name}_rom_dict") 398 399 400def get_group_owner(data_dict, save_name): 401 a_name2resp_group_dict = dict() # {a_name: PLAT WIFI BGLE} 402 with open(OWNER_DIR / 'library_owner.csv', 'r', encoding='utf-8') as f: 403 header = f.readline() 404 lines = f.readlines() 405 for line in lines: 406 a_name, group = line.strip().split(',') 407 a_name2resp_group_dict[a_name] = group 408 409 c_name2wifi_group_dict = dict() # {c_name: 应用组 算法组 协议组 前端组 系统组} 410 with open(OWNER_DIR / 'wifi_owner.csv', 'r', encoding='utf-8') as f: 411 header = f.readline() 412 lines = f.readlines() 413 for line in lines: 414 c_name, group = line.strip().split(',') 415 c_name2wifi_group_dict[c_name] = group 416 # a_name : [sum_symb_len, group_type] ---group_type: PLAT WIFI BGLE UNKNOWN 417 file_stat_dict = defaultdict(lambda: [0, 'UNKNOWN']) 418 # {c_name : {a_name : [sum_symb_len, group_type, wifi_group_type]}} 419 wifi_stat_dict = defaultdict(lambda: defaultdict(lambda: [0, 'UNKNOWN', ''])) 420 for symb_info_list in data_dict.values(): 421 for symb_info in symb_info_list: 422 symb_len, a_name, c_name = symb_info[2], symb_info[5], symb_info[6] 423 # 去除后缀 .obj 但 .heap .stack .shere_mem 跳过 424 if not c_name.startswith('.'): 425 c_name = c_name.split('.')[0] + '.c' # 426 if a_name in a_name2resp_group_dict: 427 file_stat_dict[a_name][1] = a_name2resp_group_dict[a_name] 428 wifi_stat_dict[c_name][a_name][1] = a_name2resp_group_dict[a_name] 429 if a_name2resp_group_dict[a_name] == 'WIFI': 430 if c_name in c_name2wifi_group_dict: 431 wifi_stat_dict[c_name][a_name][2] = c_name2wifi_group_dict[c_name] 432 else: 433 wifi_stat_dict[c_name][a_name][2] = 'WIFI_UNKNOWN' 434 file_stat_dict[a_name][0] += symb_len 435 wifi_stat_dict[c_name][a_name][0] += symb_len 436 # PLAT WIFI BGLE 按 库名 角度统计 大小 437 with open(OUTPUT_DIR / f"{save_name}_library.csv", 'w', encoding='utf_8_sig') as f: 438 header = ['库名', '大小', '负责组'] 439 f.write(f"{','.join(header)}\n") 440 group_dict = defaultdict(int) # 用于下面的 负责组的角度统计 地址大小 441 for a_name, info in file_stat_dict.items(): 442 f.write(f"{a_name},{info[0]},{info[1]}\n") 443 group_dict[info[1]] += info[0] 444 445 # 从负责组的角度统计 地址大小 446 with open(OUTPUT_DIR / f"{save_name}_library_summary.csv", 'w', encoding='utf_8_sig') as f: 447 header = ['Group_Owner', 'Size', 'Limit'] 448 f.write(f"{','.join(header)}\n") 449 # 各组规格限制 PLAT WIFI BTC BTH UNKNOWN STACK PKTRAM 450 lim_all = {'ws63-liteos-app.map_ram': {'PLAT' : 35.50, 'WIFI':46.5, 'RADAR':49, 'BTC': 20, 'BTH': 10, 'UNKNOWN': 0.5, 'STACK':7,'PKTRAM':50}, 451 'control_ws53.map_ram':{'PLAT' : 16.68, 'WIFI':18.64, 'BTC': 25, 'BTH': 10, 'UNKNOWN': 25, 'STACK':6,'HEAP':27}, 452 'control_ws53.map_flash':{'PLAT' : 15, 'WIFI':46.5, 'BTC': 109, 'BTH': 6, 'UNKNOWN': 35}, 453 'liteos_ws53_light.map_flash':{'PLAT' : 191, 'WIFI':670, 'BTC': 1, 'BTH': 242.5, 'UNKNOWN': 1}, 454 'ws63-liteos-app.map_flash':{'PLAT' : 197, 'WIFI':622.1, 'RADAR':37.5, 'BTC': 143.5, 'BTH': 228, 'UNKNOWN': 0.5}} 455 lim_i = 0 456 for owner, size in group_dict.items(): 457 size_t = size/BIT_SIZE 458 if save_name in lim_all: 459 lim = lim_all[save_name] 460 f.write(f"{owner},{size_t},{lim[owner]}\n") 461 if size_t > lim[owner]: 462 print("Error " + owner + " Exceeded " + save_name + " memory size limit!! Current size is %.3f limit is %.3f" %(size_t, lim[owner])) 463 sys.exit(1) 464 lim_i += 1 465 else: 466 f.write(f"{owner},{size_t},0\n") 467 468 # 应用组 算法组 协议组 前端组 系统组 按 文件名 库名 角度统计 地址大小 469 with open(OUTPUT_DIR / f"{save_name}_file.csv", 'w', encoding='utf_8_sig') as f: 470 header = ['文件名', '库名', '文件大小', '负责组', 'WIFI_责任组'] 471 f.write(f"{','.join(header)}\n") 472 wifi_group_dict = defaultdict(int) # 用于下面的 WIFI负责组的角度统计 地址大小 473 for c_name, info_dict in wifi_stat_dict.items(): 474 for a_name, info in info_dict.items(): 475 f.write(f"{c_name},{a_name},{info[0]},{info[1]},{info[2]}\n") 476 if info[1] == 'WIFI': 477 wifi_group_dict[info[2]] += info[0] 478 479 # 从WIFI负责组的角度统计 地址大小 480 with open(OUTPUT_DIR / f"{save_name}_file_summary.csv", 'w', encoding='utf_8_sig') as f: 481 header = ['Group_Owner', 'Size', 'Limit'] 482 f.write(f"{','.join(header)}\n") 483 lim_all = {'control_ws53.map_flash':{'系统组' : 8.0, '协议组':9.5, '应用组': 2, '前端组': 27.1, '算法组': 0.5,'WIFI_UNKNOWN': 5}, 484 'liteos_ws53_light.map_flash':{'系统组' : 36, '协议组':212.5, '应用组': 356, '前端组': 15, '算法组': 39,'WIFI_UNKNOWN': 0.5}, 485 'ws63-liteos-app.map_flash':{'系统组' : 35.5, '协议组':196.1, '应用组': 314.5, '前端组': 37.1, '算法组': 39.3,'WIFI_UNKNOWN': 0.5}} 486 lim_i = 0 487 for owner, size in wifi_group_dict.items(): 488 size_t = size/BIT_SIZE 489 if save_name in lim_all: 490 lim = lim_all[save_name] 491 f.write(f"{owner},{size_t},{lim[owner]}\n") 492 if size_t > lim[owner]: 493 print("Error " + owner + " Exceeded " + save_name + " memory size limit!! Current size is %.3f limit is %.3f" %(size_t, lim[owner])) 494 sys.exit(1) 495 lim_i += 1 496 else: 497 f.write(f"{owner},{size_t},0\n") 498 499 500def usage(): 501 print("usage:") 502 print(" python3 script target_lst_file target_map_file") 503 print("") 504 505 506if __name__ == "__main__": 507 if len(sys.argv[1:]) != 2: 508 usage() 509 sys.exit(0) 510 root_path = os.path.join(os.path.split(os.path.realpath(__file__))[0], '..', '..', '..') 511 root_path = os.path.abspath(root_path) 512 BIT_SIZE = 1024 513 OWNER_DIR = Path(root_path) / 'protocol/wifi/build/smaller' 514 if not OWNER_DIR.exists(): 515 sys.exit(0) 516 DATA_DIR = Path('./') 517 OUTPUT_DIR = Path('./smaller') # 输出的文件夹下 存放表格数据 518 if not OUTPUT_DIR.exists(): 519 OUTPUT_DIR.mkdir() 520 g_state_lst = ['LOAD,', 'DEBUGGING,'] # 状态列表 521 g_ram_dict, g_flash_dict, g_rom_dict = defaultdict(list), defaultdict(list), defaultdict(list) 522 try: 523 lst_info_dict = parse_lst_file(DATA_DIR / sys.argv[1]) 524 parse_map_file(DATA_DIR / sys.argv[2]) 525 except: 526 sys.exit(0) 527 get_group_owner(g_ram_dict, f"{sys.argv[2]}_ram") 528 get_group_owner(g_flash_dict, f"{sys.argv[2]}_flash") 529 get_group_owner(g_rom_dict, f"{sys.argv[2]}_rom") 530