11package main
22
33import (
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+ }
0 commit comments