-
Notifications
You must be signed in to change notification settings - Fork 45
Implement backtracing tool as a component #328
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
0aids
wants to merge
27
commits into
main
Choose a base branch
from
backtrace_testing
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
8e088ef
weird ass linking problems sort of fixed
0aids 295d6d0
backtracing works
0aids 49178b0
fix build for libunwind and child backtracing working
0aids 5f893b0
wip meta.py functions and addr inserting
0aids d9d5676
wip meta.py for mapping functions of backtracing
0aids 581cd0c
wip got multi-child fault and backtracing partially working
0aids bec9681
implemented multi-child PD fault backtracing
0aids 66dcec7
fixed incorrect alignment and unnecessary channel ID incrementing
0aids 496f5e9
cleaned up backtrace printing
0aids ae84197
feat: added backtrace component and works with backtrace example
0aids f86abeb
fix: backtraces not showing and cleaned up makefiles
0aids 02c6ffb
chore: removed unnecessary files from backtrace_test example
0aids c64d3e0
feat: separate out backtracer related python functions into component
0aids ef889b4
fix: hardcoded paths in backtracing python tool and LDFLAGS manipulat…
0aids 6133d9a
chore: some more docs
0aids 3f45c1c
fix: llvm as submodule
0aids 739aa5d
chore: remove llvm-project as a submodule in favour of wget
0aids 23bb77d
backtracer/docs: more information on backtracer usage and deps
0aids 7026586
chore: formatting
0aids 8ed5aad
fix: missing include in monitor
0aids c21a6bb
chore: clean up makefiles with dependency tracking
0aids 426f71e
chore: clean up python files
0aids 694cfe1
chore: clean up c files and licensing
0aids 31c8773
chore: remove unused vars and better names
0aids 96e4629
chore: add relevant licensing
0aids c08a4a8
chore: use updated sddf for CXX var in make
0aids 919a5d4
chore: clean up excess headers
0aids File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| # Copyright 2026, UNSW | ||
| # SPDX-License-Identifier: BSD-2-Clause | ||
| import argparse | ||
| from dataclasses import dataclass | ||
| from typing import List | ||
| from sdfgen import SystemDescription, Sddf, DeviceTree, LionsOs | ||
| from importlib.metadata import version | ||
| from board import BOARDS | ||
| from subprocess import run | ||
| from copy import deepcopy | ||
|
|
||
| # assert version("sdfgen").split(".")[1] == "28", "Unexpected sdfgen version" | ||
|
|
||
| ProtectionDomain = SystemDescription.ProtectionDomain | ||
| ProtectionDomain.PRIORITY_MAX = 254 | ||
|
|
||
| MemoryRegion = SystemDescription.MemoryRegion | ||
| Map = SystemDescription.Map | ||
| Channel = SystemDescription.Channel | ||
|
|
||
|
|
||
| def get_architecture_pointer_alignment(arch: SystemDescription.Arch): | ||
| match arch: | ||
| case ( | ||
| SystemDescription.Arch.AARCH64 | ||
| | SystemDescription.Arch.RISCV64 | ||
| | SystemDescription.Arch.X86_64 | ||
| ): | ||
| return 8 | ||
| case ( | ||
| SystemDescription.Arch.AARCH32 | ||
| | SystemDescription.Arch.RISCV32 | ||
| | SystemDescription.Arch.X86 | ||
| ): | ||
| return 4 | ||
| case _: | ||
| raise Exception(f"Alignment of architecture {arch} is unknown.") | ||
|
|
||
|
|
||
| def enable_backtracing( | ||
| sdf, arch, array_of_pds_or_single_pd, *, show_backtrace_func_list_addr=0xB00000, backtracer_stack_size=0x10000, | ||
| pd_callback_channel_id=61, endianness="little" | ||
| ): | ||
| """ | ||
| Wrap an array or single pd as children into a backtracer parent, | ||
| capable of catching faults and then forcing prints of the backtrace | ||
| Remember to compile each of the children with "backtrace.o" | ||
| """ | ||
| backtracer = ProtectionDomain( | ||
| "backtracer", | ||
| "backtracer.elf", | ||
| priority=ProtectionDomain.PRIORITY_MAX, | ||
| stack_size=backtracer_stack_size, | ||
| ) | ||
|
|
||
| pd_elf_paths = [] | ||
| if not isinstance(array_of_pds_or_single_pd, list): | ||
| array_of_pds_or_single_pd = [array_of_pds_or_single_pd] | ||
|
|
||
| for i, child_pd in enumerate(array_of_pds_or_single_pd): | ||
| # Also add a channel for allowing a thread to pause completely | ||
| newChannel = Channel( | ||
| child_pd, | ||
| backtracer, | ||
| a_id=pd_callback_channel_id, | ||
| b_id=i, | ||
| pp_a=True, | ||
| pd_a_setvar_id="unwind_helper_channel_to_backtracer", | ||
| ) | ||
| sdf.add_channel(newChannel) | ||
| backtracer.add_child_pd(child_pd) | ||
| pd_elf_paths.append(child_pd.program_image) | ||
|
|
||
| # Create a memory region at the predefined address, as an array | ||
| # Extract each of the addresses of the children's show_backtrace function | ||
| pd_show_backtrace_addrs = [] | ||
| for elf_path in pd_elf_paths: | ||
| # Not very stable but good enough for now. | ||
| shell_output = run( | ||
| f'set -o pipefail && nm {elf_path} | grep "show_backtrace" | cut --delimiter=" " -f 1', | ||
| capture_output=True, | ||
| shell=True, | ||
| text=True, | ||
| ) | ||
| if shell_output.returncode != 0: | ||
| raise Exception( | ||
| f"Failed to get addresses of 'show_backtrace' for file {elf_path}\n" | ||
| + f"stderr: {shell_output.stderr}\n" | ||
| + f"stdout: {shell_output.stdout}\n" | ||
| + f"exit code: {shell_output.returncode}\n" | ||
| ) | ||
| show_backtrace_addr = int(shell_output.stdout.strip(), 16) | ||
| print(f"'{elf_path}':show_backtrace @ {hex(show_backtrace_addr)}") | ||
| pd_show_backtrace_addrs.append(show_backtrace_addr) | ||
|
|
||
| # now write a .data file containing the files spaced out by architectures pointer size. | ||
| alignment = get_architecture_pointer_alignment(arch) | ||
| print(f"Alignment for architecture {arch.name}: {alignment}") | ||
| frame = b"" | ||
| for backtrace_addr in pd_show_backtrace_addrs: | ||
| frame += bytes(backtrace_addr.to_bytes(alignment, endianness)) | ||
| BACKTRACER_FUNCTION_DATA_PATH = "backtrace_functions.data" | ||
| with open(BACKTRACER_FUNCTION_DATA_PATH, "wb") as dataFile: | ||
| dataFile.write(frame) | ||
| func_list_mr = MemoryRegion( | ||
| sdf, "backtracerFunctions", prefill_path=BACKTRACER_FUNCTION_DATA_PATH | ||
| ) | ||
| sdf.add_mr(func_list_mr) | ||
| func_list_map = Map( | ||
| func_list_mr, vaddr=0x20000, perms="r", setvar_vaddr="backtraceFunctions" | ||
| ) | ||
| backtracer.add_map(func_list_map) | ||
| return backtracer |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| <!-- | ||
| Copyright 2024, UNSW | ||
| SPDX-License-Identifier: CC-BY-SA-4.0 | ||
| --> | ||
| # Usage | ||
| 1. Include `backtracer.mk` file. | ||
| 2. For all targets to be backtraced: | ||
| 1. Add `libunwind.a` and `unwind_helpers.o` as targets and add them to be compiled with the target. | ||
| 3. Add `-funwind-tables` to `CFLAGS` | ||
| 4. Add `--eh-frame-hdr -L{Directory of backtracer}` to `LDFLAGS` | ||
| 5. Add `-Tunwind.ld -lunwind` to `LIBS` | ||
| 6. Optionally add `--start-group` and `--end-group` to the beginning and end of `LIBS` respectively. | ||
| 3. For `meta.py`: | ||
| 1. Make sure to add the path to this directory in to `PYTHONPATH` | ||
| 2. Import `LionsOS_Backtracer` | ||
| 3. For all PDs to be traced, add them to a list and pass them to the `enable_backtracing` function. | ||
| This function returns a parent PD containing all of the children given. | ||
| The function prototype is `enable_backtracing(sdf, architecture, PD_list) -> PD` | ||
|
|
||
| See the `backtrace_test` example for more details. | ||
|
|
||
| # Dependencies | ||
| - `libc` for `libunwind` | ||
| - llvm-project's `libunwind` | ||
| - `sddf` or some implementation of `printf` | ||
| - patched `sdfgen` with Memory region prefilling capabilities (updated sdfgen) | ||
|
|
||
| # Cons | ||
| - Larger binary sizes | ||
| - Depending on `llvm-project` (sorry) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| /* | ||
| * Copyright 2025, UNSW | ||
| * SPDX-License-Identifier: BSD-2-Clause | ||
| */ | ||
| #include "monitor.h" | ||
| #include <microkit.h> | ||
| #include <sddf/util/printf.h> | ||
|
|
||
| void (**backtraceFunctions)() = NULL; | ||
|
|
||
| void init() | ||
| { | ||
| LOG("Backtracer initialised!\n"); | ||
| LOG("Backtracer table starting address: %p\n", backtraceFunctions); | ||
| } | ||
|
|
||
| #if defined(__aarch64__) | ||
| static void callConvention_prologue(seL4_UserContext *ctxt, uintptr_t funcAddr) | ||
| { | ||
| // Set the link register to old PC | ||
| ctxt->x30 = ctxt->pc; | ||
| // Set the PC to the next function | ||
| ctxt->pc = funcAddr; | ||
| LOG("Old PC: %p, New PC: %p\n", (void *)ctxt->x30, (void *)funcAddr); | ||
| } | ||
| #elif defined(__riscv__) | ||
| #error "Unimplemented backtracer for riscv" | ||
| #elif defined(__x86_64__) | ||
| #error "Unimplemented backtracer for x86_64" | ||
| #else | ||
| #error "Unsupported architecture for backtracing" | ||
| #endif | ||
|
|
||
| seL4_Bool fault(microkit_child child, microkit_msginfo msginfo, microkit_msginfo *reply_msginfo) | ||
| { | ||
| LOG("Child '%d' Faulted!\n", child); | ||
| print_fault_error(child, msginfo); | ||
| seL4_UserContext ctxt = { 0 }; | ||
| int readRegResult = seL4_TCB_ReadRegisters(BASE_TCB_CAP + child, seL4_True, 0, | ||
| sizeof(seL4_UserContext) / sizeof(seL4_Word), &ctxt); | ||
| if (readRegResult != 0) { | ||
| LOG("Failed to read registers for setting up backtrace jump! Got %d, " | ||
| "expected %d\n", | ||
| readRegResult, 0); | ||
| return seL4_False; | ||
| } | ||
| print_tcb_registers(&ctxt); | ||
| callConvention_prologue(&ctxt, (uintptr_t)(backtraceFunctions[child])); | ||
| int writeRegResult = seL4_TCB_WriteRegisters(BASE_TCB_CAP + child, seL4_True, 0, | ||
| sizeof(seL4_UserContext) / sizeof(seL4_Word), &ctxt); | ||
| if (writeRegResult != 0) { | ||
| LOG("Failed to write registers for setting up backtrace jump! Got error value %d, " | ||
| "expected %d\n", | ||
| writeRegResult, 0); | ||
| return seL4_False; | ||
| } | ||
| return seL4_True; | ||
| } | ||
|
|
||
| void notified(microkit_channel ch) | ||
| { | ||
| } | ||
| microkit_msginfo protected(microkit_channel ch, microkit_msginfo msginfo) | ||
| { | ||
| microkit_pd_stop(ch); | ||
| return msginfo; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| # | ||
| # Copyright 2026, UNSW | ||
| # | ||
| # SPDX-License-Identifier: BSD-2-Clause | ||
| # | ||
| TAR ?= tar | ||
|
|
||
| BACKTRACER_DIR := $(LIONSOS)/components/backtracer | ||
| LLVM := llvm-project-22.1.8.src | ||
| LLVM_TAR := llvm-project-22.1.8.src.tar.xz | ||
| LLVM_URL := https://github.com/llvm/llvm-project/releases/download/llvmorg-22.1.8/llvm-project-22.1.8.src.tar.xz | ||
| LIBUNWIND := $(LLVM)/libunwind | ||
| LIBUNWIND_BUILD_DIR := ./libunwind | ||
|
|
||
| CFLAGS_backtracer := \ | ||
| $(CFLAGS)\ | ||
| -I$(LIONSOS)/include \ | ||
| -I$(SDDF)/include \ | ||
| -I$(SDDF)/include/microkit \ | ||
| -I$(LIBUNWIND)/include \ | ||
| -I$(BOARD_DIR)/include \ | ||
| -I$(LIONS_LIBC)/include -funwind-tables | ||
|
|
||
| LDFLAGS_backtracer := -L$(BOARD_DIR)/lib -L$(LIONS_LIBC)/lib | ||
| LIBS_backtracer := -lmicrokit -Tmicrokit.ld libsddf_util_debug.a -lc | ||
|
|
||
| LLVM_CMAKE_FLAGS := \ | ||
| -DLLVM_ENABLE_RUNTIMES=libunwind\ | ||
| -DCMAKE_SYSTEM_NAME=Generic\ | ||
| -DCMAKE_C_COMPILER_TARGET=$(TARGET)\ | ||
| -DCMAKE_CXX_COMPILER_TARGET=$(TARGET)\ | ||
| -DCMAKE_ASM_COMPILER_TARGET=$(TARGET)\ | ||
| -DLIBUNWIND_IS_BAREMETAL=ON\ | ||
| -DLIBUNWIND_ENABLE_SHARED=OFF\ | ||
| -DLIBUNWIND_ENABLE_THREADS=OFF\ | ||
| -DLIBUNWIND_USE_COMPILER_RT=ON\ | ||
| -DLIBUNWIND_ENABLE_PEDANTIC=OFF\ | ||
| -DLIBUNWIND_ENABLE_ASSERTIONS=OFF\ | ||
| -DCMAKE_BUILD_TYPE=Debug\ | ||
| -DLIBUNWIND_ENABLE_STATIC=ON\ | ||
| -DCMAKE_C_COMPILER=$(CC)\ | ||
| -DCMAKE_CXX_COMPILER=$(CXX)\ | ||
| -DCMAKE_ASM_COMPILER=$(CC)\ | ||
| -DCMAKE_C_FLAGS="-I$(LIONS_LIBC)/include"\ | ||
| -DCMAKE_CXX_FLAGS="-I$(LIONS_LIBC)/include -fno-exceptions"\ | ||
| -DCMAKE_C_COMPILER_WORKS=ON\ | ||
| -DCMAKE_CXX_COMPILER_WORKS=ON\ | ||
| -DCMAKE_ASM_COMPILER_WORKS=ON | ||
|
|
||
| backtracer: | ||
| mkdir -p $@ | ||
|
|
||
| unwind_helpers.o: $(BACKTRACER_DIR)/unwind_helpers.c | $(LIONS_LIBC)/include backtracer $(LLVM) | ||
| ${CC} ${CFLAGS_backtracer} -c -o $@ $< | ||
|
|
||
| backtracer/backtracer.o: $(BACKTRACER_DIR)/backtracer.c | $(LIONS_LIBC)/include backtracer | ||
| ${CC} ${CFLAGS_backtracer} -c -o $@ $< | ||
|
|
||
| backtracer.elf: backtracer/backtracer.o libunwind.a | backtracer | ||
| ${LD} ${LDFLAGS_backtracer} -o $@ $^ ${LIBS_backtracer} | ||
|
|
||
| $(LLVM_TAR): | ||
| wget $(LLVM_URL) | ||
|
|
||
| $(LLVM): $(LLVM_TAR) | ||
| ${TAR} xvf $< $@/{libunwind,runtimes,cmake,utils,third-party} $@/llvm/{cmake,utils} | ||
|
|
||
| $(LIBUNWIND_BUILD_DIR): | $(LLVM) $(LIONS_LIBC)/include | ||
| cmake -B $(LIBUNWIND_BUILD_DIR) -S $(LLVM)/runtimes \ | ||
| $(LLVM_CMAKE_FLAGS) | ||
|
|
||
| libunwind.a: | $(LIONS_LIBC)/include $(LIBUNWIND_BUILD_DIR) | ||
| ${MAKE} -C $(LIBUNWIND_BUILD_DIR) | ||
| cp $(LIBUNWIND_BUILD_DIR)/lib/libunwind.a $@ | ||
|
|
||
| clean:: | ||
| ${RM} -rf backtracer backtracer.elf unwind_helpers.o libunwind.a libunwind | ||
|
|
||
| -include unwind_helpers.d backtrace/backtracer.d | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.