From 58de01cb521ba5f90aa6a6955cc8b9536dce7bb5 Mon Sep 17 00:00:00 2001 From: Stephen Brennan Date: Thu, 19 Dec 2024 14:37:05 -0800 Subject: [PATCH] tests: linux_kernel: test built-in ORC unwinding Unwinding through a kernel module with no DWARF info would have been impossible without support of built-in ORC. Further, the tests here would not pass without 329bd5da ("Make StackFrame.name fall back to symbol/PC and add StackFrame.function_name"). Signed-off-by: Stephen Brennan --- tests/linux_kernel/test_stack_trace.py | 53 +++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/tests/linux_kernel/test_stack_trace.py b/tests/linux_kernel/test_stack_trace.py index d414306a9..cd9e1512d 100644 --- a/tests/linux_kernel/test_stack_trace.py +++ b/tests/linux_kernel/test_stack_trace.py @@ -1,11 +1,14 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. # SPDX-License-Identifier: LGPL-2.1-or-later +import logging import os +import re import unittest from _drgn_util.platform import NORMALIZED_MACHINE_NAME -from drgn import Object, Program, reinterpret +from drgn import MissingDebugInfoError, Object, Program, reinterpret +from drgn.helpers.linux import load_module_kallsyms from tests import assertReprPrettyEqualsStr, modifyenv from tests.linux_kernel import ( LinuxKernelTestCase, @@ -59,6 +62,54 @@ def test_by_pid_dwarf(self): def test_by_pid_orc(self): self._test_by_pid(True) + @unittest.skipUnless( + NORMALIZED_MACHINE_NAME == "x86_64", + f"{NORMALIZED_MACHINE_NAME} does not use ORC", + ) + @skip_unless_have_test_kmod + def test_by_pid_builtin_orc(self): + # ORC was introduced in kernel 4.14. Detect the presence of ORC or skip + # the test. + try: + self.prog.symbol("__start_orc_unwind") + except LookupError: + ver = self.prog["UTS_RELEASE"].string_().decode() + self.skipTest(f"ORC is not available for {ver}") + + # Create a program with the core kernel debuginfo loaded, + # but without module debuginfo. Load a symbol finder using + # kallsyms so that the module's stack traces can still have + # usable frame names. + prog = Program() + prog.set_kernel() + try: + prog.load_default_debug_info() + except MissingDebugInfoError: + pass + kallsyms = load_module_kallsyms(prog) + prog.register_symbol_finder("module_kallsyms", kallsyms, enable_index=1) + for thread in prog.threads(): + if b"drgn_test_kthread".startswith(thread.object.comm.string_()): + pid = thread.tid + break + else: + self.fail("couldn't find drgn_test_kthread") + with self.assertLogs("drgn", logging.DEBUG) as log: + self._test_drgn_test_kthread_trace(prog.stack_trace(pid)) + + # To be sure that we actually used ORC to unwind through the drgn_test + # stack frames, search for the log output. We don't know which ORC + # version is used, so just ensure that we have a log line that mentions + # loading ORC. + expr = re.compile( + r"DEBUG:drgn:Loaded built-in ORC \(v\d+\) for module drgn_test" + ) + for line in log.output: + if expr.fullmatch(line): + break + else: + self.fail("Did not load built-in ORC for drgn_test") + @skip_unless_have_test_kmod def test_by_pt_regs(self): pt_regs = self.prog["drgn_test_kthread_pt_regs"]