Make the PC probe interval configurable, and trace the app's life as one tick

uvk5_pc_probe_interval_ms() reads UVK5_PC_PROBE_MS and falls back to 100, so existing probe runs are unchanged. At 2 ms a launch gives 468 samples in about 0.94 s and exactly one of them is inside the 4 KiB overlay: the app lives for roughly 2 ms, not 100.

The trace settles two things and adds one. The loader copies and jumps (the single overlay sample; a 16-byte app that calls nothing stays there forever). The sector-cache overwrite is dead: the flash log's last transaction for the run is the app's own code load (addr=103000 len=2408) and nothing follows it, no font read included. New: after the app dies the firmware loops inside its own flash (PCs near 0x08005118/0x08005122/0x08005248) with zero flash reads and no key response at all -- F then 7 no longer opens the menu and an explicit MENU down/up changes nothing -- so the launcher or its fault path is wedged.
This commit is contained in:
mckero committed 2026-10-02 10:14:50 +08:00
1 parent 394f473df0
commit f3a369d38f
3 files changed
+49 -2

No files matched your search

+18
View File
@@ -790,6 +790,24 @@ Next instrument, and it needs a one-line model change rather than more guessing:
100 ms, which is the same order as the app's whole life. Dropping that interval to a couple of milliseconds 100 ms, which is the same order as the app's whole life. Dropping that interval to a couple of milliseconds
turns it into a real trace and would show where in the overlay the app stops. turns it into a real trace and would show where in the overlay the app stops.
**The PC probe's interval is now a knob (UVK5_PC_PROBE_MS), and at 2 ms the overlay app's whole life is one tick.**
100 ms was the same order as the thing being measured: an app that the launcher starts and that is gone again
before the next sample. uvk5_pc_probe_interval_ms() reads UVK5_PC_PROBE_MS and falls back to 100, so every
existing probe run is unchanged. At 2 ms, a launch gives 468 samples in about 0.94 s and **exactly one of them
is inside the 4 KiB overlay** -- the app runs for roughly 2 ms, not 100.
With a real trace in hand, two things are now settled and one is new. Settled: the loader copies and jumps (the
single overlay sample), and a 16-byte app that calls nothing stays there forever. Settled the other way: the
sector-cache overwrite is dead -- the flash log's **last** transaction for the run is the app's own code load
(addr=103000 len=2408) and **nothing follows it**, no font read included. New: after the app dies the firmware
sits in a loop inside its own flash (PCs around 0x08005118/0x08005122/0x08005248) with **zero flash reads and
no key response at all** -- even F then 7 no longer opens the menu, and an explicit MENU down/up changes
nothing. So the launcher (or the fault path it takes) is wedged, not merely waiting for a release.
That is where the next round starts: the app dies within about 2 ms, the firmware never reads the flash after
loading it, and what is left running is a tight loop in the firmware with no display or key activity.
## The keypad: two real bugs, both fixed ## The keypad: two real bugs, both fixed
The old note here said "keys reach the firmware but the UI does not react" and The old note here said "keys reach the firmware but the UI does not react" and
+16
View File
@@ -642,6 +642,22 @@ flash 探针是按顺序记录每一笔事务的,而那一次启动的**最后
下一个仪器,只需要改模型一行而不是继续猜:PC 探针每 100 ms 采样一次,而应用整个生命期就是这个量级。把这个 下一个仪器,只需要改模型一行而不是继续猜:PC 探针每 100 ms 采样一次,而应用整个生命期就是这个量级。把这个
间隔降到几毫秒,它就变成真正的 trace,能看出应用**停在叠加区的哪个位置**。 间隔降到几毫秒,它就变成真正的 trace,能看出应用**停在叠加区的哪个位置**。
**PC 探针的间隔现在是可配置的(UVK5_PC_PROBE_MS);调到 2 ms 之后,叠加应用整个生命期只有一个采样点。**
100 ms 与被测对象同量级:启动器刚把应用带起来,下一个采样到来之前它就已经没了。uvk5_pc_probe_interval_ms()
读 UVK5_PC_PROBE_MS、缺省仍是 100,所以以往任何一次探针运行都保持原样。调到 2 ms 后,一次启动在约 0.94 秒里
得到 468 个样本,其中**恰好一个**落在那 4 KiB 叠加区内 —— 应用只活了约 **2 毫秒**,不是 100。
有了真正的 trace,两件事就此确定,另有一件是新发现。确定的:加载器会拷贝并跳转(那一个叠加区样本),而且
那个 16 字节、什么都不调用的应用能一直待在里面。反过来确定的:**扇区缓存覆盖说已经死了** —— 那次运行的 flash
日志里**最后一条**就是应用自己的代码加载(addr=103000 len=2408),**其后什么都没有**,包括字体读取。新发现:
应用死掉之后,固件**停在自身 flash 内的一个循环里**(PC 在 0x08005118/0x08005122/0x08005248 一带),**零次 flash
读取、按键完全无响应** —— 连 F 再 7 都打不开菜单了,显式地 MENU down/up 也毫无变化。也就是说**启动器(或它走的
故障路径)卡死了**,而不只是在等一次松开。
下一轮就从这里开始:应用在约 2 ms 内死亡,固件在加载它之后**再也没有读过 flash**,而剩下的是一个没有显示、
没有按键活动的紧循环。
## 键盘:两个真 bug,都已修复 ## 键盘:两个真 bug,都已修复
这里原来的笔记写的是"按键到达了固件但界面不反应",并且归咎于机器模型。结果发现有**两个 这里原来的笔记写的是"按键到达了固件但界面不反应",并且归咎于机器模型。结果发现有**两个
+15 -2
View File
@@ -3609,6 +3609,19 @@ static uint32_t uvk5_sniff_app_offset(const char *path)
* can be located without a debugger (a gdb attach stops the guest, which is the * can be located without a debugger (a gdb attach stops the guest, which is the
* one thing that must not happen while working out where it stopped). * one thing that must not happen while working out where it stopped).
*/ */
/*
* How often the PC probe samples. 100 ms is fine for finding a hang, but an overlay
* app's whole life can be shorter than that -- the launcher takes the screen and the app
* is gone again before the next tick -- so UVK5_PC_PROBE_MS lets a run ask for a real
* trace instead of a few points. The default keeps every existing probe run identical.
*/
static int uvk5_pc_probe_interval_ms(void)
{
const char *v = g_getenv("UVK5_PC_PROBE_MS");
int ms = v ? atoi(v) : 0;
return ms > 0 ? ms : 100;
}
static void uvk5_pc_probe_tick(void *opaque) static void uvk5_pc_probe_tick(void *opaque)
{ {
UVK5MachineState *s = opaque; UVK5MachineState *s = opaque;
@@ -3620,7 +3633,7 @@ static void uvk5_pc_probe_tick(void *opaque)
fclose(f); fclose(f);
} }
} }
timer_mod(s->pc_probe_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 100); timer_mod(s->pc_probe_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + uvk5_pc_probe_interval_ms());
} }
static void uvk5_boot_key_release(void *opaque) static void uvk5_boot_key_release(void *opaque)
@@ -3800,7 +3813,7 @@ static void uvk5_machine_init(MachineState *machine)
uvk5_arm_boot_key(s); uvk5_arm_boot_key(s);
if (g_getenv("UVK5_PC_PROBE")) { if (g_getenv("UVK5_PC_PROBE")) {
s->pc_probe_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, uvk5_pc_probe_tick, s); s->pc_probe_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, uvk5_pc_probe_tick, s);
timer_mod(s->pc_probe_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 100); timer_mod(s->pc_probe_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + uvk5_pc_probe_interval_ms());
} }
qdev_connect_gpio_out_named(DEVICE(&s->soc.gpio[0]), "pin-out", 3, qdev_connect_gpio_out_named(DEVICE(&s->soc.gpio[0]), "pin-out", 3,
qdev_get_gpio_in_named(DEVICE(&s->flash), qdev_get_gpio_in_named(DEVICE(&s->flash),