Files
ViperOS/VKernel/Kernel/platform/pch/timer.vp
T
TermiNexus c29c1953df chore(intr): 探针补记被中断代码的 RIP/CS/RSP,用于定位卡死线程停在哪条指令
isr.s: ISR_NOERR/ISR_ERR 的 _frame_ready 里把 r8(RIP)/r9(CS)/rax(RSP) 写进新增的 _intr_rip/_intr_cs/_intr_rsp(只读这三个寄存器,不影响随后压栈)。timer.vp: 心跳行追加 3 个 hex32 字段。定时器 1kHz,所以这三个值几乎总是最后一次定时中断打断的现场:intr_cs=2B 表示该线程卡在用户态 intr_rip 处,=08 表示卡在内核。
2026-10-16 17:58:51 +08:00

184 lines
6.7 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import platform.pch.pic as pic
import platform.pch.pit as pit
import sched.sched as sched
import drivers.core.cpu.apic as apic
import asm
define TIMER_FREQUENCY = 1000
define TICKS_PER_SECOND = TIMER_FREQUENCY
define MILLISECONDS_PER_TICK = (1000 / TIMER_FREQUENCY)
static ticks: volatile uint64 = 0
static seconds: volatile uint32 = 0
static milliseconds: volatile uint32 = 0
static timer_running: volatile bool = False
# ============================================================================
# 【诊断·临时,定位后必须删除】IRQ0 心跳 + 调度器快照。
#
# 症状:某个 app 退出([syscall] exit called)之后 [perf]/[chk] 永不再打印。
# 桌面线程的渲染/打印完全依赖调度器切它回来,而调度器只在 needs_reschedule
# 被 sched_tick(IRQ0)置位、且有线程调用 try_reschedule 时才切换 ⇒ 三种可能:
# A) 本探针也一起停 → IRQ0 / EOI 死了(定时器不再推进)
# B) 探针继续、cur 恒定且该 tid 的 st=1(RUNNING) → 那个线程死循环且不再调用带
# try_reschedule 的 syscall ⇒ needs_reschedule 无人消费 ⇒ 全系统不再切换
# C) 探针继续、cur 在变 → 调度器活着,问题在桌面线程自身
#
# 直接写 COM1 0x3F8(自带 LSR 超时,绝不依赖 serial 驱动、绝不挂住 ISR)。
# 每行格式(`_intr_*` 由 isr.s 在每次中断入口写入 = 最后一次定时中断打断的那条指令):
# `[t] <ticks:8hex> <current_tid:8hex> <thread_count:8hex> <各线程 state 1 位hex> <intr_rip:8hex> <intr_cs:8hex> <intr_rsp:8hex>`
# 卡死线程的现场就是 intr_rip/intr_cs:cs=2B 表示卡在用户态该 rip 处,cs=08 表示卡在内核。
# ============================================================================
_dbg_tick: uint64 = 0
def _dbg_putc(ch: uint8):
tmo: int = 200000
while tmo > 0:
if (asm.inb(0x3FD) & 0x20) != 0:
break
tmo -= 1
asm.outb(0x3F8, ch)
def _dbg_hex32(v: uint64):
sh: int = 28
while sh >= 0:
d: int = int((v >> uint64(sh)) & uint64(15))
if d < 10:
_dbg_putc(uint8(48 + d))
else:
_dbg_putc(uint8(55 + d))
sh -= 4
def _dbg_snapshot():
s: sched.Scheduler* = sched._sched_ptr
_dbg_putc(uint8(10)) # \n
_dbg_putc(uint8(91)) # [
_dbg_putc(uint8(116)) # t
_dbg_putc(uint8(93)) # ]
_dbg_putc(uint8(32)) # ' '
_dbg_hex32(uint64(ticks))
_dbg_putc(uint8(32))
_dbg_hex32(uint64(s.current_tid))
_dbg_putc(uint8(32))
_dbg_hex32(uint64(s.thread_count))
_dbg_putc(uint8(32))
i: int
for i in range(s.thread_count):
st: int = s.threads[i].state
if st < 10:
_dbg_putc(uint8(48 + st))
else:
_dbg_putc(uint8(55 + st))
# 被中断代码的 RIP/CS/RSP(isr.s 写入 _intr_*)——定位"卡在哪条指令"的关键。
ir: uint64 = 0
ic: uint64 = 0
iu: uint64 = 0
__asm__(f"mov {asmout(ir, ASM_DESCR.OUTPUT_REG)}, [_intr_rip]", op=[ASM_DESCR.CLOBBER_MEMORY])
__asm__(f"mov {asmout(ic, ASM_DESCR.OUTPUT_REG)}, [_intr_cs]", op=[ASM_DESCR.CLOBBER_MEMORY])
__asm__(f"mov {asmout(iu, ASM_DESCR.OUTPUT_REG)}, [_intr_rsp]", op=[ASM_DESCR.CLOBBER_MEMORY])
_dbg_putc(uint8(32))
_dbg_hex32(ir)
_dbg_putc(uint8(32))
_dbg_hex32(ic)
_dbg_putc(uint8(32))
_dbg_hex32(iu)
_dbg_putc(uint8(10))
def timer_init():
global ticks, seconds, milliseconds, timer_running
__asm__("mfence", op=[ASM_DESCR.CLOBBER_MEMORY])
ticks = 0
seconds = 0
milliseconds = 0
timer_running = True
__asm__("mfence", op=[ASM_DESCR.CLOBBER_MEMORY])
pit.pit_init(TIMER_FREQUENCY)
def timer_handler():
global ticks, seconds, milliseconds, timer_running, _dbg_tick
if not timer_running:
pic.eoi(0)
apic.eoi()
return
__asm__("mfence", op=[ASM_DESCR.CLOBBER_MEMORY])
ticks = ticks + 1
milliseconds = milliseconds + MILLISECONDS_PER_TICK
if milliseconds >= 1000:
seconds = seconds + 1
milliseconds = milliseconds - 1000
__asm__("mfence", op=[ASM_DESCR.CLOBBER_MEMORY])
sched.Scheduler.sched_tick()
pic.eoi(0)
# LAPIC EOI:APIC 启用后中断经 LINT0(EXTINT) 送达会置 LAPIC ISR,
# 只发 PIC EOI 不清 LAPIC ISR → 首个定时中断后后续中断被屏蔽 →
# ticks 不推进 → msleep 永久挂起(QEMU/OVMF 下 APIC 必启用)。
apic.eoi()
# 【诊断·临时】每 1000 tick(≈1s)打一行心跳+调度器快照,见文件头说明。
_dbg_tick = _dbg_tick + 1
if (_dbg_tick % 1000) == 0:
_dbg_snapshot()
def timer_start():
global timer_running
__asm__("mfence", op=[ASM_DESCR.CLOBBER_MEMORY])
timer_running = True
__asm__("mfence", op=[ASM_DESCR.CLOBBER_MEMORY])
def timer_stop():
global timer_running
__asm__("mfence", op=[ASM_DESCR.CLOBBER_MEMORY])
timer_running = False
__asm__("mfence", op=[ASM_DESCR.CLOBBER_MEMORY])
def timer_reset():
global ticks, seconds, milliseconds
__asm__("mfence", op=[ASM_DESCR.CLOBBER_MEMORY])
ticks = 0
seconds = 0
milliseconds = 0
__asm__("mfence", op=[ASM_DESCR.CLOBBER_MEMORY])
def timer_get_ticks() -> uint64:
current_ticks: uint64
__asm__("mfence", op=[ASM_DESCR.CLOBBER_MEMORY])
current_ticks = ticks
__asm__("mfence", op=[ASM_DESCR.CLOBBER_MEMORY])
return current_ticks
def timer_get_seconds() -> uint32:
current_seconds: uint32
__asm__("mfence", op=[ASM_DESCR.CLOBBER_MEMORY])
current_seconds = seconds
__asm__("mfence", op=[ASM_DESCR.CLOBBER_MEMORY])
return current_seconds
def timer_get_milliseconds() -> uint32:
current_milliseconds: uint32
__asm__("mfence", op=[ASM_DESCR.CLOBBER_MEMORY])
current_milliseconds = milliseconds
__asm__("mfence", op=[ASM_DESCR.CLOBBER_MEMORY])
return current_milliseconds
def timer_get_time(out_seconds: uint32*, out_milliseconds: uint32*):
__asm__("mfence", op=[ASM_DESCR.CLOBBER_MEMORY])
if out_seconds:
*out_seconds = seconds
if out_milliseconds:
*out_milliseconds = milliseconds
__asm__("mfence", op=[ASM_DESCR.CLOBBER_MEMORY])
def timer_msleep(ms: uint32):
target: uint64 = ticks + (ms + MILLISECONDS_PER_TICK - 1) / MILLISECONDS_PER_TICK
while ticks < target:
asm.sti()
asm.hlt()
sched.Scheduler._yield()
def timer_sleep(sec: uint32):
target: uint64 = ticks + sec * TICKS_PER_SECOND
while ticks < target:
asm.sti()
asm.hlt()
sched.Scheduler._yield()