openharmony_kernel_liteos_m/tools/mem_analysis.py

353 lines
13 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2020-2022 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import re
import sys
import argparse
import os
import datetime
g_version = 2.5
g_excel_support = False
g_row_num = 0
try:
from openpyxl import Workbook
except:
print("excel output is not support, need install openpyxl")
else:
g_excel_support = True
def is_number(num):
try:
int(num, 16)
return True
except ValueError:
pass
return False
static_map = {}
def storage_static_data(offset, section, sizeHex, symbol, lib, obj):
global g_row_num
if (0 == g_row_num):
static_map[g_row_num] = {'offsets' : "offsets", 'section' : "section",\
'sizeH' : "size(Hex)", 'sizeD' : "size(Dec)",\
'symbol' : "symbol", 'lib' : "lib", 'obj' : "object"}
g_row_num = g_row_num + 1
size = int(sizeHex, 16)
static_map[g_row_num] = {'offsets': offset, 'section' : section,\
'sizeH' : sizeHex, 'sizeD' : size,\
'symbol' : symbol, 'lib' : lib, 'obj' : obj}
g_row_num = g_row_num + 1
def store_static_excel():
wb = Workbook()
ws = wb.active
for base_address, values in static_map.items():
c = ws.cell(row=base_address + 1, column=1)
c.value = values.get('offsets')
c = ws.cell(row=base_address + 1, column=2)
c.value = values.get('section')
c = ws.cell(row=base_address + 1, column=3)
c.value = values.get('sizeH')
c = ws.cell(row=base_address + 1, column=4)
c.value = values.get('sizeD')
c = ws.cell(row=base_address + 1, column=5)
c.value = values.get('symbol')
c = ws.cell(row=base_address + 1, column=6)
c.value = values.get('lib')
c = ws.cell(row=base_address + 1, column=7)
c.value = values.get('obj')
wb.save('static_symbol-%s.xlsx' % datetime.datetime.now().strftime('%Y-%m-%d %H_%M_%S'))
return
def format_store_static():
global g_excel_support
target_list = []
for base_address, values in static_map.items():
target_list.append('{:<10s}'.format(values.get('offsets')))
target_list.append('{:<30s}'.format(values.get('section')))
target_list.append('{:<10}'.format(values.get('sizeH')))
target_list.append('{:<10}'.format(values.get('sizeD')))
target_list.append('{:<55s}{:<55s}{:<55s}'.format(values.get('symbol'), values.get('lib'), values.get('obj')))
target_list.append("\r")
with open("./static_symbol_out.txt", "w+") as out_fd:
out_fd.write("".join(target_list))
if g_excel_support == True:
store_static_excel()
return
dync_map = {}
#dync map multiple LR, first dync_map node will record LR size
def storage_dync_head(size):
global g_row_num
dync_map[0] = {'addr' : "Memnode", 'symbol' : "size", 'lib' : size}
g_row_num = 1
#when the analysis of one line LR is finished, need record "\r\n" and the new line info
def storage_dync_node(node_addr, size):
global g_row_num
dync_map[g_row_num] = {'addr' : "\r\n", 'symbol' : node_addr, 'lib' : size}
g_row_num = g_row_num + 1
def storage_dync_data(addr, symbol, lib):
global g_row_num
dync_map[g_row_num] = {'addr' : addr, 'symbol' : symbol, 'lib' : lib}
g_row_num = g_row_num + 1
def store_dync_excel():
wb = Workbook()
ws = wb.active
row = 1
column = 0
for base_address, values in dync_map.items():
if (0 == base_address):
c = ws.cell(row, 1)
c.value = values.get('addr')
c = ws.cell(row, 2)
c.value = values.get('symbol')
for i in range(values.get('lib')):
column = i * 3 + 2
addr_name = "LR%s_addr" % (str(i))
c = ws.cell(row, column + 1)
c.value = addr_name
symbol_name = "LR%s_symbol" % (str(i))
c = ws.cell(row, column + 2)
c.value = symbol_name
object_name = "LR%s_object" % (str(i))
c = ws.cell(row, column + 3)
c.value = object_name
else:
if("\r\n" == values.get('addr')):
row = row + 1
column = 1
c = ws.cell(row, column)
c.value = values.get('symbol')
column = column + 1
c = ws.cell(row, column)
c.value = values.get('lib')
else:
c = ws.cell(row, column + 1)
c.value = values.get('addr')
c = ws.cell(row, column + 2)
c.value = values.get('symbol')
c = ws.cell(row, column + 3)
c.value = values.get('lib')
column = column + 3
wb.save('dync_mem-%s.xlsx' % datetime.datetime.now().strftime('%Y-%m-%d %H_%M_%S'))
return
def format_store_dync():
global g_excel_support
target_list = []
for base_address, values in dync_map.items():
if (0 == base_address):
target_list.append('{:<15s}{:<15s}'.format("node", "size"))
for i in range(values.get('lib')):
addr_name = "LR%s_addr" % (str(i))
symbol_name = "LR%s_symbol" % (str(i))
object_name = "LR%s_object" % (str(i))
target_list.append('{:<15s}{:<55s}{:<55s}'.format(addr_name, symbol_name, object_name))
else:
if ("\r\n" == values.get('addr')):
target_list.append("\r")
target_list.append('{:<15s}{:<15}'.format(values.get('symbol'), values.get('lib')))
else:
target_list.append('{:<15s}'.format(values.get('addr')))
target_list.append('{:<55s}'.format(values.get('symbol')))
target_list.append('{:<55s}'.format(values.get('lib')))
with open("./dynamic_memory_out.txt", "w+") as out_fd:
out_fd.write("".join(target_list))
if g_excel_support == True:
store_dync_excel()
return
address_map = {}
def get_func_by_name(function_name):
for base_address, values in address_map.items():
if values.get('func_name') == function_name:
return values.get('lib_name'), values.get('obj_name')
return "none", "none"
def get_func_by_address(address):
if 0 == address:
return "none", "none"
for base_address, values in address_map.items():
if (base_address <= address) and (base_address + values.get('size') > address):
return values.get('func_name'), values.get('obj_name')
return "none", "none"
def line_start_check(line):
if line.startswith(' .text.'):
function_name = line.split(".text.")[1]
elif line.startswith(' .rodata.'):
function_name = line.split(".rodata.")[1]
elif line.startswith(' .bss.'):
function_name = line.split(".bss.")[1]
elif line.startswith(' .sram.text.'):
function_name = line.split(".sram.text.")[1]
elif line.startswith(' .data.'):
function_name = line.split(".data.")[1]
else:
return None
return function_name
def parse_map(file_path):
parse_begin = False
function_name = ""
address = None
size = None
obj_name = ""
lib_name = ""
pl = re.compile(r'[(](.*?)[)]', re.S)
with open(file_path, "r") as f:
for line in f.readlines():
if not parse_begin:
function_name = line_start_check(line)
if function_name != None:
if len(function_name.split()) > 1:
strs = function_name.split()
function_name = strs[0]
function_name = function_name.strip()
address = int(strs[1], 16)
size = int(strs[2], 16)
obj_name = re.findall(pl, strs[3])
if len(obj_name) > 0:
obj_name = obj_name[0]
else:
obj_name = strs[3]
char_temp = strs[3].split('(')
if len(char_temp) > 1:
lib_name = char_temp[0].split('/')[-1]
else:
lib_name = "none"
address_map[address] = {'size' : size, 'lib_name' : lib_name, \
'obj_name' : obj_name, 'func_name' : function_name}
else:
parse_begin = True
else:
continue
else:
parse_begin = False
if len(line.strip().split()) != 3:
continue
strs = line.strip().split()
address = int(strs[0], 16)
size = int(strs[1], 16)
obj_name = re.findall(pl, strs[2])
if len(obj_name) > 0:
obj_name = obj_name[0]
else:
obj_name = strs[2]
function_name = function_name.strip()
char_temp = strs[2].split('(')
if len(char_temp) > 1:
lib_name = char_temp[0].split('/')[-1]
else:
lib_name = "none"
address_map[address] = {'size' : size, 'lib_name' : lib_name, \
'obj_name' : obj_name, 'func_name' : function_name}
def parse_dump(dump_path):
with open(dump_path, "r") as symbol_fd:
symline = symbol_fd.readlines()
for index, line in enumerate(symline):
offset = line[:8]
#According to the regular expression of objdump , section start at 17 char
symbol_list = line[17:].split()
if len(symbol_list) < 3:
continue
sec = symbol_list[0]
size = symbol_list[1]
if (False == is_number(size)) or 0 == int(size, 16):
continue
lib, obj = get_func_by_name(symbol_list[-1])
storage_static_data(offset, sec, size, symbol_list[-1], lib, obj)
def parse_log(log_path):
with open(log_path, "r") as log_fd:
logline = log_fd.readlines()
get_valid_log = False
for index, line in enumerate(logline):
char_list = line.split()
if len(char_list) < 3:
continue
if ("node" == char_list[0]) & ("LR[0]" == char_list[2]):
storage_dync_head(len(char_list) - 2)
get_valid_log = True
continue
if True == get_valid_log:
mem_addr = char_list[0].split(':')
if True == is_number(mem_addr[0]):
for i in range(len(char_list)):
if i == 0:
size = int(char_list[1], 16)
storage_dync_node(mem_addr[0], size)
if i > 1:
symbol, lib = get_func_by_address(int(char_list[i], 16))
storage_dync_data(char_list[i], symbol, lib)
else:
get_valid_log = False
continue
def main():
print("memory parses tool ver.%2f\r\n" % g_version)
parser = argparse.ArgumentParser()
parser.add_argument('--m', help='map path.')
parser.add_argument('--l', help='dynamic mem log path.')
parser.add_argument('--d', help='objdump path.')
args = parser.parse_args()
print("map path: %s\r\n" % args.m)
if args.m == None :
print("arg error, input -h get the help list\r\n")
return
parse_map(args.m)
print("dump path: %s" % args.d)
if args.d != None :
parse_dump(args.d)
format_store_static()
else:
print("dump path unspecified, will not be static parser")
print("you can enter the objdump -t command under the linux shell to obtain dump information\r\n")
print("log path: %s" % args.l)
if args.l != None :
parse_log(args.l)
format_store_dync()
else:
print("log path unspecified, will not be dynamic parser\r\n")
return
if __name__ == '__main__':
sys.exit(main())