diff options
Diffstat (limited to 'scripts')
| -rw-r--r-- | scripts/ci/parse_clang_tidy.py | 54 | ||||
| -rw-r--r-- | scripts/gdb/teachos.py | 47 | ||||
| -rw-r--r-- | scripts/gdb/teachos/__init__.py | 8 | ||||
| -rw-r--r-- | scripts/gdb/teachos/dump_mb2i.py | 241 | ||||
| -rw-r--r-- | scripts/gdb/toolchain.py | 35 | ||||
| -rwxr-xr-x | scripts/qemu-wrapper.sh | 30 |
6 files changed, 413 insertions, 2 deletions
diff --git a/scripts/ci/parse_clang_tidy.py b/scripts/ci/parse_clang_tidy.py new file mode 100644 index 0000000..ec51d77 --- /dev/null +++ b/scripts/ci/parse_clang_tidy.py @@ -0,0 +1,54 @@ +import hashlib +import json +import os +import re +import sys + + +def parse_clang_tidy(log_file_path: str) -> list[dict]: + issues = [] + pattern = re.compile( + r"^(.*?):(\d+):(\d+):\s+(warning|error|note):\s+(.*?)\s+\[(.*?)\]$" + ) + repo_root = os.environ.get("CI_PROJECT_DIR", os.getcwd()) + + with open(log_file_path, "r") as f: + for line in f: + match = pattern.match(line) + if not match: + continue + + path, line, column, severity, message, name = match.groups() + severity = "minor" if severity == "warning" else "major" + + try: + path = os.path.relpath(path, repo_root) + except ValueError: + pass + + fingerprint_data = f"{path}:{line}:{column}:{message}:{name}" + fingerprint = hashlib.sha256(fingerprint_data.encode()).hexdigest() + + issues.append( + { + "description": f"{message} ({name})", + "fingerprint": fingerprint, + "severity": severity, + "location": { + "path": path, + "lines": { + "begin": int(line), + }, + }, + } + ) + + return issues + + +if __name__ == "__main__": + if len(sys.argv) < 2: + sys.exit("Usage: python3 parse_clang_tidy.py <CLANG_TIDY_LOG_PATH>") + + issues = parse_clang_tidy(sys.argv[1]) + print(json.dumps(issues, indent=2)) diff --git a/scripts/gdb/teachos.py b/scripts/gdb/teachos.py new file mode 100644 index 0000000..355f6b9 --- /dev/null +++ b/scripts/gdb/teachos.py @@ -0,0 +1,47 @@ +import sys +import os +import gdb +import importlib.util + +script_path = os.path.abspath(__file__) +script_root = os.path.dirname(script_path) +repo_root = os.path.dirname(os.path.dirname(script_root)) + +if script_root not in sys.path: + sys.path.insert(0, script_root) + +from teachos.dump_mb2i import DumpMB2I + +components = { + "kstd": "libs/kstd/gdb", + "kapi": "kapi/gdb", +} + +for component, path in components.items(): + full_path = os.path.join(repo_root, *path.split("/")) + init_file = os.path.join(full_path, "__init__.py") + + if os.path.isfile(init_file): + try: + spec = importlib.util.spec_from_file_location(component, init_file) + module = importlib.util.module_from_spec(spec) + sys.modules[component] = module + spec.loader.exec_module(module) + + if hasattr(module, "register_printers"): + module.register_printers(gdb.current_objfile()) + gdb.write(f"Info: Registered pretty printers for '{component}'.\n") + else: + gdb.write( + f"Warning: '{component}' does not have 'register_printers' function\n" + ) + except Exception as e: + gdb.write(f"Warning: Failed to load '{component}' pretty printers: {e}\n") + else: + gdb.write(f"Warning: GDB extension init not found: '{init_file}'\n") + +gdb.write("Info: Loaded TeachOS pretty printers.\n") + +DumpMB2I() + +gdb.write("Info: Loaded TeachOS tools.\n")
\ No newline at end of file diff --git a/scripts/gdb/teachos/__init__.py b/scripts/gdb/teachos/__init__.py new file mode 100644 index 0000000..a5eca92 --- /dev/null +++ b/scripts/gdb/teachos/__init__.py @@ -0,0 +1,8 @@ +def format_size(size): + for unit in ["Bytes", "KiB", "MiB", "GiB", "TiB", "PiB"]: + if size < 1024.0: + if unit == "Bytes": + return f"{int(size)} {unit}" + return f"{size:.2f} {unit}" + size /= 1024.0 + return f"{size:.2f} PiB" diff --git a/scripts/gdb/teachos/dump_mb2i.py b/scripts/gdb/teachos/dump_mb2i.py new file mode 100644 index 0000000..0657ebd --- /dev/null +++ b/scripts/gdb/teachos/dump_mb2i.py @@ -0,0 +1,241 @@ +import gdb +import struct +from enum import IntEnum +from teachos import format_size + + +class TagType(IntEnum): + END = 0 + CMDLINE = 1 + BOOT_LOADER_NAME = 2 + MODULE = 3 + BASIC_MEMINFO = 4 + BOOTDEV = 5 + MMAP = 6 + VBE = 7 + FRAMEBUFFER = 8 + ELF_SECTIONS = 9 + APM = 10 + EFI32_SYSTEM_TABLE_POINTER = 11 + EFI64_SYSTEM_TABLE_POINTER = 12 + SMBIOS_TABLES = 13 + ACPI_OLD = 14 + ACPI_NEW = 15 + NETWORKING = 16 + EFI_MEMORY_MAP = 17 + EFI_BOOT_SERVICES_NOT_TERMINATED = 18 + EFI32_IMAGE_HANDLE_POINTER = 19 + EFI64_IMAGE_HANDLE_POINTER = 20 + LOAD_BASE_ADDR = 21 + + +class MemoryType(IntEnum): + AVAILABLE = 1 + RESERVED = 2 + ACPI_RECLAIM = 3 + NVS = 4 + BAD = 5 + + +class FramebufferType(IntEnum): + INDEXED = 0 + RGB = 1 + TEXT = 2 + + +TAG_NAMES = { + TagType.END: "End of Tags", + TagType.CMDLINE: "Boot Command Line", + TagType.BOOT_LOADER_NAME: "Boot Loader Name", + TagType.MODULE: "Boot Module", + TagType.BASIC_MEMINFO: "Basic Memory Information", + TagType.BOOTDEV: "BIOS Boot Device", + TagType.MMAP: "Memory Map", + TagType.VBE: "VBE Info", + TagType.FRAMEBUFFER: "Framebuffer Info", + TagType.ELF_SECTIONS: "ELF Symbols", + TagType.APM: "APM Table", + TagType.EFI32_SYSTEM_TABLE_POINTER: "EFI 32-bit System Table Pointer", + TagType.EFI64_SYSTEM_TABLE_POINTER: "EFI 64-bit System Table Pointer", + TagType.SMBIOS_TABLES: "SMBIOS Tables", + TagType.ACPI_OLD: "ACPI old RSDP (1.0)", + TagType.ACPI_NEW: "ACPI new RSDP (2.0+)", + TagType.NETWORKING: "Networking Information", + TagType.EFI_MEMORY_MAP: "EFI Memory Map", + TagType.EFI_BOOT_SERVICES_NOT_TERMINATED: "EFI Boot Services Not Terminated", + TagType.EFI32_IMAGE_HANDLE_POINTER: "EFI 32-bit Image Handle Pointer", + TagType.EFI64_IMAGE_HANDLE_POINTER: "EFI 64-bit Image Handle Pointer", + TagType.LOAD_BASE_ADDR: "Image Load Base Physical Address", +} + +MEMORY_TYPES = { + MemoryType.AVAILABLE: "Available", + MemoryType.RESERVED: "Reserved", + MemoryType.ACPI_RECLAIM: "ACPI Reclaim", + MemoryType.NVS: "Non-volatile storage", + MemoryType.BAD: "Bad Memory", +} + +FRAMEBUFFER_TYPES = { + FramebufferType.INDEXED: "Indexed Color", + FramebufferType.RGB: "RGB", + FramebufferType.TEXT: "Text Mode", +} + + +INDENT = f"{' '*11}" + + +class DumpMB2I(gdb.Command): + """ + Dump the information provided by the Multiboot2 loader. + Usage: dump_mb2i <address_expression> + Example: dump_mb2i $rbx + dump_mb2i 0x8000 + """ + + def __init__(self): + super(DumpMB2I, self).__init__("dump_mb2i", gdb.COMMAND_USER) + + def invoke(self, arg, from_tty): + if not arg: + gdb.write( + "Error: please provide the address of the Multiboot2 information structure.\n" + ) + return + + try: + address = int(gdb.parse_and_eval(arg)) + except gdb.error as e: + gdb.write(f"Error: Invalid address expression: {e}\n") + return + + inferior = gdb.selected_inferior() + + try: + header_data = inferior.read_memory(address, 8).tobytes() + total_size, reserved = struct.unpack("<II", header_data) + except gdb.MemoryError: + gdb.write(f"Error: Cannot read memory at {address:#018x}\n") + return + + if total_size < 8 or total_size > 1024 * 1024: + gdb.write( + f"Warning: suspicious total size ({total_size} bytes). Aborting.\n" + ) + return + + gdb.write(f"+{'-'*70}+\n") + gdb.write(f"| Multiboot2 Boot Information Payload{' ':>34}|\n") + gdb.write(f"+{'-'*70}+\n") + gdb.write(f"Base Address: {address:#018x}\nTotal Size: {total_size} Bytes\n\n") + + offset = 8 + + while offset < total_size: + tag_address = address + offset + + try: + tag_header = inferior.read_memory(tag_address, 8).tobytes() + tag_type, tag_size = struct.unpack("<II", tag_header) + except gdb.MemoryError: + gdb.write( + f"Error: Cannot read memory at {tag_address:#018x} (offset {offset})\n" + ) + break + + if tag_type == TagType.END and tag_size == 8: + break + + self._print_tag(inferior, tag_address, tag_type, tag_size) + + offset += (tag_size + 7) & ~7 + + def _print_tag(self, inferior, tag_address, tag_type, tag_size): + name = TAG_NAMES.get(tag_type, f"Unknown Tag") + size = format_size(tag_size) + payload = format_size(tag_size - 8) + gdb.write(f"[Tag {tag_type:#04x}] {name} (size: {size} | payload: {payload})\n") + + if tag_size <= 8 and tag_type != TagType.END: + return + + try: + data = inferior.read_memory(tag_address + 8, tag_size - 8).tobytes() + + handlers = { + TagType.CMDLINE: self._write_string_tag, + TagType.BOOT_LOADER_NAME: self._write_string_tag, + TagType.MODULE: self._write_module_tag, + TagType.BASIC_MEMINFO: self._write_basic_memory_info_tag, + TagType.BOOTDEV: self._write_boot_device_tag, + TagType.MMAP: self._write_memory_map_tag, + TagType.LOAD_BASE_ADDR: self._write_image_load_base_physical_address_tag, + TagType.FRAMEBUFFER: self._write_framebuffer_info_tag, + } + + handler = handlers.get(tag_type, self._write_unknown_tag) + handler(data) + + except Exception as e: + gdb.write( + f"{INDENT}<failed to decode tag: {e} ({tag_type:#04x} | {tag_size:#06x})>\n" + ) + + def _write_string_tag(self, data): + text = data.split(b"\x00")[0].decode("ascii", "replace") + gdb.write(f'{INDENT}"{text}"\n') + + def _write_module_tag(self, data): + start, end = struct.unpack_from("<II", data) + string = data[8:].split(b"\x00")[0].decode("ascii", "replace") + gdb.write( + f"{INDENT}start: {start:#010x} | end: {end:#010x} | size: {format_size(end - start)}\n" + ) + if string: + gdb.write(f'{INDENT}string: "{string}"\n') + + def _write_basic_memory_info_tag(self, data): + lower, upper = struct.unpack_from("<II", data) + gdb.write( + f"{INDENT}upper memory: {format_size(upper * 1024)} | lower memory: {format_size(lower * 1024)}\n" + ) + + def _write_boot_device_tag(self, data): + device, partition, sub_partition = struct.unpack_from("<III", data) + gdb.write( + f"{INDENT}device: {device:#04x} | partition: {partition:#06x} | sub-partition: {sub_partition:#06x}\n" + ) + + def _write_memory_map_tag(self, data): + entry_size, entry_version = struct.unpack_from("<II", data) + if entry_size < 24: + return + num_entries = (len(data) - 8) // entry_size + for i in range(num_entries): + entry_offset = 8 + i * entry_size + base, length, memory_type = struct.unpack_from("<QQI", data, entry_offset) + type_string = MEMORY_TYPES.get(memory_type, "Unknown") + gdb.write( + f"{INDENT}[{i:02d}] {base:#018x} - {base + length:#018x} | {format_size(length)} | type: {type_string}\n" + ) + + def _write_image_load_base_physical_address_tag(self, data): + base = struct.unpack("<I", data)[0] + gdb.write(f"{INDENT}address: {base:#010x}\n") + + def _write_framebuffer_info_tag(self, data): + address, pitch, width, height, bpp, type = struct.unpack_from("<QIIIBB", data) + type_string = FRAMEBUFFER_TYPES.get(type, "Unknown") + gdb.write(f"{INDENT}address: {address:#010x} | type: {type_string}\n") + gdb.write(f"{INDENT}depth: {bpp} | pitch: {format_size(pitch)}\n") + if type == FramebufferType.TEXT: + gdb.write(f"{INDENT}width: {width} chars | height: {height} chars\n") + else: + gdb.write(f"{INDENT}width: {width} px | height: {height} px\n") + + def _write_unknown_tag(self, data): + for i in range(0, len(data), 16): + chunk = data[i : i + 16] + hex_chunk = " ".join([f"{b:02x}" for b in chunk]) + gdb.write(f"{INDENT}{i:#010x}: {hex_chunk}\n") diff --git a/scripts/gdb/toolchain.py b/scripts/gdb/toolchain.py new file mode 100644 index 0000000..bbb7810 --- /dev/null +++ b/scripts/gdb/toolchain.py @@ -0,0 +1,35 @@ +import os +import subprocess +import sys + + +def setup_toolchain_debugging(): + try: + gcc_path = ( + subprocess.check_output(["which", "x86_64-pc-elf-g++"]) + .decode("utf-8") + .strip() + ) + python_dir = None + + share_path = os.path.join(os.path.dirname(os.path.dirname(gcc_path)), "share") + for root, dirs, _ in os.walk(share_path): + if "libstdcxx" in dirs: + python_dir = root + break + + if python_dir: + sys.path.insert(0, python_dir) + + from libstdcxx.v6.printers import register_libstdcxx_printers + from libstdcxx.v6.xmethods import register_libstdcxx_xmethods + + register_libstdcxx_printers(None) + register_libstdcxx_xmethods(None) + + print(f"Loaded Printers & Xmethods from: {python_dir}") + except Exception as e: + print(f"Debug setup failed: {e}") + + +setup_toolchain_debugging() diff --git a/scripts/qemu-wrapper.sh b/scripts/qemu-wrapper.sh index fa89ac5..dd0200d 100755 --- a/scripts/qemu-wrapper.sh +++ b/scripts/qemu-wrapper.sh @@ -1,4 +1,30 @@ #!/bin/bash +# scripts/qemu-wrapper.sh -echo "QEMU WRAPPER" -qemu-system-x86_64 $@ +WORKSPACE_DIR=${1:-.} +BUILD_TYPE=${2:-Debug} +ISO_PATH=$3 +IS_DEBUG=${4:-0} + +if [ -z "$ISO_PATH" ]; then + echo "Usage: $0 <workspace_dir> <build_type> <iso_path> [is_debug]" + exit 1 +fi + +ARGS=( + "-m" "64M" + "-machine" "q35" + "-smp" "4,sockets=1,cores=4,threads=1" + "-display" "curses" + "-debugcon" "file:${WORKSPACE_DIR}/qemu-debugcon-${BUILD_TYPE}.log" + "-cdrom" "${ISO_PATH}" +) + +if [ "$IS_DEBUG" == "1" ]; then + ARGS=( "-s" "-no-reboot" "-d" "int,cpu_reset" "${ARGS[@]}" ) +fi + +echo "QEMU WRAPPER: Executing TeachOS" +echo "Arguments: ${ARGS[@]}" + +qemu-system-x86_64 "${ARGS[@]}" 2> "${WORKSPACE_DIR}/qemu-stderr-${BUILD_TYPE}.log" |
