diff --git a/docs/introduction/build_system.md b/docs/introduction/build_system.md index e5c087e4e..986ff7dad 100644 --- a/docs/introduction/build_system.md +++ b/docs/introduction/build_system.md @@ -84,6 +84,7 @@ bash bootstrap.sh # 这里请不要加上sudo, 因为需要安装的开发依 - lsb-release - git - dosfstools +- unzip - Rust以及其工具链 **请留意,若您的Linux系统是在虚拟机中运行的,还请您在您的VMware/Virtual Box虚拟机的处理器设置选项卡中,开启Intel VT-x或AMD-V选项,否则,DragonOS将无法运行。** diff --git a/docs/kernel/debug/debug-kernel-with-gdb.md b/docs/kernel/debug/debug-kernel-with-gdb.md new file mode 100644 index 000000000..a6266e6e1 --- /dev/null +++ b/docs/kernel/debug/debug-kernel-with-gdb.md @@ -0,0 +1,359 @@ + +# 如何使用GDB调试内核 + +## 前言 +  GDB是一个功能强大的开源调试工具,能够帮助您更好的诊断和修复程序中的错误。 +  它提供了一套丰富的功能,使您能够检查程序的执行状态、跟踪代码的执行流程、查看和修改变量的值、分析内存状态等。它可以与编译器配合使用,以便您在调试过程中访问程序的调试信息。 + +  此教程将告诉您如何在DragonOS中使用`rust-gdb`来调试内核,包括如何开始调试以及相应的调试命令。 + +:::{note} +如果您已经熟悉了`rust-gdb`的各种命令,那您只需要阅读此教程的第一部分即可。 +::: + +--- +## 1.从何开始 + +### 1.1 准备工作 + +  在您开始调试内核之前,需要在/Kernel/Cargo.toml中开启调试模式,将Cargo.toml中的`debug = false`更改为`debug = true`。 + +```shell +debug = false +``` +  **更改为** +```shell +debug = true +``` + +### 1.2 运行DragonOS + +  准备工作完成后,您就可以编译、运行DragonOS来开展后续的调试工作了。 +  在DragonOS根目录中开启终端,使用`make run`即可开始编译运行DragonOS,如需更多编译命令方面的帮助,详见 +> [构建DragonOS](https://docs.dragonos.org/zh_CN/latest/introduction/build_system.html)。 + +### 1.3 运行GDB +  当DragonOS开始运行后,您就可以启动GDB开始调试了。 + +  **您只需要开启一个新的终端,运行`make gdb`即可运行GDB调试器。** + +```shell +❯ make gdb +rust-gdb -n -x tools/.gdbinit +GNU gdb (Ubuntu 12.1-0ubuntu1~22.04) 12.1 +Copyright (C) 2022 Free Software Foundation, Inc. +License GPLv3+: GNU GPL version 3 or later +This is free software: you are free to change and redistribute it. +There is NO WARRANTY, to the extent permitted by law. +Type "show copying" and "show warranty" for details. +This GDB was configured as "x86_64-linux-gnu". +Type "show configuration" for configuration details. +For bug reporting instructions, please see: +. +Find the GDB manual and other documentation resources online at: + . + +--Type for more, q to quit, c to continue without paging-- +``` + +:::{note} +若出现以上信息,输入c再回车即可。 +::: + +--- + +## 2.调试 + +### 2.1 开始 + +  当以上步骤完成后,就已经可以开始调试了。 + +```shell +For help, type "help". +Type "apropos word" to search for commands related to "word". +warning: No executable has been specified and target does not support +determining executable automatically. Try using the "file" command. +0xffff8000001f8f63 in ?? () +(gdb) +``` + +:::{note} +GDB输出的信息中`0xffff8000001f8f63 in ?? ()`表明DragonOS还在引导加载的过程中。 +::: + +  **输入`continue`或者`c`,程序将继续执行。** + +```shell +For help, type "help". +Type "apropos word" to search for commands related to "word". +warning: No executable has been specified and target does not support +determining executable automatically. Try using the "file" command. +0xffff8000001f8f63 in ?? () +(gdb) continue +Continuing. +``` + +  在DragonOS运行时,您可以随时按下`Ctrl+C`来发送中断信息。来查看内核当前状态。 + +```shell +(gdb) continue +Continuing. +^C +Thread 1 received signal SIGINT, Interrupt. +0xffff800000140c21 in io_in8 (port=113) at common/glib.h:136 +136 __asm__ __volatile__("inb %%dx, %0 \n\t" +(gdb) +``` + +### 2.2 设置断点和监视点 + +  设置断点和监视点是程序调试中最基础的一步。 + +- **设置断点** + +  您可以使用`break`或者`b`命令来设置断点。 + +  关于`break`或者`b`命令的使用: + +```shell +b #在当前活动源文件的相应行号打断点 + +b : #在对应文件的相应行号打断点 + +b #为一个命名函数打断点 +``` + +- **设置监视点** + +  您可以使用`watch`命令来设置监视点 + +```shell +watch # 设置对特定变量的监视点,将在特定变量发生变化的时候触发断点 + +watch # 设置对特定表达式的监视点,比如watch *(int*)0x12345678会在内存地址0x12345678处 + # 的整数值发生更改时触发断点。 +``` + +- **管理断点与监视点** + +  当我们打上断点之后,我们该如何查看我们所有的断点信息呢? + +  您可以通过`info b`,`info break`或者`info breakpoints`来查看所有的断点信息: + +```shell +(gdb) b 309 +Breakpoint 12 at 0xffff8000001f8f16: file /home/heyicong/.cargo/registry/src/mirrors.tuna.tsinghua.edu.cn-df7c3c540f42cdbd/thingbuf-0.1.4/src/lib.rs, line 315. +(gdb) watch slots +Watchpoint 13: slots +(gdb) info b +Num Type Disp Enb Address What +12 breakpoint keep y 0xffff8000001f8f16 in thingbuf::Core::pop_ref + at /home/heyicong/.cargo/registry/src/mirrors.tuna.tsinghua.edu.cn-df7c3c540f42cdbd/thingbuf-0.1.4/src/lib.rs:315 +13 watchpoint keep y slots +(gdb) +``` + +  以上信息中,编号为12的断点即是我们在活动源文件309行打的断点,若其`Address`为``,则表示在多个地址上存在相同的断点位置。这在循环中是非常常见的情况。编号为13的便是我们对`slots`变量设置的监视点。 + +  我们可以通过以下命令对断点或者监视点进行操作: + +```shell +delete # 或 d 删除对应编号的断点,在您不再需要使用这个断点的时候可以通过此命令删除断点 +delete # 或 d 删除对应编号的监视点,在您不再需要使用这个监视点的时候可以通过此命令删除监视点 + +disable # 禁用对应编号的断点,这适合于您只是暂时不需要使用这个断点时使用,当您禁用一个断点,下 + # 次程序运行到该断点处将不会停下来 +disable # 禁用对应编号的监视点,这适合于您只是暂时不需要使用这个监视点时使用 + +enable # 启用对应编号的断点 +enable # 启用对应编号的监视点 + +#clear命令 +clear # 清除当前活动源文件的断点以及监视点 +clear # 清除对应编号的所有断点或监视点,这与delete行为是一致的 +clear # 清除指定文件的所有断点与监视点 +``` + +## 2.3 变量和内存查看 + +- **print 和 display** + +  您可以通过`print`或者`p`来打印变量值。 + +  `print`命令用于打印变量或表达式的值。它允许您在调试过程中查看程序中的数据。 + +```shell +print # 打印对应变量名的值,例如:print my_variable 或者 p my_variable + +print # 打印合法表达式的值,例如:print a+b 或者 p a+b + +# 示例输出 +(gdb) print order +$3 = core::sync::atomic::Ordering::SeqCst +``` + +```{note} +如果您不仅想打印值,还想显示更多详细信息(例如类型信息),可以使用ptype命令。 +``` + +  您可以使用`display`命令来持续追踪变量或者表达式,`display`命令用于设置需要持续跟踪并在每次程序停止时显示的表达式。它类似于print命令,但与print不同的是,display命令在每次程序停止时自动打印指定表达式的值,而无需手动输入命令。 + +```shell +display # 打印对应变量名的值,例如:display my_variable + +display # 打印合法表达式的值,例如:display a+b + +# 示例输出 +(gdb) display order +1: order = core::sync::atomic::Ordering::SeqCst #其中1表示display编号, + #您可以通过info display命令来查看所有display编号 +``` + +```{note} +一旦您设置了display命令,每当程序停止(例如,在断点处停止)时,GDB将自动打印指定表达式的值。 + +display命令非常有用,因为它允许您在调试过程中持续监视表达式的值,而无需每次都手动输入print命令。它特别适用于那些您希望持续跟踪的变量或表达式。 +``` + +  **要取消已设置的display命令并停止自动显示表达式的值,可以使用undisplay命令:** + +```shell +undisplay # 如果不指定,则将取消所有已设置的display命令, + # 您可以通过info display命令来查看所有display编号 +``` + +```{note} +请注意,print和display命令只会在程序暂停执行时评估变量或表达式的值。如果程序正在运行,您需要通过设置断点或使用其他调试命令来暂停程序,然后才能使用print命令查看数据的值,display命令设置的值将会在程序暂停时自动输出。 +``` + +- **输出格式** + +  您可以设置输出格式来获取更多您需要的信息,例如:`print /a var` +> 参考至[GDB Cheat Sheet](https://darkdust.net/files/GDB%20Cheat%20Sheet.pdf) + +```shell +Format +a Pointer. +c Read as integer, print as character. +d Integer, signed decimal. +f Floating point number. +o Integer, print as octal. +s Try to treat as C string. +t Integer, print as binary (t = „two“). +u Integer, unsigned decimal. +x Integer, print as hexadecimal. +``` + +### 2.4 查看调用堆栈 + +- **查看调用栈** + +  当程序在断点处暂停时,应该怎样追踪程序行为呢? + +  您可以通过`backtarce`命令来查看调用栈。`backtrace`命令用于打印当前调用栈的回溯信息。它显示了程序在执行过程中所有活动的函数调用链,包括每个函数的名称、参数和源文件中的行号。 + +```shell +# 示例输出 +(gdb) backtrace +#0 function1 (arg1=10, arg2=20) at file1.c:15 +#1 function2 () at file2.c:25 +#2 xx () at xx.c:8 +``` + +  每一行回溯信息都以#开头,指示帧的编号。然后是函数名和参数列表,最后是源文件名和行号。 +通过查看回溯信息,您可以了解程序在哪些函数中执行,以及每个函数在调用栈中的位置。这对于调试程序和定位问题非常有用。 + +- **切换堆栈** + +  您可以通过`frame`或者`f`命令来切换对应的栈帧获取更多信息以及操作。 + +```shell +frame +f +``` + +  除了简单地执行backtrace命令,还可以使用一些选项来自定义回溯信息的输出。例如: +```shell +backtrace full #显示完整的符号信息,包括函数参数和局部变量。 +backtrace #限制回溯信息的帧数,只显示指定数量的帧。 +backtrace - #指定要显示的帧范围。 +backtrace thread #显示指定线程的回溯信息。 +``` + +### 2.5 多核心 + +  在调试内核时,您可能需要查看各个核心的运行状态。 + +  您可以通过`info threads`命令来查看各个核心的运行状态 + +```shell +(gdb) info threads + Id Target Id Frame + 1 Thread 1.1 (CPU#0 [halted ]) 0xffff800000140a3e in Start_Kernel () at main.c:227 +* 2 Thread 1.2 (CPU#1 [running]) thingbuf::Core::pop_ref () + at /home/heyicong/.cargo/registry/src/mirrors.tuna.tsinghua.edu.cn-df7c3c540f42cdbd/thingbuf-0.1.4/src/lib.rs:315 +(gdb) +``` + +  您可以使用`thread `命令切换到指定的核心上下文,以便查看和调试特定核心的状态。例如: + +```shell +(gdb) thread 1 +[Switching to thread 1 (Thread 1.1)] +#0 0xffff800000140a3e in Start_Kernel () at main.c:227 +227 hlt(); +``` + +### 2.6 更多 + +  接下来,我将为您介绍更多您可能在调试中能够使用的命令: + +```shell +step #或者s,逐行执行程序,并进入到函数调用中。可以在step命令后加执行次数,例:step 3 表示要连续执行3个步骤 +step #进入指定的函数,并停止在函数内的第一行。 + +next #或者n,逐行执行程序,但跳过函数调用,直接执行函数调用后的下一行代码。 + #它允许你在不进入函数内部的情况下执行代码,从而快速跳过函数调用的细节。 + #同样,next也可以在命令后加执行次数 + +finish #用于从当前函数中一直执行到函数返回为止,并停在调用该函数的地方。 + #它允许你快速执行完当前函数的剩余部分,并返回到调用函数的上下文中。 + +continue #用于继续程序的执行,直到遇到下一个断点或 + #程序正常结束或者程序暂停。 + +quit #退出调试 + +list #或者l,显示当前活动源文件源代码的片段,以及当前执行的位置。 +list : #显示文件里面的函数的源代码片段 +list : #显示文件里面的附近的源代码片段 +list , #显示当前活动源文件的之间的源代码片段 +set listsize #设置list命令显示的源代码行数。默认情况下,list命令显示当前行和其周围的几行代码。 + +info args #显示当前函数的参数及其值 +info breakpoints #显示断点以及监视点信息 +info display #显示当前设置的display列表 +info locals #显示当前函数/栈帧中的局部变量及其值 +info sharedlibrary #显示当前已加载的共享库(shared library)信息 +info signals #显示当前程序所支持的信号信息。它可以列出程序可以接收和处理的不同信号的列表。 +info threads #显示各个核心/线程信息,它可以列出当前正在运行的核心/线程以及它们的状态。 + +show directories #显示当前源代码文件的搜索路径列表。这些搜索路径决定了GDB在查找源代码文件时的搜索范围。 +show listsize #显示打印源代码时的上下文行数。它确定了在使用list命令(或其简写形式l)时显示的源代码行数。 + +whatis variable_name #查看给定变量或表达式的类型信息。它可以帮助你了解变量的数据类型。 +ptype #显示给定类型或变量的详细类型信息。它可以帮助你了解类型的结构和成员。 + #相较于whatis命令,ptype命令更加详细。 + +set var = #设置变量值 + +return #强制使当前函数返回设定值 +``` + +--- + +## 最后 + +  现在,您已经可以使用rust-gdb来调试DragonOS内核代码了。 + +> 您可以参阅GDB命令文档来获取更多帮助:[GDB Cheat Sheet](https://darkdust.net/files/GDB%20Cheat%20Sheet.pdf) \ No newline at end of file diff --git a/docs/kernel/debug/index.rst b/docs/kernel/debug/index.rst index 5e6a640b1..042b9b182 100644 --- a/docs/kernel/debug/index.rst +++ b/docs/kernel/debug/index.rst @@ -8,3 +8,4 @@ :caption: 目录 traceback + debug-kernel-with-gdb diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index a1202da38..625a6d96e 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -25,6 +25,7 @@ num-derive = "0.3" # 一个no_std的hashmap、hashset hashbrown = "0.13.2" elf = { version = "0.7.2", default-features = false } +atomic_enum = "0.2.0" # 构建时依赖项 [build-dependencies] diff --git a/kernel/src/Makefile b/kernel/src/Makefile index 673183b41..841c40a4c 100644 --- a/kernel/src/Makefile +++ b/kernel/src/Makefile @@ -44,11 +44,8 @@ all: kernel @dbg='debug';for x in $$dbg; do \ cd $$x;\ - $(MAKE) generate_kallsyms kernel_root_path="$(shell pwd)";\ + $(MAKE) generate_kallsyms kernel_root_path="$(shell pwd)"||exit 1;\ cd ..;\ - if [ "$$?" != "0" ]; then\ - exit $$?;\ - fi;\ done diff --git a/kernel/src/common/math/Makefile b/kernel/src/common/math/Makefile index c70df0222..f85134716 100644 --- a/kernel/src/common/math/Makefile +++ b/kernel/src/common/math/Makefile @@ -1,14 +1,10 @@ - - +SRC = $(wildcard *.c) +OBJ = $(SRC:.c=.o) CFLAGS += -I . -all: fabs.o round.o pow.o - -fabs.o: fabs.c - $(CC) $(CFLAGS) -c fabs.c -o fabs.o +.PHONY: all -round.o: round.c - $(CC) $(CFLAGS) -c round.c -o round.o +all: $(OBJ) -pow.o: pow.c - $(CC) $(CFLAGS) -c pow.c -o pow.o \ No newline at end of file +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ \ No newline at end of file diff --git a/kernel/src/driver/acpi/Makefile b/kernel/src/driver/acpi/Makefile index 4fed31679..f85134716 100644 --- a/kernel/src/driver/acpi/Makefile +++ b/kernel/src/driver/acpi/Makefile @@ -1,8 +1,10 @@ - -all: acpi.o - +SRC = $(wildcard *.c) +OBJ = $(SRC:.c=.o) CFLAGS += -I . +.PHONY: all + +all: $(OBJ) -acpi.o: acpi.c - $(CC) $(CFLAGS) -c acpi.c -o acpi.o +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ \ No newline at end of file diff --git a/kernel/src/driver/disk/Makefile b/kernel/src/driver/disk/Makefile index 377024dc4..f85134716 100644 --- a/kernel/src/driver/disk/Makefile +++ b/kernel/src/driver/disk/Makefile @@ -1,7 +1,10 @@ +SRC = $(wildcard *.c) +OBJ = $(SRC:.c=.o) +CFLAGS += -I . -all: ata.o +.PHONY: all -CFLAGS += -I . +all: $(OBJ) -ata.o: ata.c - $(CC) $(CFLAGS) -c ata.c -o ata.o \ No newline at end of file +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ \ No newline at end of file diff --git a/kernel/src/driver/keyboard/Makefile b/kernel/src/driver/keyboard/Makefile index 517494b4d..f85134716 100644 --- a/kernel/src/driver/keyboard/Makefile +++ b/kernel/src/driver/keyboard/Makefile @@ -1,8 +1,10 @@ - -all: ps2_keyboard.o - +SRC = $(wildcard *.c) +OBJ = $(SRC:.c=.o) CFLAGS += -I . +.PHONY: all + +all: $(OBJ) -ps2_keyboard.o: ps2_keyboard.c - $(CC) $(CFLAGS) -c ps2_keyboard.c -o ps2_keyboard.o \ No newline at end of file +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ \ No newline at end of file diff --git a/kernel/src/driver/mouse/Makefile b/kernel/src/driver/mouse/Makefile index 192de29ee..f85134716 100644 --- a/kernel/src/driver/mouse/Makefile +++ b/kernel/src/driver/mouse/Makefile @@ -1,8 +1,10 @@ - -all: ps2_mouse.o - +SRC = $(wildcard *.c) +OBJ = $(SRC:.c=.o) CFLAGS += -I . +.PHONY: all + +all: $(OBJ) -ps2_mouse.o: ps2_mouse.c - $(CC) $(CFLAGS) -c ps2_mouse.c -o ps2_mouse.o \ No newline at end of file +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ \ No newline at end of file diff --git a/kernel/src/driver/multiboot2/Makefile b/kernel/src/driver/multiboot2/Makefile index 0e3524979..f85134716 100644 --- a/kernel/src/driver/multiboot2/Makefile +++ b/kernel/src/driver/multiboot2/Makefile @@ -1,7 +1,10 @@ +SRC = $(wildcard *.c) +OBJ = $(SRC:.c=.o) +CFLAGS += -I . -all: multiboot2.o +.PHONY: all -CFLAGS += -I . +all: $(OBJ) -multiboot2.o: multiboot2.c - $(CC) $(CFLAGS) -c multiboot2.c -o multiboot2.o \ No newline at end of file +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ \ No newline at end of file diff --git a/kernel/src/driver/pci/Makefile b/kernel/src/driver/pci/Makefile index 318284815..a505ba89f 100644 --- a/kernel/src/driver/pci/Makefile +++ b/kernel/src/driver/pci/Makefile @@ -1,9 +1,11 @@ +SRC = $(wildcard *.c) +OBJ = $(SRC:.c=.o) +CFLAGS += -I . -all: pci_irq.o +.PHONY: all -CFLAGS += -I . +all: $(OBJ) - -pci_irq.o: pci_irq.c - $(CC) $(CFLAGS) -c pci_irq.c -o pci_irq.o +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ diff --git a/kernel/src/driver/video/Makefile b/kernel/src/driver/video/Makefile index eb0fafb11..ae15a094a 100644 --- a/kernel/src/driver/video/Makefile +++ b/kernel/src/driver/video/Makefile @@ -1,8 +1,10 @@ - -all: video.o - +SRC = $(wildcard *.c) +OBJ = $(SRC:.c=.o) CFLAGS += -I . +.PHONY: all + +all: $(OBJ) -video.o: video.c - $(CC) $(CFLAGS) -c video.c -o video.o +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ diff --git a/kernel/src/filesystem/ramfs/mod.rs b/kernel/src/filesystem/ramfs/mod.rs index 3dd893b96..e2cef7c7e 100644 --- a/kernel/src/filesystem/ramfs/mod.rs +++ b/kernel/src/filesystem/ramfs/mod.rs @@ -114,6 +114,33 @@ impl RamFS { } impl IndexNode for LockedRamFSInode { + fn truncate(&self, len: usize) -> Result<(), SystemError> { + let mut inode = self.0.lock(); + + //如果是文件夹,则报错 + if inode.metadata.file_type == FileType::Dir { + return Err(SystemError::EINVAL); + } + + //当前文件长度大于_len才进行截断,否则不操作 + if inode.data.len() > len { + inode.data.resize(len, 0); + } + return Ok(()); + } + + fn close(&self, _data: &mut FilePrivateData) -> Result<(), SystemError> { + return Ok(()); + } + + fn open( + &self, + _data: &mut FilePrivateData, + _mode: &super::vfs::file::FileMode, + ) -> Result<(), SystemError> { + return Ok(()); + } + fn read_at( &self, offset: usize, diff --git a/kernel/src/ktest/Makefile b/kernel/src/ktest/Makefile index 4c47f8ce9..f85134716 100644 --- a/kernel/src/ktest/Makefile +++ b/kernel/src/ktest/Makefile @@ -1,20 +1,10 @@ - +SRC = $(wildcard *.c) +OBJ = $(SRC:.c=.o) CFLAGS += -I . +.PHONY: all -all: ktest.o bitree.o kfifo.o mutex.o idr.o - -ktest.o: ktest.c - $(CC) $(CFLAGS) -c ktest.c -o ktest.o - -bitree.o: test-bitree.c - $(CC) $(CFLAGS) -c test-bitree.c -o test-bitree.o - -kfifo.o: test-kfifo.c - $(CC) $(CFLAGS) -c test-kfifo.c -o test-kfifo.o - -mutex.o: test-mutex.c - $(CC) $(CFLAGS) -c test-mutex.c -o test-mutex.o +all: $(OBJ) -idr.o: test-idr.c - $(CC) $(CFLAGS) -c test-idr.c -o test-idr.o \ No newline at end of file +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ \ No newline at end of file diff --git a/kernel/src/libs/mod.rs b/kernel/src/libs/mod.rs index d7149f01c..d298cb49f 100644 --- a/kernel/src/libs/mod.rs +++ b/kernel/src/libs/mod.rs @@ -11,6 +11,8 @@ pub mod lib_ui; pub mod list; pub mod lockref; pub mod mutex; +pub mod notifier; +pub mod once; pub mod printk; pub mod rbtree; #[macro_use] @@ -21,5 +23,4 @@ pub mod spinlock; pub mod vec_cursor; #[macro_use] pub mod volatile; -pub mod notifier; pub mod wait_queue; diff --git a/kernel/src/libs/once.rs b/kernel/src/libs/once.rs new file mode 100644 index 000000000..c132035a4 --- /dev/null +++ b/kernel/src/libs/once.rs @@ -0,0 +1,179 @@ +use core::{ + fmt::{self, Debug, Formatter}, + sync::atomic::Ordering, +}; + +use atomic_enum::atomic_enum; + +pub struct Once { + inner: AtomicOnceState, +} + +#[atomic_enum] +#[derive(PartialEq, Eq)] +pub enum OnceState { + Incomplete, + Posioned, + Complete, +} + +#[allow(dead_code)] +impl Once { + pub const fn new() -> Self { + Self { + inner: AtomicOnceState::new(OnceState::Incomplete), + } + } + + #[track_caller] + pub fn call_once(&self, f: F) { + if self.is_completed() { + return; + } + + // set initialized + let r = self.inner.compare_exchange( + OnceState::Incomplete, + OnceState::Posioned, + Ordering::SeqCst, + Ordering::SeqCst, + ); + if r.is_err() { + return; + } + // call function + f(); + // set completed + self.inner.store(OnceState::Complete, Ordering::SeqCst); + } + + /// Performs the same function as [`call_once()`] except ignores poisoning. + /// + /// Unlike [`call_once()`], if this [`Once`] has been poisoned (i.e., a previous + /// call to [`call_once()`] or [`call_once_force()`] caused a panic), calling + /// [`call_once_force()`] will still invoke the closure `f` and will _not_ + /// result in an immediate panic. If `f` panics, the [`Once`] will remain + /// in a poison state. If `f` does _not_ panic, the [`Once`] will no + /// longer be in a poison state and all future calls to [`call_once()`] or + /// [`call_once_force()`] will be no-ops. + /// + /// The closure `f` is yielded a [`OnceState`] structure which can be used + /// to query the poison status of the [`Once`]. + /// + /// [`call_once()`]: Once::call_once + /// [`call_once_force()`]: Once::call_once_force + /// + /// # Examples + /// + /// ``` + /// use std::sync::Once; + /// use std::thread; + /// + /// static INIT: Once = Once::new(); + /// + /// // poison the once + /// let handle = thread::spawn(|| { + /// INIT.call_once(|| panic!()); + /// }); + /// assert!(handle.join().is_err()); + /// + /// // poisoning propagates + /// let handle = thread::spawn(|| { + /// INIT.call_once(|| {}); + /// }); + /// assert!(handle.join().is_err()); + /// + /// // call_once_force will still run and reset the poisoned state + /// INIT.call_once_force(|state| { + /// assert!(state.is_poisoned()); + /// }); + /// + /// // once any success happens, we stop propagating the poison + /// INIT.call_once(|| {}); + /// ``` + pub fn call_once_force(&self, f: F) + where + F: FnOnce(&OnceState), + { + // fast path check + if self.is_completed() { + return; + } + + // set poisoned + self.inner + .compare_exchange( + OnceState::Incomplete, + OnceState::Posioned, + Ordering::SeqCst, + Ordering::SeqCst, + ) + .ok(); + + // call function + f(&self.inner.load(Ordering::SeqCst)); + + // set initialized + self.inner.store(OnceState::Complete, Ordering::SeqCst); + } + + /// Fast path check + #[inline] + pub fn is_completed(&self) -> bool { + self.inner.load(Ordering::SeqCst) == OnceState::Complete + } + + /// Returns the current state of the `Once` instance. + #[inline] + pub fn state(&self) -> OnceState { + self.inner.load(Ordering::SeqCst) + } +} + +impl Debug for Once { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("Once").finish_non_exhaustive() + } +} + +#[allow(dead_code)] +impl OnceState { + /// Returns `true` if the associated [`Once`] was poisoned prior to the + /// invocation of the closure passed to [`Once::call_once_force()`]. + /// + /// # Examples + /// + /// A poisoned [`Once`]: + /// + /// ``` + /// use std::sync::Once; + /// use std::thread; + /// + /// static INIT: Once = Once::new(); + /// + /// // poison the once + /// let handle = thread::spawn(|| { + /// INIT.call_once(|| panic!()); + /// }); + /// assert!(handle.join().is_err()); + /// + /// INIT.call_once_force(|state| { + /// assert!(state.is_poisoned()); + /// }); + /// ``` + /// + /// An unpoisoned [`Once`]: + /// + /// ``` + /// use std::sync::Once; + /// + /// static INIT: Once = Once::new(); + /// + /// INIT.call_once_force(|state| { + /// assert!(!state.is_poisoned()); + /// }); + #[inline] + pub fn is_poisoned(&self) -> bool { + *self == OnceState::Posioned + } +} diff --git a/kernel/src/syscall/Makefile b/kernel/src/syscall/Makefile index 477a787a2..ae15a094a 100644 --- a/kernel/src/syscall/Makefile +++ b/kernel/src/syscall/Makefile @@ -1,8 +1,10 @@ - +SRC = $(wildcard *.c) +OBJ = $(SRC:.c=.o) CFLAGS += -I . +.PHONY: all -all: syscall.o +all: $(OBJ) -syscall.o: syscall.c - $(CC) $(CFLAGS) -c syscall.c -o syscall.o +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ diff --git a/tools/bootstrap.sh b/tools/bootstrap.sh index 9667be242..27f545c81 100644 --- a/tools/bootstrap.sh +++ b/tools/bootstrap.sh @@ -40,6 +40,7 @@ install_ubuntu_debian_pkg() sudo "$1" install -y \ ca-certificates \ curl \ + unzip \ gnupg \ lsb-release \ llvm-dev libclang-dev clang gcc-multilib \