zephyr/scripts/build/check_init_priorities_test.py
Fabio Baltieri 5212a4c619 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>
2023-09-20 20:24:46 +01:00

434 lines
16 KiB
Python
Executable File

#!/usr/bin/env python3
# Copyright 2023 Google LLC
# SPDX-License-Identifier: Apache-2.0
"""
Tests for check_init_priorities
"""
import mock
import pathlib
import unittest
from elftools.elf.relocation import Section
from elftools.elf.sections import SymbolTableSection
import check_init_priorities
class TestPriority(unittest.TestCase):
"""Tests for the Priority class."""
def test_priority_parsing(self):
prio1 = check_init_priorities.Priority("POST_KERNEL", 12)
self.assertEqual(prio1._level_priority, (3, 12))
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", 0)
check_init_priorities.Priority("_DOESNOTEXIST0_", 0)
def test_priority_levels(self):
prios = [
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("POST_KERNEL", 12)
self.assertEqual(str(prio), "POST_KERNEL 12")
self.assertEqual(repr(prio), "<Priority POST_KERNEL 12>")
class testZephyrInitLevels(unittest.TestCase):
"""Tests for the ZephyrInitLevels class."""
@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=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.ZephyrInitLevels("")
obj._elf = mock_elf
obj._load_objects()
self.assertDictEqual(obj._objects, {0xaa: ("a", 4, 1), 0xbb: ("b", 8, 2)})
@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)
rel = mock.Mock(spec=Section)
mock_elf.iter_sections.return_value = [sts, rel]
s0 = mock.Mock()
s0.name = "__init_EARLY_start"
s0.entry.st_value = 0x00
s1 = mock.Mock()
s1.name = "__init_PRE_KERNEL_1_start"
s1.entry.st_value = 0x11
s2 = mock.Mock()
s2.name = "__init_PRE_KERNEL_2_start"
s2.entry.st_value = 0x22
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._load_level_addr()
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.ZephyrInitLevels.__init__", return_value=None)
def test_device_ord_from_name(self, mock_zilinit):
obj = check_init_priorities.ZephyrInitLevels("")
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),
}
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.ZephyrInitLevels")
@mock.patch("pickle.load")
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_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.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):
validator = check_init_priorities.Validator("", "", None)
validator.log = mock.Mock()
validator._check_dep(123, 123)
self.assertFalse(validator.log.info.called)
self.assertFalse(validator.log.warning.called)
self.assertFalse(validator.log.error.called)
@mock.patch("check_init_priorities.Validator.__init__", return_value=None)
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._obj.devices = {1: 10}
validator._check_dep(1, 2)
validator._obj.devices = {2: 20}
validator._check_dep(1, 2)
self.assertFalse(validator.log.info.called)
self.assertFalse(validator.log.warning.called)
self.assertFalse(validator.log.error.called)
@mock.patch("check_init_priorities.Validator.__init__", return_value=None)
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
validator._ord2node = {1: mock.Mock(), 2: mock.Mock(), 3: mock.Mock()}
validator._ord2node[1]._binding = None
validator._ord2node[1].path = "/1"
validator._ord2node[2]._binding = None
validator._ord2node[2].path = "/2"
validator._ord2node[3]._binding = None
validator._ord2node[3].path = "/3"
validator._obj.devices = {1: 10, 2: 10, 3: 20}
validator._check_dep(3, 1)
validator._check_dep(2, 1)
validator._check_dep(1, 3)
validator.log.info.assert_called_once_with("/3 20 > /1 10")
validator.log.warning.assert_called_once_with("/2 10 == /1 10")
validator.log.error.assert_called_once_with("/1 10 < /3 20")
self.assertEqual(validator.warnings, 1)
self.assertEqual(validator.errors, 1)
@mock.patch("check_init_priorities.Validator.__init__", return_value=None)
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
save_inverted_priorities = check_init_priorities._INVERTED_PRIORITY_COMPATIBLES
check_init_priorities._INVERTED_PRIORITY_COMPATIBLES = set([("compat-3", "compat-1")])
validator._ord2node = {1: mock.Mock(), 3: mock.Mock()}
validator._ord2node[1]._binding.compatible = "compat-1"
validator._ord2node[1].path = "/1"
validator._ord2node[3]._binding.compatible = "compat-3"
validator._ord2node[3].path = "/3"
validator._obj.devices = {1: 20, 3: 10}
validator._check_dep(3, 1)
self.assertListEqual(validator.log.info.call_args_list, [
mock.call("Swapped priority: compat-3, compat-1"),
mock.call("/3 20 > /1 10"),
])
self.assertEqual(validator.warnings, 0)
self.assertEqual(validator.errors, 0)
check_init_priorities._INVERTED_PRIORITY_COMPATIBLES = save_inverted_priorities
@mock.patch("check_init_priorities.Validator.__init__", return_value=None)
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
save_ignore_compatibles = check_init_priorities._IGNORE_COMPATIBLES
check_init_priorities._IGNORE_COMPATIBLES = set(["compat-3"])
validator._ord2node = {1: mock.Mock(), 3: mock.Mock()}
validator._ord2node[1]._binding.compatible = "compat-1"
validator._ord2node[1].path = "/1"
validator._ord2node[3]._binding.compatible = "compat-3"
validator._ord2node[3].path = "/3"
validator._obj.devices = {1: 20, 3: 10}
validator._check_dep(3, 1)
self.assertListEqual(validator.log.info.call_args_list, [
mock.call("Ignoring priority: compat-3"),
])
self.assertEqual(validator.warnings, 0)
self.assertEqual(validator.errors, 0)
check_init_priorities._IGNORE_COMPATIBLES = save_ignore_compatibles
@mock.patch("check_init_priorities.Validator._check_dep")
@mock.patch("check_init_priorities.Validator.__init__", return_value=None)
def test_check_edt_r(self, mock_vinit, mock_cd):
validator = check_init_priorities.Validator("", "", None)
d0 = mock.Mock()
d0.dep_ordinal = 1
d1 = mock.Mock()
d1.dep_ordinal = 2
c0 = mock.Mock()
c0.props = {"compatible": "c"}
c1 = mock.Mock()
c1.props = {}
c1._binding.path = "another-binding-path.yaml"
c2 = mock.Mock()
c2.props = {}
c2._binding.path = "binding-path.yaml"
c2._binding.child_binding = None
c2.depends_on = [d1]
dev = mock.Mock()
dev.depends_on = [d0]
dev._binding.child_binding = "child-binding"
dev._binding.path = "binding-path.yaml"
dev.children.values.return_value = [c0, c1, c2]
validator._check_edt_r(0, dev)
self.assertListEqual(mock_cd.call_args_list, [
mock.call(0, 1),
mock.call(0, 2),
])
@mock.patch("check_init_priorities.Validator._check_edt_r")
@mock.patch("check_init_priorities.Validator.__init__", return_value=None)
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._obj = mock.Mock()
validator._obj.devices = {1: 10, 2: 10, 3: 20}
validator.check_edt()
self.assertListEqual(mock_cer.call_args_list, [
mock.call(1, validator._ord2node[1]),
mock.call(2, validator._ord2node[2]),
mock.call(3, validator._ord2node[3]),
])
if __name__ == "__main__":
unittest.main()