Skip to content

Commit 21ab64e

Browse files
committed
refactor: update cmd/help/kvspace, keytree/vthread, layoutcode
1 parent 701bcb4 commit 21ab64e

4 files changed

Lines changed: 88 additions & 2 deletions

File tree

cmd/kvlang/help.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ func showHelp() {
3030
fmt.Fprintln(os.Stderr, " watch <key> [--timeout 5s] 阻塞等待通知")
3131
fmt.Fprintln(os.Stderr, " notify <key> <value> 推送通知")
3232
fmt.Fprintln(os.Stderr, " clear 清空所有 kvspace 数据")
33+
fmt.Fprintln(os.Stderr, " trace <vtid> 追踪 vthread 单步执行,输出 NDJSON")
3334
fmt.Fprintln(os.Stderr)
3435
fmt.Fprintln(os.Stderr, "全局选项:")
3536
fmt.Fprintln(os.Stderr, " --addr host:port Redis 地址(默认 127.0.0.1:6379)")

cmd/kvlang/kvspace.go

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package main
22

33
import (
4+
"encoding/json"
45
"flag"
56
"fmt"
67
"os"
78
"strings"
9+
"time"
810

911
"kvlang/internal/keytree"
1012
"kvlang/internal/kvspace"
@@ -16,7 +18,7 @@ func cmdKVSpace(args []string) {
1618
addr := fs.String("addr", "127.0.0.1:6379", "Redis 地址 (host:port)")
1719
fs.Usage = func() {
1820
fmt.Fprintln(os.Stderr, "usage: kvlang kvspace [--addr host:port] <subcommand> [args]")
19-
fmt.Fprintln(os.Stderr, "subcommands: get mget set del list tree dump watch notify clear")
21+
fmt.Fprintln(os.Stderr, "subcommands: get mget set del list tree dump watch notify clear trace")
2022
fs.PrintDefaults()
2123
}
2224
fs.Parse(args)
@@ -80,6 +82,16 @@ func cmdKVSpace(args []string) {
8082
case "clear":
8183
clearAll(kv)
8284

85+
case "trace":
86+
// trace 监听 /vthread/<vtid>/.debug.pause 上的暂停事件,
87+
// 将每个事件输出为一行 NDJSON,并自动发送 "step" 使程序继续执行。
88+
// 用于捕获已开启调试模式(.debug = "step")的 vthread 的执行轨迹。
89+
//
90+
// 用法:kvlang kvspace trace <vtid>
91+
// 配合:先用 kvlang kvspace set /vthread/<vtid>/.debug "step" 开启调试
92+
if len(sub) < 2 { usageExit("kvlang kvspace trace <vtid>") }
93+
kvTrace(kv, sub[1])
94+
8395
default:
8496
fmt.Fprintf(os.Stderr, "unknown kvspace subcommand: %s\n", sub[0])
8597
os.Exit(1)
@@ -149,3 +161,52 @@ func dumpPrefix(kv kvspace.KVSpace, prefix string) {
149161
children, _ := kv.List(prefix)
150162
for _, c := range children { dumpPrefix(kv, prefix+"/"+c) }
151163
}
164+
165+
// kvTrace 监听 /vthread/<vtid>/.debug.pause 上的暂停事件。
166+
//
167+
// 每收到一条暂停事件(JSON),将其原样输出为一行 NDJSON(stdout),
168+
// 然后自动向 /vthread/<vtid>/.debug.resume 发送 "step",驱动 CPU 执行下一条指令。
169+
//
170+
// 终止条件:
171+
// - 连续超时(30 s 无事件):认为程序已结束
172+
// - os.Interrupt(Ctrl-C)
173+
//
174+
// 用于配合 kvlang kvspace set /vthread/<vtid>/.debug "step" 捕获程序执行轨迹,
175+
// 输出结果可直接送入 jq / 其他 NDJSON 工具分析。
176+
func kvTrace(kv kvspace.KVSpace, vtid string) {
177+
pauseKey := keytree.VThreadDebugPause(vtid)
178+
resumeKey := keytree.VThreadDebugResume(vtid)
179+
statusKey := keytree.VThreadStatus(vtid)
180+
181+
const watchTimeout = 10 * time.Second
182+
const maxIdle = 3 // 连续超时次数上限
183+
184+
idle := 0
185+
for {
186+
val, err := kv.Watch(pauseKey, watchTimeout)
187+
if err != nil {
188+
// 超时:检查 vthread 是否已终止
189+
idle++
190+
statusVal, serr := kv.Get(statusKey)
191+
if serr != nil || statusVal.IsNil() || idle >= maxIdle {
192+
// vthread 已终止或长期无活动 → 正常结束 trace
193+
return
194+
}
195+
continue
196+
}
197+
idle = 0
198+
199+
// 将暂停事件输出为一行 NDJSON
200+
// val 本身是 CPU 投递的 JSON 字符串,直接输出即可
201+
raw := val.Str()
202+
if raw == "" {
203+
// 空通知(不常见),构造最小 JSON
204+
out, _ := json.Marshal(map[string]any{"vtid": vtid, "note": "empty-pause"})
205+
raw = string(out)
206+
}
207+
fmt.Println(raw)
208+
209+
// 自动发送 "step",驱动 CPU 继续执行下一条指令
210+
kv.Notify(resumeKey, kvspace.Str("step")) //nolint:errcheck
211+
}
212+
}

internal/keytree/vthread.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,27 @@ func VThreadStatusMsg(vtid, statusVal string) string {
5252
// VThreadAt 返回 /vthread/<vtid>/<key>,通用路径构造(仅供引擎内部调试使用)
5353
func VThreadAt(vtid, key string) string { return "/vthread/" + vtid + "/" + key }
5454

55+
// ── 调试标志(引擎保留,. 前缀)────────────────────────────────────────────
56+
//
57+
// 所有 kvcpu 实例在 execute 循环中自动检查这三个键,无需特殊启动方式。
58+
// Agent 通过已有 kvspace 命令读写这些键来控制调试行为。
59+
//
60+
// .debug 调试控制键(agent 写,CPU 读)
61+
// ""/"" = 正常执行;"step" = 每条指令后暂停;"break:<func>" = 函数入口暂停
62+
// .debug.pause 暂停事件键(CPU 写 Notify,agent 用 Watch 等待)
63+
// 值:JSON {"pc":"...","func":"...","frame":"...","op":"..."}
64+
// .debug.resume 恢复命令键(agent 写 Notify,CPU 用 Watch 等待)
65+
// 值:"step"(执行一步继续暂停)| "continue"(恢复全速)| "abort"(终止)
66+
67+
// VThreadDebug 返回 /vthread/<vtid>/.debug(调试控制键)
68+
func VThreadDebug(vtid string) string { return "/vthread/" + vtid + "/.debug" }
69+
70+
// VThreadDebugPause 返回 /vthread/<vtid>/.debug.pause(CPU → agent 暂停事件)
71+
func VThreadDebugPause(vtid string) string { return "/vthread/" + vtid + "/.debug.pause" }
72+
73+
// VThreadDebugResume 返回 /vthread/<vtid>/.debug.resume(agent → CPU 恢复命令)
74+
func VThreadDebugResume(vtid string) string { return "/vthread/" + vtid + "/.debug.resume" }
75+
5576
// VtidFromPC 从绝对 PC 提取 vtid。
5677
//
5778
// "/vthread/42/[0,0]" → "42"

internal/layoutcode/layoutcode.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,12 +223,15 @@ func Bootstrap(ctx context.Context, kv kvspace.KVSpace, vtid, funcName string, a
223223
kv.Set(vthreadRoot+"/.rootfunc", kvspace.Str(funcName))
224224

225225
// 绑定入参(若有)
226+
// 使用 ResolveReadValue 而非 kvspace.Str,确保字面量(如 "3"、"true")
227+
// 以正确的类型(int/bool)写入帧,与 HandleCall 的参数绑定语义保持一致。
228+
// 否则数字参数会被存储为 string,导致 le/lt 等比较操作进入 strCmp 分支。
226229
if len(args) > 0 {
227230
sigVal, _ := kv.Get(funcKey)
228231
sig := parser.ParseFuncSig(sigVal.Str())
229232
for i, param := range sig.ParamNames() {
230233
if i < len(args) {
231-
kv.Set(vthreadRoot+"/"+param, kvspace.Str(args[i]))
234+
kv.Set(vthreadRoot+"/"+param, builtin.ResolveReadValue(kv, "", args[i]))
232235
}
233236
}
234237
}

0 commit comments

Comments
 (0)