记录GDB MI的所有输入输出
我实现了一些GDB的漂亮打印器(pretty printers),大多数IDE在不同时间点、在各种情形下都会让GDB挂起。我已经使用 sys.settrace 为我的Python代码添加了详细日志记录,但我仍然不明白到底为什么会调用我的打印器。
是否可以开启 对GDB MI的所有输入输出的完整日志记录?
例如,在VS Code中可以在launch.json中启用此类日志记录:
"logging": {
"engineLogging": true,
"trace": true,
"traceResponse": true
}
然后我在Debug Console中看到如下:
1: (3617) <-1078-var-create - * "curtime"
--> E (output): {"type":"event","event":"output","body":{"category":"console","output":"1: (3618) ->1078^done,name=\"var5\",numchild=\"0\",value=\"1782204844911511\",type=\"uint64_t\",thread-id=\"1\",has_more=\"0\"\n"},"seq":2528}
在这里可以看到GDB MI命令 "var-create" 以及随后的GDB响应。这条日志记录帮助我找到了 这个问题。
解决方案
我决定采用 [strace] 的方法,这是在了解到日志命令之前我就为VS Code使用过的方法。我用Python写了一个合适的解码脚本。原来 gdb 会从 stdin 接收MI输入,并将MI输出发送给 stdout。
Logging
为了开始日志记录,我运行以下命令:
strace -p "$(pidof gdb)" -e fds=0,1 -s 65536 -xx -o strace_gdb.txt
参数说明:
- 附加到名称为
gdb的进程的PID - 对描述符0(stdin)和1(stdout)的文件操作进行日志记录
- 每次调用最多记录65536字节
- 将所有读取/写入的字节以十六进制转义序列输出(如 "\x20\x0a")
- 将所有内容输出到文件
strace_gdb.txt
不过输出格式非常混乱:每个输入字符都被归属于它自己的 read 调用!
解码
于是我运行一个Python脚本,找出read/write调用中的所有字符串,将它们从十六进制格式解码,拼接成完整的字符串,并去掉 (gdb) 提示符。脚本如下:
import sys
def hex_parse(c):
n = ord(c)
if n >= ord('a') and n <= ord('z'):
return 10 + (n - ord('a'))
assert n >= ord('0') and n <= ord('9')
return n - ord('0')
def extract_string_argument(text, idx):
pos = text.find('(', 0)
for i in range(idx):
pos = text.find(',', pos + 1)
pos = text.find('"', pos + 1)
pbeg = pos + 1
pos = text.find('"', pos + 1)
pend = pos
k = (pend - pbeg) // 4
res = [0] * k
for i in range(k):
assert text[pbeg + 4*i + 0] == '\\' and text[pbeg + 4*i + 1] == 'x'
value = hex_parse(text[pbeg + 4*i + 2]) * 16 + hex_parse(text[pbeg + 4*i + 3])
res[i] = value
return bytearray(res)
fname = ['read', 'write']
prefix = [b'-IN: ', b'OUT: ']
buffers = [b'', b'']
autotrunc = b'\n(gdb) \n'
def flush(s):
buf = buffers[s]
if len(buf) == 0:
return
if s == 1:
buf = buf.replace(autotrunc, b'')
fout.write(prefix[s] + buf)
if not buf.endswith(b'\n'):
fout.write(b'\n')
buffers[s] = b''
def output(s, data):
for i in range(2):
if i != s:
flush(i)
buffers[s] += data
input_fn = 'strace_gdb.txt'
output_fn = 'dec_' + input_fn
with open(output_fn, 'wb') as fout:
with open(input_fn, 'rt') as fin:
for l in fin.readlines():
l = l.strip()
if l.startswith('read'):
text = extract_string_argument(l, 1)
output(0, text)
elif l.startswith('write'):
text = extract_string_argument(l, 1)
output(1, text)
结果
下面是一个解码后日志的示例:
-IN: 95-var-delete "var51"
OUT: 95^done,ndeleted="5"
-IN: 96-var-create --thread 3 --frame 0 var55 @ "surf"
OUT: 96^done,name="var55",numchild="1",value="0x310483b0",type="const modelSurface_t *",has_more="0"
-IN: 97-var-create --thread 3 --frame 0 var56 @ "sint"
OUT: 97^done,name="var56",numchild="1",value="0x7fb70c017230",type="surfaceInteraction_t *",has_more="0"
-IN: 98-var-create --thread 3 --frame 0 var57 @ "genLightTris"
OUT: 98^done,name="var57",numchild="0",value="true",type="bool",has_more="0"
-IN: 99-var-create --thread 3 --frame 0 var58 @ "genShadows"
OUT: 99^done,name="var58",numchild="0",value="false",type="bool",has_more="0"
-IN: 100-var-create --thread 3 --frame 0 var59 @ "c"
OUT: 100^done,name="var59",numchild="0",value="0",type="int",has_more="0"
-IN: 101-var-create --thread 3 --frame 0 var60 @ "lightShader"
OUT: 101^done,name="var60",numchild="2",value="0x2f9bb2e0",type="const idMaterial *",has_more="0"
-IN: 102-var-create --thread 3 --frame 0 var61 @ "spectrumbypass"
OUT: 102^done,name="var61",numchild="0",value="false",type="bool",has_more="0"
-IN: 103-var-create --thread 3 --frame 0 var62 @ "interactionGenerated"
OUT: 103^done,name="var62",numchild="0",value="false",type="bool",has_more="0"
-IN: 104-var-create --thread 3 --frame 0 var63 @ "shadowGen"
OUT: 104^done,name="var63",numchild="0",value="SG_DYNAMIC",type="shadowGen_t",has_more="0"
-IN: 105-var-create --thread 3 --frame 0 var64 @ "this"
OUT: 105^done,name="var64",numchild="2",value="0x7fb6d4023fc8",type="idInteraction * const",has_more="0"
-IN: 106-var-create --thread 3 --frame 0 var65 @ "model"
OUT: 106^done,name="var65",numchild="0",value="0x310634e0",type="const idRenderModel *",has_more="0"
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。