From 4f79b7ff6bf773b58ece22e8cc0845a8082dbd55 Mon Sep 17 00:00:00 2001 From: Louie Lu Date: Wed, 5 Oct 2016 14:24:59 +0800 Subject: [PATCH] scripts: New tool to dump init hooks Introducing new tool to dump init hook in f9-kernel. At now this script will dump init hook by hook level, but if hook is at same level, is may not dump by the real sequence when kernel perform run_init_hook in runtime. This tool's mechanism for getting hook level is to parse source code and use regex to get `INIT_HOOK(hander, hook_level)`, hook_level will be eval in python and for fallback, it will parse object file in build directory to get hook level, so you'll need to bulid a full f9-kernel, and get setup GNU toolchain at first. Also, this tool will get some information from config file like `.config`, `Makefile`, `mk/toolchain.mk` for some general setting variable: BOARD, CROSS_COMPILE...etc Class MakefileConfig perform will config interface. Parser in MakefileConfig can only parse makefile `?=` variable when the attribute is not set. (it only serive for BOARD and CROSS_COMPILE for now) for example: c = MakefileConfig() # Create new object, # also load .config into it. c.CONFIG_DEBUG # Return True c.CONFIG_LOADER # Raise KeyError c.parse_makefile('Makefile') # Parsing 'Makefile' c.BOARD # Return config board c.out # build/$(BOARD) As I say, it only impl very low parsing process for makefile, so you can see the last example return `build/$(BOARD)`, thought $(BOARD) has been set in object c, it still won't convert to `build/stm32p103` style for now. --- scripts/list_init_hooks.py | 176 +++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100755 scripts/list_init_hooks.py diff --git a/scripts/list_init_hooks.py b/scripts/list_init_hooks.py new file mode 100755 index 00000000..36019fd1 --- /dev/null +++ b/scripts/list_init_hooks.py @@ -0,0 +1,176 @@ +#!/usr/bin/python +# +# Copyright (c) 2016 The F9 Microkernel Project. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +# +# Description: +# list all init hooks in f9-kernel. +# +# At now this script will list by hook level, +# but if the hook is on the same int level, this will +# not dump by real sequence when kernel perform run_init_hook. +# + +# ----------------- LIBRARIES -------------------- + +import os +import re +import sys +import glob +import subprocess +from collections import OrderedDict + +# ------------------- CONST -------------------- + +# Define in kernel/init_hook.h +HOOK_LEVEL = OrderedDict([]) +HOOK_LEVEL.update({'INIT_LEVEL_EARLIEST': 1}) +HOOK_LEVEL.update({'INIT_LEVEL_PLATFORM_EARLY': 0x1000}) +HOOK_LEVEL.update({'INIT_LEVEL_PLATFORM': 0x2000}) +HOOK_LEVEL.update({'INIT_LEVEL_KERNEL_EARLY': 0x3000}) +HOOK_LEVEL.update({'INIT_LEVEL_KERNEL': 0x4000}) +HOOK_LEVEL.update({'INIT_LEVEL_LAST': 0xFFFFFFFF}) + +# Dynamiclly adding variable on the fly +for hook in HOOK_LEVEL: + exec('%s = %d' % (hook, HOOK_LEVEL[hook])) + + +# ----------------- CLASS -------------------- +class MakefileConfig(dict): + __getattr__ = dict.__getitem__ + __setattr__ = dict.__setitem__ + __delattr__ = dict.__delitem__ + + def __init__(self, infile=None): + self.board = {} + + # Parsing .config file + self.parse_defconfig() + self.parse_config('.config', user_config=True) + + # Parsing infile for Makefile + if infile is not None: + self.parse_makefile(infile) + + def parse_makefile(self, infile): + f = map(lambda x: x.strip('\r\n ').replace(' ', ''), + open(infile, 'r').readlines()) + + for i in f: + if len(i.split('?=')) == 2: + key, value = i.split('?=') + + # Hack for ?=, if not set then set. + try: + self.__getattr__(key) + except: + self.__setattr__(key, value) + + def parse_config(self, path, user_config=False): + maps = map(lambda x: x.strip('\r\n '), + open(path, 'r').readlines()) + + for i in maps: + if i and i.startswith('#') is False: + key, value = i.split('=') + + if user_config: + self.__setattr__(key, True) + + if key.startswith('CONFIG_BOARD_'): + self.__setattr__('BOARD', self.board[key]) + else: + if key.startswith('CONFIG_BOARD_'): + self.board[key] = path.split('/')[1] + + def parse_defconfig(self): + defconfig = glob.glob('board/*/defconfig') + for path in defconfig: + self.parse_config(path) + + +class hook: + + def __init__(self, s): + self.config = MakefileConfig('Makefile') + self.config.parse_makefile('mk/toolchain.mk') + self.parse(s) + + def __str__(self): + return "%-30s%-30s%-30s%x" % ( + self.files, self.hook_function, self.hook_level, self.hook_level_int) + + def __lt__(self, other): + return self.hook_level_int < other.hook_level_int + + def parse_from_obj(self, f): + path = 'build/%s/%s' % (self.config.BOARD, f.replace('.c', '.o')) + s = subprocess.check_output(['%sobjdump' % (self.config.CROSS_COMPILE), + '-s', + '-j', + '.init_hook', + path]).decode('utf-8') + + # Get hook level + s = s.split('\n')[4].split(' ')[2] + + # Convert to hex value + ret = int(''.join([s[i:i + 2] for i in range(0, len(s), 2)][::-1]), 16) + return ret + + def parse(self, s): + s = s.replace(' ', '') + self.files, hooks = s.split(':') + + # Regex for splitting function and level + regex = r"\((.*?),(.*?)\)" + self.hook_function, self.hook_level = re.search(regex, hooks).groups() + try: + self.hook_level_int = eval(self.hook_level) + except: + self.hook_level_int = self.parse_from_obj(self.files) + + +def parse_hooks(hooks): + init_hooks = sorted([hook(i) for i in hooks]) + + return init_hooks + + +def print_by_level(hooks): + last_level = 0 + for level_tag, level_int in HOOK_LEVEL.items(): + print(level_tag) + + token = False + for hook in hooks: + if hook.hook_level_int > last_level and \ + hook.hook_level_int <= level_int: + token = True + print(hook) + + if token is False: + print('--- None ---') + + last_level = level_int + print('') + + +if __name__ == '__main__': + # Change to top dir + abspath = os.path.abspath(sys.path[0]) + dname = os.path.dirname(abspath) + os.chdir(dname) + + # This command search all INIT_HOOK in top dir + grep_command = ['grep', '-e', 'INIT_HOOK', '-r', '--include', '*.c'] + + # Get grep output + grep_output = filter(lambda x: x, subprocess.check_output( + grep_command).decode('utf-8').split('\n')) + + # Process + ph = parse_hooks(grep_output) + print_by_level(ph)