scripts: check_init_priorities: use the Zephyr executable file
Rework check_init_priorities to use the main executable file instead of the individual object files for discovering the devices. This should make the script more robust in case of stale files in the build directory, and also makes it work with LTO object files. Additionally, keep track of the detected init calls, and add a handy "-i" option to produce a human readable print of the initcalls in the call sequence, which can be useful for debugging initialization problems due to odd SYS_INIT and DEVICE interactions. Signed-off-by: Fabio Baltieri <fabiobaltieri@google.com>
This commit is contained in:
parent
f0cc8d4cb1
commit
5212a4c619
|
@ -1811,8 +1811,7 @@ if(CONFIG_CHECK_INIT_PRIORITIES)
|
|||
list(APPEND
|
||||
post_build_commands
|
||||
COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/build/check_init_priorities.py
|
||||
--build-dir ${PROJECT_BINARY_DIR}/..
|
||||
--edt-pickle ${EDT_PICKLE}
|
||||
--elf-file=${ZEPHYR_BINARY_DIR}/${KERNEL_ELF_NAME}
|
||||
${fail_on_warning}
|
||||
)
|
||||
endif()
|
||||
|
|
|
@ -747,6 +747,7 @@ config BUILD_OUTPUT_STRIP_PATHS
|
|||
config CHECK_INIT_PRIORITIES
|
||||
bool "Build time initialization priorities check"
|
||||
default y
|
||||
depends on !NATIVE_LIBRARY
|
||||
help
|
||||
Check the build for initialization priority issues by comparing the
|
||||
initialization priority in the build with the device dependency
|
||||
|
|
|
@ -6,14 +6,16 @@
|
|||
"""
|
||||
Checks the initialization priorities
|
||||
|
||||
This script parses the object files in the specified build directory, creates a
|
||||
list of known devices and their effective initialization priorities and
|
||||
compares that with the device dependencies inferred from the devicetree
|
||||
hierarchy.
|
||||
This script parses a Zephyr executable file, creates a list of known devices
|
||||
and their effective initialization priorities and compares that with the device
|
||||
dependencies inferred from the devicetree hierarchy.
|
||||
|
||||
This can be used to detect devices that are initialized in the incorrect order,
|
||||
but also devices that are initialized at the same priority but depends on each
|
||||
other, which can potentially break if the linking order is changed.
|
||||
|
||||
Optionally, it can also produce a human readable list of the initialization
|
||||
calls for the various init levels.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
|
@ -24,7 +26,6 @@ import pickle
|
|||
import sys
|
||||
|
||||
from elftools.elf.elffile import ELFFile
|
||||
from elftools.elf.relocation import RelocationSection
|
||||
from elftools.elf.sections import SymbolTableSection
|
||||
|
||||
# This is needed to load edt.pickle files.
|
||||
|
@ -32,24 +33,14 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..",
|
|||
"dts", "python-devicetree", "src"))
|
||||
from devicetree import edtlib # pylint: disable=unused-import
|
||||
|
||||
# Prefix used for relocation sections containing initialization data, as in
|
||||
# sequence of "struct init_entry".
|
||||
_INIT_SECTION_PREFIX = (".rel.z_init_", ".rela.z_init_")
|
||||
|
||||
# Prefix used for "struct device" reference initialized based on devicetree
|
||||
# entries with a known ordinal.
|
||||
_DEVICE_ORD_PREFIX = "__device_dts_ord_"
|
||||
|
||||
# File name suffix for object files to be scanned.
|
||||
_OBJ_FILE_SUFFIX = ".c.obj"
|
||||
|
||||
# Defined init level in order of priority.
|
||||
_DEVICE_INIT_LEVELS = ["EARLY", "PRE_KERNEL_1", "PRE_KERNEL_2", "POST_KERNEL",
|
||||
"APPLICATION", "SMP"]
|
||||
|
||||
# File name to check for detecting and skiping nested build directories.
|
||||
_BUILD_DIR_DETECT_FILE = "CMakeCache.txt"
|
||||
|
||||
# List of compatibles for node where the initialization priority should be the
|
||||
# opposite of the device tree inferred dependency.
|
||||
_INVERTED_PRIORITY_COMPATIBLES = frozenset()
|
||||
|
@ -65,34 +56,28 @@ _IGNORE_COMPATIBLES = frozenset([
|
|||
class Priority:
|
||||
"""Parses and holds a device initialization priority.
|
||||
|
||||
Parses an ELF section name for the corresponding initialization level and
|
||||
priority, for example ".rel.z_init_PRE_KERNEL_155_" for "PRE_KERNEL_1 55".
|
||||
|
||||
The object can be used for comparing levels with one another.
|
||||
|
||||
Attributes:
|
||||
name: the section name
|
||||
"""
|
||||
def __init__(self, name):
|
||||
for idx, level in enumerate(_DEVICE_INIT_LEVELS):
|
||||
if level in name:
|
||||
_, priority_str = name.strip("_").split(level)
|
||||
priority, sub_priority = priority_str.split("_")
|
||||
def __init__(self, level, priority):
|
||||
for idx, level_name in enumerate(_DEVICE_INIT_LEVELS):
|
||||
if level_name == level:
|
||||
self._level = idx
|
||||
self._priority = int(priority)
|
||||
self._sub_priority = int(sub_priority)
|
||||
self._priority = priority
|
||||
# Tuples compare elementwise in order
|
||||
self._level_priority = (self._level, self._priority, self._sub_priority)
|
||||
self._level_priority = (self._level, self._priority)
|
||||
return
|
||||
|
||||
raise ValueError("Unknown level in %s" % name)
|
||||
raise ValueError("Unknown level in %s" % level)
|
||||
|
||||
def __repr__(self):
|
||||
return "<%s %s %d %d>" % (self.__class__.__name__,
|
||||
_DEVICE_INIT_LEVELS[self._level], self._priority, self._sub_priority)
|
||||
return "<%s %s %d>" % (self.__class__.__name__,
|
||||
_DEVICE_INIT_LEVELS[self._level], self._priority)
|
||||
|
||||
def __str__(self):
|
||||
return "%s %d %d" % (_DEVICE_INIT_LEVELS[self._level], self._priority, self._sub_priority)
|
||||
return "%s %d" % (_DEVICE_INIT_LEVELS[self._level], self._priority)
|
||||
|
||||
def __lt__(self, other):
|
||||
return self._level_priority < other._level_priority
|
||||
|
@ -104,15 +89,15 @@ class Priority:
|
|||
return self._level_priority
|
||||
|
||||
|
||||
class ZephyrObjectFile:
|
||||
"""Load an object file and finds the device defined within it.
|
||||
class ZephyrInitLevels:
|
||||
"""Load an executable file and find the initialization calls and devices.
|
||||
|
||||
Load an object file and scans the relocation sections looking for the known
|
||||
ones containing initialization callbacks. Then finds what device ordinals
|
||||
are being initialized at which priority and stores the list internally.
|
||||
Load a Zephyr executable file and scan for the list of initialization calls
|
||||
and defined devices.
|
||||
|
||||
A dictionary of {ordinal: Priority} is available in the defined_devices
|
||||
class variable.
|
||||
The list of devices is available in the "devices" class variable in the
|
||||
{ordinal: Priority} format, the list of initilevels is in the "initlevels"
|
||||
class variables in the {"level name": ["call", ...]} format.
|
||||
|
||||
Attributes:
|
||||
file_path: path of the file to be loaded.
|
||||
|
@ -120,26 +105,49 @@ class ZephyrObjectFile:
|
|||
def __init__(self, file_path):
|
||||
self.file_path = file_path
|
||||
self._elf = ELFFile(open(file_path, "rb"))
|
||||
self._load_symbols()
|
||||
self._find_defined_devices()
|
||||
self._load_objects()
|
||||
self._load_level_addr()
|
||||
self._process_initlevels()
|
||||
|
||||
def _load_symbols(self):
|
||||
"""Initialize the symbols table."""
|
||||
self._symbols = {}
|
||||
def _load_objects(self):
|
||||
"""Initialize the object table."""
|
||||
self._objects = {}
|
||||
|
||||
for section in self._elf.iter_sections():
|
||||
if not isinstance(section, SymbolTableSection):
|
||||
continue
|
||||
|
||||
for num, sym in enumerate(section.iter_symbols()):
|
||||
if sym.name:
|
||||
self._symbols[num] = sym.name
|
||||
for sym in section.iter_symbols():
|
||||
if (sym.name and
|
||||
sym.entry.st_size > 0 and
|
||||
sym.entry.st_info.type in ["STT_OBJECT", "STT_FUNC"]):
|
||||
self._objects[sym.entry.st_value] = (
|
||||
sym.name, sym.entry.st_size, sym.entry.st_shndx)
|
||||
|
||||
def _device_ord_from_rel(self, rel):
|
||||
"""Find a device ordinal from a device symbol name."""
|
||||
sym_id = rel["r_info_sym"]
|
||||
sym_name = self._symbols.get(sym_id, None)
|
||||
def _load_level_addr(self):
|
||||
"""Find the address associated with known init levels."""
|
||||
self._init_level_addr = {}
|
||||
|
||||
for section in self._elf.iter_sections():
|
||||
if not isinstance(section, SymbolTableSection):
|
||||
continue
|
||||
|
||||
for sym in section.iter_symbols():
|
||||
for level in _DEVICE_INIT_LEVELS:
|
||||
name = f"__init_{level}_start"
|
||||
if sym.name == name:
|
||||
self._init_level_addr[level] = sym.entry.st_value
|
||||
elif sym.name == "__init_end":
|
||||
self._init_level_end = sym.entry.st_value
|
||||
|
||||
if len(self._init_level_addr) != len(_DEVICE_INIT_LEVELS):
|
||||
raise ValueError(f"Missing init symbols, found: {self._init_level_addr}")
|
||||
|
||||
if not self._init_level_end:
|
||||
raise ValueError(f"Missing init section end symbol")
|
||||
|
||||
def _device_ord_from_name(self, sym_name):
|
||||
"""Find a device ordinal from a symbol name."""
|
||||
if not sym_name:
|
||||
return None
|
||||
|
||||
|
@ -149,35 +157,67 @@ class ZephyrObjectFile:
|
|||
_, device_ord = sym_name.split(_DEVICE_ORD_PREFIX)
|
||||
return int(device_ord)
|
||||
|
||||
def _find_defined_devices(self):
|
||||
"""Find the device structures defined in the object file."""
|
||||
self.defined_devices = {}
|
||||
def _object_name(self, addr):
|
||||
if not addr:
|
||||
return "NULL"
|
||||
elif addr in self._objects:
|
||||
return self._objects[addr][0]
|
||||
else:
|
||||
return "unknown"
|
||||
|
||||
for section in self._elf.iter_sections():
|
||||
if not isinstance(section, RelocationSection):
|
||||
continue
|
||||
def _initlevel_pointer(self, addr, idx, shidx):
|
||||
elfclass = self._elf.elfclass
|
||||
if elfclass == 32:
|
||||
ptrsize = 4
|
||||
elif elfclass == 64:
|
||||
ptrsize = 8
|
||||
else:
|
||||
ValueError(f"Unknown pointer size for ELF class f{elfclass}")
|
||||
|
||||
if not section.name.startswith(_INIT_SECTION_PREFIX):
|
||||
continue
|
||||
section = self._elf.get_section(shidx)
|
||||
start = section.header.sh_addr
|
||||
data = section.data()
|
||||
|
||||
prio = Priority(section.name)
|
||||
offset = addr - start
|
||||
|
||||
for rel in section.iter_relocations():
|
||||
device_ord = self._device_ord_from_rel(rel)
|
||||
if not device_ord:
|
||||
continue
|
||||
start = offset + ptrsize * idx
|
||||
stop = offset + ptrsize * (idx + 1)
|
||||
|
||||
if device_ord in self.defined_devices:
|
||||
raise ValueError(
|
||||
f"Device {device_ord} already defined, stale "
|
||||
"object files in the build directory? "
|
||||
"Try running a clean build.")
|
||||
return int.from_bytes(data[start:stop], byteorder="little")
|
||||
|
||||
self.defined_devices[device_ord] = prio
|
||||
def _process_initlevels(self):
|
||||
"""Process the init level and find the init functions and devices."""
|
||||
self.devices = {}
|
||||
self.initlevels = {}
|
||||
|
||||
def __repr__(self):
|
||||
return (f"<{self.__class__.__name__} {self.file_path} "
|
||||
f"defined_devices: {self.defined_devices}>")
|
||||
for i, level in enumerate(_DEVICE_INIT_LEVELS):
|
||||
start = self._init_level_addr[level]
|
||||
if i + 1 == len(_DEVICE_INIT_LEVELS):
|
||||
stop = self._init_level_end
|
||||
else:
|
||||
stop = self._init_level_addr[_DEVICE_INIT_LEVELS[i + 1]]
|
||||
|
||||
self.initlevels[level] = []
|
||||
|
||||
priority = 0
|
||||
addr = start
|
||||
while addr < stop:
|
||||
if addr not in self._objects:
|
||||
raise ValueError(f"no symbol at addr {addr:08x}")
|
||||
obj, size, shidx = self._objects[addr]
|
||||
|
||||
arg0_name = self._object_name(self._initlevel_pointer(addr, 0, shidx))
|
||||
arg1_name = self._object_name(self._initlevel_pointer(addr, 1, shidx))
|
||||
|
||||
self.initlevels[level].append(f"{obj}: {arg0_name}({arg1_name})")
|
||||
|
||||
ordinal = self._device_ord_from_name(arg1_name)
|
||||
if ordinal:
|
||||
prio = Priority(level, priority)
|
||||
self.devices[ordinal] = prio
|
||||
|
||||
addr += size
|
||||
priority += 1
|
||||
|
||||
class Validator():
|
||||
"""Validates the initialization priorities.
|
||||
|
@ -187,52 +227,26 @@ class Validator():
|
|||
dependency list and log any found priority issue.
|
||||
|
||||
Attributes:
|
||||
build_dir: the build directory to scan
|
||||
edt_pickle_path: path of the EDT pickle file
|
||||
elf_file_path: path of the ELF file
|
||||
edt_pickle: name of the EDT pickle file
|
||||
log: a logging.Logger object
|
||||
"""
|
||||
def __init__(self, build_dir, edt_pickle_path, log):
|
||||
def __init__(self, elf_file_path, edt_pickle, log):
|
||||
self.log = log
|
||||
|
||||
edtser = pathlib.Path(build_dir, edt_pickle_path)
|
||||
with open(edtser, "rb") as f:
|
||||
edt_pickle_path = pathlib.Path(
|
||||
pathlib.Path(elf_file_path).parent,
|
||||
edt_pickle)
|
||||
with open(edt_pickle_path, "rb") as f:
|
||||
edt = pickle.load(f)
|
||||
|
||||
self._ord2node = edt.dep_ord2node
|
||||
|
||||
self._objs = []
|
||||
for file in self._find_build_objfiles(build_dir, is_root=True):
|
||||
obj = ZephyrObjectFile(file)
|
||||
if obj.defined_devices:
|
||||
self._objs.append(obj)
|
||||
for dev, prio in obj.defined_devices.items():
|
||||
dev_path = self._ord2node[dev].path
|
||||
self.log.debug(f"{file}: {dev_path} {prio}")
|
||||
|
||||
self._dev_priorities = {}
|
||||
for obj in self._objs:
|
||||
for dev, prio in obj.defined_devices.items():
|
||||
if dev in self._dev_priorities:
|
||||
dev_path = self._ord2node[dev].path
|
||||
raise ValueError(
|
||||
f"ERROR: device {dev} ({dev_path}) already defined")
|
||||
self._dev_priorities[dev] = prio
|
||||
self._obj = ZephyrInitLevels(elf_file_path)
|
||||
|
||||
self.warnings = 0
|
||||
self.errors = 0
|
||||
|
||||
def _find_build_objfiles(self, build_dir, is_root=False):
|
||||
"""Find all project object files, skip sub-build directories."""
|
||||
if not is_root and pathlib.Path(build_dir, _BUILD_DIR_DETECT_FILE).exists():
|
||||
return
|
||||
|
||||
for file in pathlib.Path(build_dir).iterdir():
|
||||
if file.is_file() and file.name.endswith(_OBJ_FILE_SUFFIX):
|
||||
yield file
|
||||
if file.is_dir():
|
||||
for file in self._find_build_objfiles(file.resolve()):
|
||||
yield file
|
||||
|
||||
def _check_dep(self, dev_ord, dep_ord):
|
||||
"""Validate the priority between two devices."""
|
||||
if dev_ord == dep_ord:
|
||||
|
@ -254,8 +268,8 @@ class Validator():
|
|||
self.log.info(f"Swapped priority: {dev_compat}, {dep_compat}")
|
||||
dev_ord, dep_ord = dep_ord, dev_ord
|
||||
|
||||
dev_prio = self._dev_priorities.get(dev_ord, None)
|
||||
dep_prio = self._dev_priorities.get(dep_ord, None)
|
||||
dev_prio = self._obj.devices.get(dev_ord, None)
|
||||
dep_prio = self._obj.devices.get(dep_ord, None)
|
||||
|
||||
if not dev_prio or not dep_prio:
|
||||
return
|
||||
|
@ -286,10 +300,16 @@ class Validator():
|
|||
|
||||
def check_edt(self):
|
||||
"""Scan through all known devices and validate the init priorities."""
|
||||
for dev_ord in self._dev_priorities:
|
||||
for dev_ord in self._obj.devices:
|
||||
dev = self._ord2node[dev_ord]
|
||||
self._check_edt_r(dev_ord, dev)
|
||||
|
||||
def print_initlevels(self):
|
||||
for level, calls in self._obj.initlevels.items():
|
||||
print(level)
|
||||
for call in calls:
|
||||
print(f" {call}")
|
||||
|
||||
def _parse_args(argv):
|
||||
"""Parse the command line arguments."""
|
||||
parser = argparse.ArgumentParser(
|
||||
|
@ -297,8 +317,8 @@ def _parse_args(argv):
|
|||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
allow_abbrev=False)
|
||||
|
||||
parser.add_argument("-d", "--build-dir", default="build",
|
||||
help="build directory to use")
|
||||
parser.add_argument("-f", "--elf-file", default=pathlib.Path("build", "zephyr", "zephyr.elf"),
|
||||
help="ELF file to use")
|
||||
parser.add_argument("-v", "--verbose", action="count",
|
||||
help=("enable verbose output, can be used multiple times "
|
||||
"to increase verbosity level"))
|
||||
|
@ -308,8 +328,10 @@ def _parse_args(argv):
|
|||
help="always exit with a return code of 0, used for testing")
|
||||
parser.add_argument("-o", "--output",
|
||||
help="write the output to a file in addition to stdout")
|
||||
parser.add_argument("--edt-pickle", default=pathlib.Path("zephyr", "edt.pickle"),
|
||||
help="path to read the pickled edtlib.EDT object from",
|
||||
parser.add_argument("-i", "--initlevels", action="store_true",
|
||||
help="print the initlevel functions instead of checking the device dependencies")
|
||||
parser.add_argument("--edt-pickle", default=pathlib.Path("edt.pickle"),
|
||||
help="name of the the pickled edtlib.EDT file",
|
||||
type=pathlib.Path)
|
||||
|
||||
return parser.parse_args(argv)
|
||||
|
@ -323,7 +345,7 @@ def _init_log(verbose, output):
|
|||
log.addHandler(console)
|
||||
|
||||
if output:
|
||||
file = logging.FileHandler(output)
|
||||
file = logging.FileHandler(output, mode="w")
|
||||
file.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
|
||||
log.addHandler(file)
|
||||
|
||||
|
@ -341,10 +363,13 @@ def main(argv=None):
|
|||
|
||||
log = _init_log(args.verbose, args.output)
|
||||
|
||||
log.info(f"check_init_priorities build_dir: {args.build_dir}")
|
||||
log.info(f"check_init_priorities: {args.elf_file}")
|
||||
|
||||
validator = Validator(args.build_dir, args.edt_pickle, log)
|
||||
validator.check_edt()
|
||||
validator = Validator(args.elf_file, args.edt_pickle, log)
|
||||
if args.initlevels:
|
||||
validator.print_initlevels()
|
||||
else:
|
||||
validator.check_edt()
|
||||
|
||||
if args.always_succeed:
|
||||
return 0
|
||||
|
|
|
@ -10,7 +10,7 @@ import mock
|
|||
import pathlib
|
||||
import unittest
|
||||
|
||||
from elftools.elf.relocation import RelocationSection
|
||||
from elftools.elf.relocation import Section
|
||||
from elftools.elf.sections import SymbolTableSection
|
||||
|
||||
import check_init_priorities
|
||||
|
@ -19,191 +19,245 @@ class TestPriority(unittest.TestCase):
|
|||
"""Tests for the Priority class."""
|
||||
|
||||
def test_priority_parsing(self):
|
||||
prio1 = check_init_priorities.Priority(".rel.z_init_POST_KERNEL12_0_")
|
||||
self.assertEqual(prio1._level_priority, (3, 12, 0))
|
||||
prio1 = check_init_priorities.Priority("POST_KERNEL", 12)
|
||||
self.assertEqual(prio1._level_priority, (3, 12))
|
||||
|
||||
prio2 = check_init_priorities.Priority("noisenoise_POST_KERNEL99_00023_")
|
||||
self.assertEqual(prio2._level_priority, (3, 99, 23))
|
||||
|
||||
prio3 = check_init_priorities.Priority("_PRE_KERNEL_10_99999_")
|
||||
self.assertEqual(prio3._level_priority, (1, 0, 99999))
|
||||
|
||||
prio4 = check_init_priorities.Priority("_PRE_KERNEL_110_00001_")
|
||||
self.assertEqual(prio4._level_priority, (1, 10, 1))
|
||||
prio1 = check_init_priorities.Priority("APPLICATION", 9999)
|
||||
self.assertEqual(prio1._level_priority, (4, 9999))
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
check_init_priorities.Priority("i-am-not-a-priority")
|
||||
check_init_priorities.Priority("_DOESNOTEXIST0_")
|
||||
check_init_priorities.Priority(".rel.z_init_POST_KERNEL12_blah")
|
||||
check_init_priorities.Priority(".rel.z_init_2_")
|
||||
check_init_priorities.Priority(".rel.z_init_POST_KERNEL1_")
|
||||
check_init_priorities.Priority("i-am-not-a-priority", 0)
|
||||
check_init_priorities.Priority("_DOESNOTEXIST0_", 0)
|
||||
|
||||
def test_priority_levels(self):
|
||||
prios = [
|
||||
check_init_priorities.Priority(".rel.z_init_EARLY0_0_"),
|
||||
check_init_priorities.Priority(".rel.z_init_EARLY1_0_"),
|
||||
check_init_priorities.Priority(".rel.z_init_EARLY11_0_"),
|
||||
check_init_priorities.Priority(".rel.z_init_PRE_KERNEL_10_0_"),
|
||||
check_init_priorities.Priority(".rel.z_init_PRE_KERNEL_11_0_"),
|
||||
check_init_priorities.Priority(".rel.z_init_PRE_KERNEL_111_0_"),
|
||||
check_init_priorities.Priority(".rel.z_init_PRE_KERNEL_111_1_"),
|
||||
check_init_priorities.Priority(".rel.z_init_PRE_KERNEL_111_00002_"),
|
||||
check_init_priorities.Priority(".rel.z_init_PRE_KERNEL_111_00010_"),
|
||||
check_init_priorities.Priority(".rel.z_init_PRE_KERNEL_20_0_"),
|
||||
check_init_priorities.Priority(".rel.z_init_PRE_KERNEL_21_0_"),
|
||||
check_init_priorities.Priority(".rel.z_init_PRE_KERNEL_211_0_"),
|
||||
check_init_priorities.Priority(".rel.z_init_POST_KERNEL0_0_"),
|
||||
check_init_priorities.Priority(".rel.z_init_POST_KERNEL1_0_"),
|
||||
check_init_priorities.Priority(".rel.z_init_POST_KERNEL11_0_"),
|
||||
check_init_priorities.Priority(".rel.z_init_APPLICATION0_0_"),
|
||||
check_init_priorities.Priority(".rel.z_init_APPLICATION1_0_"),
|
||||
check_init_priorities.Priority(".rel.z_init_APPLICATION11_0_"),
|
||||
check_init_priorities.Priority(".rel.z_init_SMP0_0_"),
|
||||
check_init_priorities.Priority(".rel.z_init_SMP1_0_"),
|
||||
check_init_priorities.Priority(".rel.z_init_SMP11_0_"),
|
||||
check_init_priorities.Priority("EARLY", 0),
|
||||
check_init_priorities.Priority("EARLY", 1),
|
||||
check_init_priorities.Priority("PRE_KERNEL_1", 0),
|
||||
check_init_priorities.Priority("PRE_KERNEL_1", 1),
|
||||
check_init_priorities.Priority("PRE_KERNEL_2", 0),
|
||||
check_init_priorities.Priority("PRE_KERNEL_2", 1),
|
||||
check_init_priorities.Priority("POST_KERNEL", 0),
|
||||
check_init_priorities.Priority("POST_KERNEL", 1),
|
||||
check_init_priorities.Priority("APPLICATION", 0),
|
||||
check_init_priorities.Priority("APPLICATION", 1),
|
||||
check_init_priorities.Priority("SMP", 0),
|
||||
check_init_priorities.Priority("SMP", 1),
|
||||
]
|
||||
|
||||
self.assertListEqual(prios, sorted(prios))
|
||||
|
||||
def test_priority_strings(self):
|
||||
prio = check_init_priorities.Priority(".rel.z_init_POST_KERNEL12_00023_")
|
||||
self.assertEqual(str(prio), "POST_KERNEL 12 23")
|
||||
self.assertEqual(repr(prio), "<Priority POST_KERNEL 12 23>")
|
||||
prio = check_init_priorities.Priority("POST_KERNEL", 12)
|
||||
self.assertEqual(str(prio), "POST_KERNEL 12")
|
||||
self.assertEqual(repr(prio), "<Priority POST_KERNEL 12>")
|
||||
|
||||
class testZephyrObjectFile(unittest.TestCase):
|
||||
"""Tests for the ZephyrObjectFile class."""
|
||||
class testZephyrInitLevels(unittest.TestCase):
|
||||
"""Tests for the ZephyrInitLevels class."""
|
||||
|
||||
@mock.patch("check_init_priorities.ZephyrObjectFile.__init__", return_value=None)
|
||||
def test_load_symbols(self, mock_zofinit):
|
||||
@mock.patch("check_init_priorities.ZephyrInitLevels.__init__", return_value=None)
|
||||
def test_load_objects(self, mock_zilinit):
|
||||
mock_elf = mock.Mock()
|
||||
|
||||
sts = mock.Mock(spec=SymbolTableSection)
|
||||
rel = mock.Mock(spec=RelocationSection)
|
||||
rel = mock.Mock(spec=Section)
|
||||
mock_elf.iter_sections.return_value = [sts, rel]
|
||||
|
||||
s0 = mock.Mock()
|
||||
s0.name = "a"
|
||||
s0.entry.st_info.type = "STT_OBJECT"
|
||||
s0.entry.st_size = 4
|
||||
s0.entry.st_value = 0xaa
|
||||
s0.entry.st_shndx = 1
|
||||
|
||||
s1 = mock.Mock()
|
||||
s1.name = None
|
||||
|
||||
s2 = mock.Mock()
|
||||
s2.name = "b"
|
||||
s2.entry.st_info.type = "STT_FUNC"
|
||||
s2.entry.st_size = 8
|
||||
s2.entry.st_value = 0xbb
|
||||
s2.entry.st_shndx = 2
|
||||
|
||||
sts.iter_symbols.return_value = [s0, s1, s2]
|
||||
|
||||
obj = check_init_priorities.ZephyrObjectFile("")
|
||||
obj = check_init_priorities.ZephyrInitLevels("")
|
||||
obj._elf = mock_elf
|
||||
obj._load_symbols()
|
||||
obj._load_objects()
|
||||
|
||||
self.assertDictEqual(obj._symbols, {0: "a", 2: "b"})
|
||||
self.assertDictEqual(obj._objects, {0xaa: ("a", 4, 1), 0xbb: ("b", 8, 2)})
|
||||
|
||||
@mock.patch("check_init_priorities.Priority")
|
||||
@mock.patch("check_init_priorities.ZephyrObjectFile._device_ord_from_rel")
|
||||
@mock.patch("check_init_priorities.ZephyrObjectFile.__init__", return_value=None)
|
||||
def test_find_defined_devices(self, mock_zofinit, mock_dofr, mock_prio):
|
||||
@mock.patch("check_init_priorities.ZephyrInitLevels.__init__", return_value=None)
|
||||
def test_load_level_addr(self, mock_zilinit):
|
||||
mock_elf = mock.Mock()
|
||||
|
||||
sts = mock.Mock(spec=SymbolTableSection)
|
||||
rel1 = mock.Mock(spec=RelocationSection)
|
||||
rel1.name = ".rel.z_init_SOMETHING"
|
||||
rel2 = mock.Mock(spec=RelocationSection)
|
||||
rel2.name = ".rel.something_else"
|
||||
mock_elf.iter_sections.return_value = [sts, rel1, rel2]
|
||||
rel = mock.Mock(spec=Section)
|
||||
mock_elf.iter_sections.return_value = [sts, rel]
|
||||
|
||||
r0 = mock.Mock()
|
||||
rel1.iter_relocations.return_value = [r0]
|
||||
s0 = mock.Mock()
|
||||
s0.name = "__init_EARLY_start"
|
||||
s0.entry.st_value = 0x00
|
||||
|
||||
mock_dofr.return_value = 123
|
||||
s1 = mock.Mock()
|
||||
s1.name = "__init_PRE_KERNEL_1_start"
|
||||
s1.entry.st_value = 0x11
|
||||
|
||||
r0_prio = mock.Mock()
|
||||
mock_prio.return_value = r0_prio
|
||||
s2 = mock.Mock()
|
||||
s2.name = "__init_PRE_KERNEL_2_start"
|
||||
s2.entry.st_value = 0x22
|
||||
|
||||
obj = check_init_priorities.ZephyrObjectFile("")
|
||||
s3 = mock.Mock()
|
||||
s3.name = "__init_POST_KERNEL_start"
|
||||
s3.entry.st_value = 0x33
|
||||
|
||||
s4 = mock.Mock()
|
||||
s4.name = "__init_APPLICATION_start"
|
||||
s4.entry.st_value = 0x44
|
||||
|
||||
s5 = mock.Mock()
|
||||
s5.name = "__init_SMP_start"
|
||||
s5.entry.st_value = 0x55
|
||||
|
||||
s6 = mock.Mock()
|
||||
s6.name = "__init_end"
|
||||
s6.entry.st_value = 0x66
|
||||
|
||||
sts.iter_symbols.return_value = [s0, s1, s2, s3, s4, s5, s6]
|
||||
|
||||
obj = check_init_priorities.ZephyrInitLevels("")
|
||||
obj._elf = mock_elf
|
||||
obj._find_defined_devices()
|
||||
obj._load_level_addr()
|
||||
|
||||
self.assertDictEqual(obj.defined_devices, {123: r0_prio})
|
||||
mock_dofr.assert_called_once_with(r0)
|
||||
mock_prio.assert_called_once_with(rel1.name)
|
||||
self.assertDictEqual(obj._init_level_addr, {
|
||||
"EARLY": 0x00,
|
||||
"PRE_KERNEL_1": 0x11,
|
||||
"PRE_KERNEL_2": 0x22,
|
||||
"POST_KERNEL": 0x33,
|
||||
"APPLICATION": 0x44,
|
||||
"SMP": 0x55,
|
||||
})
|
||||
self.assertEqual(obj._init_level_end, 0x66)
|
||||
|
||||
@mock.patch("check_init_priorities.ZephyrObjectFile.__init__", return_value=None)
|
||||
def test_device_ord_from_rel(self, mock_zofinit):
|
||||
obj = check_init_priorities.ZephyrObjectFile("")
|
||||
@mock.patch("check_init_priorities.ZephyrInitLevels.__init__", return_value=None)
|
||||
def test_device_ord_from_name(self, mock_zilinit):
|
||||
obj = check_init_priorities.ZephyrInitLevels("")
|
||||
|
||||
obj._symbols = {
|
||||
1: "blah",
|
||||
2: "__device_dts_ord_123",
|
||||
self.assertEqual(obj._device_ord_from_name(None), None)
|
||||
self.assertEqual(obj._device_ord_from_name("hey, hi!"), None)
|
||||
self.assertEqual(obj._device_ord_from_name("__device_dts_ord_123"), 123)
|
||||
|
||||
@mock.patch("check_init_priorities.ZephyrInitLevels.__init__", return_value=None)
|
||||
def test_object_name(self, mock_zilinit):
|
||||
obj = check_init_priorities.ZephyrInitLevels("")
|
||||
obj._objects = {0x123: ("name", 4)}
|
||||
|
||||
self.assertEqual(obj._object_name(0), "NULL")
|
||||
self.assertEqual(obj._object_name(73), "unknown")
|
||||
self.assertEqual(obj._object_name(0x123), "name")
|
||||
|
||||
@mock.patch("check_init_priorities.ZephyrInitLevels.__init__", return_value=None)
|
||||
def test_initlevel_pointer_32(self, mock_zilinit):
|
||||
obj = check_init_priorities.ZephyrInitLevels("")
|
||||
obj._elf = mock.Mock()
|
||||
obj._elf.elfclass = 32
|
||||
mock_section = mock.Mock()
|
||||
obj._elf.get_section.return_value = mock_section
|
||||
mock_section.header.sh_addr = 0x100
|
||||
mock_section.data.return_value = (b"\x01\x00\x00\x00"
|
||||
b"\x02\x00\x00\x00"
|
||||
b"\x03\x00\x00\x00")
|
||||
|
||||
self.assertEqual(obj._initlevel_pointer(0x100, 0, 0), 1)
|
||||
self.assertEqual(obj._initlevel_pointer(0x100, 1, 0), 2)
|
||||
self.assertEqual(obj._initlevel_pointer(0x104, 0, 0), 2)
|
||||
self.assertEqual(obj._initlevel_pointer(0x104, 1, 0), 3)
|
||||
|
||||
@mock.patch("check_init_priorities.ZephyrInitLevels.__init__", return_value=None)
|
||||
def test_initlevel_pointer_64(self, mock_zilinit):
|
||||
obj = check_init_priorities.ZephyrInitLevels("")
|
||||
obj._elf = mock.Mock()
|
||||
obj._elf.elfclass = 64
|
||||
mock_section = mock.Mock()
|
||||
obj._elf.get_section.return_value = mock_section
|
||||
mock_section.header.sh_addr = 0x100
|
||||
mock_section.data.return_value = (b"\x01\x00\x00\x00\x00\x00\x00\x00"
|
||||
b"\x02\x00\x00\x00\x00\x00\x00\x00"
|
||||
b"\x03\x00\x00\x00\x00\x00\x00\x00")
|
||||
|
||||
self.assertEqual(obj._initlevel_pointer(0x100, 0, 0), 1)
|
||||
self.assertEqual(obj._initlevel_pointer(0x100, 1, 0), 2)
|
||||
self.assertEqual(obj._initlevel_pointer(0x108, 0, 0), 2)
|
||||
self.assertEqual(obj._initlevel_pointer(0x108, 1, 0), 3)
|
||||
|
||||
@mock.patch("check_init_priorities.ZephyrInitLevels._object_name")
|
||||
@mock.patch("check_init_priorities.ZephyrInitLevels._initlevel_pointer")
|
||||
@mock.patch("check_init_priorities.ZephyrInitLevels.__init__", return_value=None)
|
||||
def test_process_initlevels(self, mock_zilinit, mock_ip, mock_on):
|
||||
obj = check_init_priorities.ZephyrInitLevels("")
|
||||
obj._init_level_addr = {
|
||||
"EARLY": 0x00,
|
||||
"PRE_KERNEL_1": 0x00,
|
||||
"PRE_KERNEL_2": 0x00,
|
||||
"POST_KERNEL": 0x08,
|
||||
"APPLICATION": 0x0c,
|
||||
"SMP": 0x0c,
|
||||
}
|
||||
obj._init_level_end = 0x0c
|
||||
obj._objects = {
|
||||
0x00: ("a", 4, 0),
|
||||
0x04: ("b", 4, 0),
|
||||
0x08: ("c", 4, 0),
|
||||
}
|
||||
|
||||
self.assertEqual(obj._device_ord_from_rel({"r_info_sym": 0}), None)
|
||||
self.assertEqual(obj._device_ord_from_rel({"r_info_sym": 1}), None)
|
||||
self.assertEqual(obj._device_ord_from_rel({"r_info_sym": 2}), 123)
|
||||
mock_ip.side_effect = lambda *args: args
|
||||
|
||||
def mock_obj_name(*args):
|
||||
if args[0] == (0, 0, 0):
|
||||
return "i0"
|
||||
elif args[0] == (0, 1, 0):
|
||||
return "__device_dts_ord_11"
|
||||
elif args[0] == (4, 0, 0):
|
||||
return "i1"
|
||||
elif args[0] == (4, 1, 0):
|
||||
return "__device_dts_ord_22"
|
||||
return f"name_{args[0][0]}_{args[0][1]}"
|
||||
mock_on.side_effect = mock_obj_name
|
||||
|
||||
obj._process_initlevels()
|
||||
|
||||
self.assertDictEqual(obj.initlevels, {
|
||||
"EARLY": [],
|
||||
"PRE_KERNEL_1": [],
|
||||
"PRE_KERNEL_2": ["a: i0(__device_dts_ord_11)", "b: i1(__device_dts_ord_22)"],
|
||||
"POST_KERNEL": ["c: name_8_0(name_8_1)"],
|
||||
"APPLICATION": [],
|
||||
"SMP": [],
|
||||
})
|
||||
self.assertDictEqual(obj.devices, {
|
||||
11: check_init_priorities.Priority("PRE_KERNEL_2", 0),
|
||||
22: check_init_priorities.Priority("PRE_KERNEL_2", 1),
|
||||
})
|
||||
|
||||
class testValidator(unittest.TestCase):
|
||||
"""Tests for the Validator class."""
|
||||
|
||||
@mock.patch("check_init_priorities.ZephyrObjectFile")
|
||||
@mock.patch("check_init_priorities.Validator._find_build_objfiles")
|
||||
@mock.patch("check_init_priorities.ZephyrInitLevels")
|
||||
@mock.patch("pickle.load")
|
||||
def test_initialize(self, mock_pl, mock_fbo, mock_zof):
|
||||
mock_fbo.return_value = ["filepath"]
|
||||
|
||||
def test_initialize(self, mock_pl, mock_zil):
|
||||
mock_log = mock.Mock()
|
||||
mock_prio = mock.Mock()
|
||||
mock_obj = mock.Mock()
|
||||
mock_obj.defined_devices = {123: mock_prio}
|
||||
mock_zof.return_value = mock_obj
|
||||
mock_zil.return_value = mock_obj
|
||||
|
||||
with mock.patch("builtins.open", mock.mock_open()) as mock_open:
|
||||
validator = check_init_priorities.Validator("path", "pickle", mock_log)
|
||||
|
||||
self.assertListEqual(validator._objs, [mock_obj])
|
||||
self.assertDictEqual(validator._dev_priorities, {123: mock_prio})
|
||||
mock_fbo.assert_called_once_with("path", is_root=True)
|
||||
mock_zof.assert_called_once_with("filepath")
|
||||
mock_open.assert_called_once_with(pathlib.Path("path/pickle"), "rb")
|
||||
|
||||
@mock.patch("pathlib.Path")
|
||||
@mock.patch("check_init_priorities.Validator.__init__", return_value=None)
|
||||
def test_find_build_objfiles(self, mock_vinit, mock_path):
|
||||
mock_log = mock.Mock()
|
||||
|
||||
validator = check_init_priorities.Validator("", "", mock_log)
|
||||
|
||||
mock_file = mock.Mock()
|
||||
mock_file.is_file.return_value = True
|
||||
mock_file.is_dir.return_value = False
|
||||
mock_file.file.name = "filename.c.obj"
|
||||
|
||||
mock_dir = mock.Mock()
|
||||
mock_dir.is_file.return_value = False
|
||||
mock_dir.is_dir.return_value = True
|
||||
mock_dir.resolve.return_value = "subdir"
|
||||
mock_dir.iterdir.return_value = []
|
||||
|
||||
mock_nosettingsfile = mock.Mock()
|
||||
mock_nosettingsfile.exists.return_value = False
|
||||
|
||||
mock_path_root = mock.Mock()
|
||||
mock_path_root.iterdir.return_value = [mock_file, mock_dir]
|
||||
|
||||
def mock_path_stubs(*args):
|
||||
if args == ("root",):
|
||||
return mock_path_root
|
||||
elif args == ("subdir",):
|
||||
return mock_dir
|
||||
elif args == ("subdir", "CMakeCache.txt"):
|
||||
return mock_nosettingsfile
|
||||
raise ValueError
|
||||
mock_path.side_effect = mock_path_stubs
|
||||
|
||||
ret = list(validator._find_build_objfiles("root", is_root=True))
|
||||
self.assertListEqual(ret, [mock_file])
|
||||
|
||||
mock_nosettingsfile.exists.assert_called_once_with()
|
||||
self.assertListEqual(mock_path.call_args_list, [
|
||||
mock.call("root"),
|
||||
mock.call("subdir", "CMakeCache.txt"),
|
||||
mock.call("subdir")
|
||||
])
|
||||
self.assertEqual(validator._obj, mock_obj)
|
||||
mock_zil.assert_called_once_with("path")
|
||||
mock_open.assert_called_once_with(pathlib.Path("pickle"), "rb")
|
||||
|
||||
@mock.patch("check_init_priorities.Validator.__init__", return_value=None)
|
||||
def test_check_dep_same_node(self, mock_vinit):
|
||||
|
@ -220,15 +274,16 @@ class testValidator(unittest.TestCase):
|
|||
def test_check_dep_no_prio(self, mock_vinit):
|
||||
validator = check_init_priorities.Validator("", "", None)
|
||||
validator.log = mock.Mock()
|
||||
validator._obj = mock.Mock()
|
||||
|
||||
validator._ord2node = {1: mock.Mock(), 2: mock.Mock()}
|
||||
validator._ord2node[1]._binding = None
|
||||
validator._ord2node[2]._binding = None
|
||||
|
||||
validator._dev_priorities = {1: 10}
|
||||
validator._obj.devices = {1: 10}
|
||||
validator._check_dep(1, 2)
|
||||
|
||||
validator._dev_priorities = {2: 20}
|
||||
validator._obj.devices = {2: 20}
|
||||
validator._check_dep(1, 2)
|
||||
|
||||
self.assertFalse(validator.log.info.called)
|
||||
|
@ -239,6 +294,7 @@ class testValidator(unittest.TestCase):
|
|||
def test_check(self, mock_vinit):
|
||||
validator = check_init_priorities.Validator("", "", None)
|
||||
validator.log = mock.Mock()
|
||||
validator._obj = mock.Mock()
|
||||
validator.warnings = 0
|
||||
validator.errors = 0
|
||||
|
||||
|
@ -250,7 +306,7 @@ class testValidator(unittest.TestCase):
|
|||
validator._ord2node[3]._binding = None
|
||||
validator._ord2node[3].path = "/3"
|
||||
|
||||
validator._dev_priorities = {1: 10, 2: 10, 3: 20}
|
||||
validator._obj.devices = {1: 10, 2: 10, 3: 20}
|
||||
|
||||
validator._check_dep(3, 1)
|
||||
validator._check_dep(2, 1)
|
||||
|
@ -266,6 +322,7 @@ class testValidator(unittest.TestCase):
|
|||
def test_check_swapped(self, mock_vinit):
|
||||
validator = check_init_priorities.Validator("", "", None)
|
||||
validator.log = mock.Mock()
|
||||
validator._obj = mock.Mock()
|
||||
validator.warnings = 0
|
||||
validator.errors = 0
|
||||
|
||||
|
@ -279,7 +336,7 @@ class testValidator(unittest.TestCase):
|
|||
validator._ord2node[3]._binding.compatible = "compat-3"
|
||||
validator._ord2node[3].path = "/3"
|
||||
|
||||
validator._dev_priorities = {1: 20, 3: 10}
|
||||
validator._obj.devices = {1: 20, 3: 10}
|
||||
|
||||
validator._check_dep(3, 1)
|
||||
|
||||
|
@ -296,6 +353,7 @@ class testValidator(unittest.TestCase):
|
|||
def test_check_ignored(self, mock_vinit):
|
||||
validator = check_init_priorities.Validator("", "", None)
|
||||
validator.log = mock.Mock()
|
||||
validator._obj = mock.Mock()
|
||||
validator.warnings = 0
|
||||
validator.errors = 0
|
||||
|
||||
|
@ -309,7 +367,7 @@ class testValidator(unittest.TestCase):
|
|||
validator._ord2node[3]._binding.compatible = "compat-3"
|
||||
validator._ord2node[3].path = "/3"
|
||||
|
||||
validator._dev_priorities = {1: 20, 3: 10}
|
||||
validator._obj.devices = {1: 20, 3: 10}
|
||||
|
||||
validator._check_dep(3, 1)
|
||||
|
||||
|
@ -360,7 +418,8 @@ class testValidator(unittest.TestCase):
|
|||
def test_check_edt(self, mock_vinit, mock_cer):
|
||||
validator = check_init_priorities.Validator("", "", None)
|
||||
validator._ord2node = {1: mock.Mock(), 2: mock.Mock(), 3: mock.Mock()}
|
||||
validator._dev_priorities = {1: 10, 2: 10, 3: 20}
|
||||
validator._obj = mock.Mock()
|
||||
validator._obj.devices = {1: 10, 2: 10, 3: 20}
|
||||
|
||||
validator.check_edt()
|
||||
|
||||
|
|
|
@ -5,21 +5,20 @@ find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
|||
|
||||
set(output_file ${PROJECT_BINARY_DIR}/check_init_priorities_output.txt)
|
||||
|
||||
add_custom_target(
|
||||
check_init_priorities_output
|
||||
add_custom_command(
|
||||
COMMENT "Running check_init_priorities.py"
|
||||
OUTPUT ${output_file}
|
||||
DEPENDS ${BYPRODUCT_KERNEL_ELF_NAME}
|
||||
COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/build/check_init_priorities.py
|
||||
--verbose
|
||||
--build-dir ${PROJECT_BINARY_DIR}/..
|
||||
--elf-file=${ZEPHYR_BINARY_DIR}/${KERNEL_ELF_NAME}
|
||||
--output ${output_file}
|
||||
--always-succeed
|
||||
COMMAND ${PYTHON_EXECUTABLE} ${APPLICATION_SOURCE_DIR}/validate_check_init_priorities_output.py
|
||||
${output_file}
|
||||
DEPENDS zephyr_pre0
|
||||
)
|
||||
if (TARGET zephyr_pre1)
|
||||
add_dependencies(zephyr_pre1 check_init_priorities_output)
|
||||
endif()
|
||||
|
||||
add_custom_target(check_init_priorities_output ALL DEPENDS ${output_file})
|
||||
|
||||
project(check_init_priorities)
|
||||
|
||||
|
|
|
@ -8,12 +8,12 @@
|
|||
import sys
|
||||
|
||||
REFERENCE_OUTPUT = [
|
||||
"ERROR: /i2c@11112222/test-i2c-dev@10 PRE_KERNEL_1 49 31 < /gpio@ffff PRE_KERNEL_1 50 29",
|
||||
"ERROR: /i2c@11112222/test-i2c-dev@10 PRE_KERNEL_1 49 31 < /i2c@11112222 PRE_KERNEL_1 50 30",
|
||||
"INFO: /i2c@11112222/test-i2c-dev@11 PRE_KERNEL_1 50 32 > /gpio@ffff PRE_KERNEL_1 50 29",
|
||||
"INFO: /i2c@11112222/test-i2c-dev@11 PRE_KERNEL_1 50 32 > /i2c@11112222 PRE_KERNEL_1 50 30",
|
||||
"INFO: /i2c@11112222/test-i2c-dev@12 PRE_KERNEL_1 51 33 > /gpio@ffff PRE_KERNEL_1 50 29",
|
||||
"INFO: /i2c@11112222/test-i2c-dev@12 PRE_KERNEL_1 51 33 > /i2c@11112222 PRE_KERNEL_1 50 30"
|
||||
"ERROR: /i2c@11112222/test-i2c-dev@10 PRE_KERNEL_1 0 < /gpio@ffff PRE_KERNEL_1 1",
|
||||
"ERROR: /i2c@11112222/test-i2c-dev@10 PRE_KERNEL_1 0 < /i2c@11112222 PRE_KERNEL_1 2",
|
||||
"INFO: /i2c@11112222/test-i2c-dev@11 PRE_KERNEL_1 3 > /gpio@ffff PRE_KERNEL_1 1",
|
||||
"INFO: /i2c@11112222/test-i2c-dev@11 PRE_KERNEL_1 3 > /i2c@11112222 PRE_KERNEL_1 2",
|
||||
"INFO: /i2c@11112222/test-i2c-dev@12 PRE_KERNEL_1 4 > /gpio@ffff PRE_KERNEL_1 1",
|
||||
"INFO: /i2c@11112222/test-i2c-dev@12 PRE_KERNEL_1 4 > /i2c@11112222 PRE_KERNEL_1 2",
|
||||
]
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
|
@ -23,7 +23,7 @@ if len(sys.argv) != 2:
|
|||
output = []
|
||||
with open(sys.argv[1], "r") as file:
|
||||
for line in file:
|
||||
if line.startswith("INFO: check_init_priorities build_dir:"):
|
||||
if line.startswith("INFO: check_init_priorities"):
|
||||
continue
|
||||
output.append(line.strip())
|
||||
|
||||
|
|
Loading…
Reference in a new issue