Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tracepoint extension support #160

Open
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

cczetier
Copy link

Description

This PR adds basic tracepoint extension support to GDB stub. Closes #157.

API Stability

  • This PR does not require a breaking API change

Checklist

  • Documentation
    • Ensured any public-facing rustdoc formatting looks good (via cargo doc)
    • (if appropriate) Added feature to "Debugging Features" in README.md
  • Validation
    • Included output of running examples/armv4t with RUST_LOG=trace + any relevant GDB output under the "Validation" section below
    • Included output of running ./example_no_std/check_size.sh before/after changes under the "Validation" section below
  • If implementing a new protocol extension IDET
    • Included a basic sample implementation in examples/armv4t
    • IDET can be optimized out (confirmed via ./example_no_std/check_size.sh)
    • OR implementation requires introducing non-optional binary bloat (please elaborate under "Description")
  • If upstreaming an Arch implementation
    • I have tested this code in my project, and to the best of my knowledge, it is working as intended.

Validation

GDB output
(gdb) trace test.c:10
Tracepoint 1 at 0x55550040: file test.c, line 10.
(gdb) break *0x55550044
Breakpoint 2 at 0x55550044: file test.c, line 10.
(gdb) break main
Breakpoint 3 at 0x5555000c: file test.c, line 2.
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: target:/test.elf 
Reading /test.elf from remote target...

Breakpoint 3, main () at test.c:2
2	in test.c
(gdb) tstart
warning: Target does not support trace user/notes, info ignored
(gdb) c
Continuing.

Breakpoint 2, 0x55550044 in main () at test.c:10
10	in test.c
(gdb) tstop
warning: Target does not support trace notes, note ignored
(gdb) tstatus
Trace stopped for an unknown reason.
Collected 1 trace frames.
Trace will stop if GDB disconnects.
Not looking at any trace frame.
(gdb) info tracepoints
Num     Type           Disp Enb Address    What
1       tracepoint     keep y   0x55550040 in main at test.c:10
	tracepoint already hit 1 time
	installed on target
(gdb) tfind
Found trace frame 0, tracepoint 1
#0  main () at test.c:10
10	in test.c
(gdb) i r
r0             0x0                 0
r1             0x0                 0
r2             0x0                 0
r3             0x0                 0
r4             0x0                 0
r5             0x0                 0
r6             0x0                 0
r7             0x0                 0
r8             0x0                 0
r9             0x0                 0
r10            0x0                 0
r11            0xffffffc           268435452
r12            0x0                 0
sp             0xfffffe8           0xfffffe8
lr             0x12345678          305419896
pc             0x55550040          0x55550040 <main+64>
cpsr           0x80000010          -2147483632
custom         0x12345678          305419896
time           0xb6fdf9ec          3070097900
unavailable    <unavailable>
loading section ".text" into memory from [0x55550000..0x55550078] Setting PC to 0x55550000 Waiting for a GDB connection on "127.0.0.1:9001"...

(The start of the cargo run output is corrupted by binary data that's printed, so I cut out the portion of the output relevant for tracepoint packets)

TRACE gdbstub::protocol::recv_packet     > <-- $QTinit#59
 TRACE gdbstub::protocol::response_writer > --> $OK#9a
 TRACE gdbstub::protocol::recv_packet     > <-- $QTDP:1:0000000055550040:E:0:0#49
 TRACE gdbstub::protocol::response_writer > --> $OK#9a
 TRACE gdbstub::protocol::recv_packet     > <-- $QTro:55550000,55550078#23
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("QTro:55550000,55550078")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $QTBuffer:circular:0#f8
 TRACE gdbstub::protocol::response_writer > --> $OK#9a
 TRACE gdbstub::protocol::recv_packet     > <-- $QTBuffer:size:-1#8c
 TRACE gdbstub::protocol::response_writer > --> $OK#9a
 TRACE gdbstub::protocol::recv_packet     > <-- $QTNotes:user:;notes:;#ba
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("QTNotes:user:;notes:;")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $QTStart#b3
 TRACE gdbstub::protocol::response_writer > --> $OK#9a
 TRACE gdbstub::protocol::recv_packet     > <-- $Z0,55550044,4#b2
 TRACE gdbstub::protocol::response_writer > --> $OK#9a
 TRACE gdbstub::protocol::recv_packet     > <-- $qTStatus#49
 TRACE gdbstub::protocol::response_writer > --> $T1;tframes:00#4c
 TRACE gdbstub::protocol::recv_packet     > <-- $vCont;s:p1.1#f2
 TRACE gdbstub::stub::state_machine       > transition: "Idle<armv4t::emu::Emu>" --> "Running"
 TRACE armv4t_emu::arm                    > ARM: pc: 0x5555000c, inst: 0xe3a03004, cond: 0xe, cflags: 0000
 TRACE armv4t_emu::arm                    > Instruction: DataProc2
 TRACE gdbstub::protocol::response_writer > --> $S05#b8
 TRACE gdbstub::stub::state_machine       > transition: "Running" --> "Idle<armv4t::emu::Emu>"
 TRACE gdbstub::protocol::recv_packet     > <-- $g#67
 TRACE gdbstub::protocol::response_writer > --> $0000000000000000000000000400000000000000000000000000000000000000000000000000000000000000fcffff0f00000000e8ffff0f7856341210005555xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx1000000078563412#7d
 TRACE gdbstub::protocol::recv_packet     > <-- $Z0,5555000c,4#dd
 TRACE gdbstub::protocol::response_writer > --> $OK#9a
 TRACE gdbstub::protocol::recv_packet     > <-- $qTStatus#49
 TRACE gdbstub::protocol::response_writer > --> $T1;tframes:00#4c
 TRACE gdbstub::protocol::recv_packet     > <-- $vCont;c:p1.-1#0f
 TRACE gdbstub::stub::state_machine       > transition: "Idle<armv4t::emu::Emu>" --> "Running"
 TRACE armv4t_emu::arm                    > ARM: pc: 0x55550010, inst: 0xe50b3008, cond: 0xe, cflags: 0000
 TRACE armv4t_emu::arm                    > Instruction: SingleXferI
 TRACE armv4t_emu::arm                    > ARM: pc: 0x55550014, inst: 0xe3a03003, cond: 0xe, cflags: 0000
 TRACE armv4t_emu::arm                    > Instruction: DataProc2
 TRACE armv4t_emu::arm                    > ARM: pc: 0x55550018, inst: 0xe50b3010, cond: 0xe, cflags: 0000
 TRACE armv4t_emu::arm                    > Instruction: SingleXferI
 TRACE armv4t_emu::arm                    > ARM: pc: 0x5555001c, inst: 0xe51b3008, cond: 0xe, cflags: 0000
 TRACE armv4t_emu::arm                    > Instruction: SingleXferI
 TRACE armv4t_emu::arm                    > ARM: pc: 0x55550020, inst: 0xe2833001, cond: 0xe, cflags: 0000
 TRACE armv4t_emu::arm                    > Instruction: DataProc2
 TRACE armv4t_emu::arm                    > ARM: pc: 0x55550024, inst: 0xe50b3008, cond: 0xe, cflags: 0000
 TRACE armv4t_emu::arm                    > Instruction: SingleXferI
 TRACE armv4t_emu::arm                    > ARM: pc: 0x55550028, inst: 0xe51b3010, cond: 0xe, cflags: 0000
 TRACE armv4t_emu::arm                    > Instruction: SingleXferI
 TRACE armv4t_emu::arm                    > ARM: pc: 0x5555002c, inst: 0xe2833003, cond: 0xe, cflags: 0000
 TRACE armv4t_emu::arm                    > Instruction: DataProc2
 TRACE armv4t_emu::arm                    > ARM: pc: 0x55550030, inst: 0xe50b3010, cond: 0xe, cflags: 0000
 TRACE armv4t_emu::arm                    > Instruction: SingleXferI
 TRACE armv4t_emu::arm                    > ARM: pc: 0x55550034, inst: 0xe3a03000, cond: 0xe, cflags: 0000
 TRACE armv4t_emu::arm                    > Instruction: DataProc2
 TRACE armv4t_emu::arm                    > ARM: pc: 0x55550038, inst: 0xe50b300c, cond: 0xe, cflags: 0000
 TRACE armv4t_emu::arm                    > Instruction: SingleXferI
 TRACE armv4t_emu::arm                    > ARM: pc: 0x5555003c, inst: 0xea000005, cond: 0xe, cflags: 0000
 TRACE armv4t_emu::arm                    > Instruction: Branch
 TRACE armv4t_emu::arm                    > ARM: pc: 0x55550058, inst: 0xe51b300c, cond: 0xe, cflags: 0000
 TRACE armv4t_emu::arm                    > Instruction: SingleXferI
 TRACE armv4t_emu::arm                    > ARM: pc: 0x5555005c, inst: 0xe3530902, cond: 0xe, cflags: 0000
 TRACE armv4t_emu::arm                    > Instruction: DataProc2
 TRACE armv4t_emu::arm                    > ARM: pc: 0x55550060, inst: 0xbafffff6, cond: 0xb, cflags: 1000
 TRACE armv4t_emu::arm                    > Instruction: Branch
 TRACE armv4t_emu::arm                    > ARM: pc: 0x55550040, inst: 0xe51b3008, cond: 0xe, cflags: 1000
 TRACE armv4t_emu::arm                    > Instruction: SingleXferI
 TRACE gdbstub::protocol::response_writer > --> $T05thread:p01.01;swbreak:;#6a
 TRACE gdbstub::stub::state_machine       > transition: "Running" --> "Idle<armv4t::emu::Emu>"
 TRACE gdbstub::protocol::recv_packet     > <-- $g#67
 TRACE gdbstub::protocol::response_writer > --> $0000000000000000000000000500000000000000000000000000000000000000000000000000000000000000fcffff0f00000000e8ffff0f7856341244005555xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx1000008078563412#8d
 TRACE gdbstub::protocol::recv_packet     > <-- $qfThreadInfo#bb
 TRACE gdbstub::protocol::response_writer > --> $mp01.01#cd
 TRACE gdbstub::protocol::recv_packet     > <-- $qsThreadInfo#c8
 TRACE gdbstub::protocol::response_writer > --> $l#6c
 TRACE gdbstub::protocol::recv_packet     > <-- $z0,5555000c,4#fd
 TRACE gdbstub::protocol::response_writer > --> $OK#9a
 TRACE gdbstub::protocol::recv_packet     > <-- $z0,55550044,4#d2
 TRACE gdbstub::protocol::response_writer > --> $OK#9a
 TRACE gdbstub::protocol::recv_packet     > <-- $m55550044,4#69
 TRACE gdbstub::protocol::response_writer > --> $013083e2#c6
 TRACE gdbstub::protocol::recv_packet     > <-- $m55550040,4#65
 TRACE gdbstub::protocol::response_writer > --> $08301be5#f8
 TRACE gdbstub::protocol::recv_packet     > <-- $m55550044,4#69
 TRACE gdbstub::protocol::response_writer > --> $013083e2#c6
 TRACE gdbstub::protocol::recv_packet     > <-- $m55550040,4#65
 TRACE gdbstub::protocol::response_writer > --> $08301be5#f8
 TRACE gdbstub::protocol::recv_packet     > <-- $m55550044,2#67
 TRACE gdbstub::protocol::response_writer > --> $0130#c4
 TRACE gdbstub::protocol::recv_packet     > <-- $m55550042,2#65
 TRACE gdbstub::protocol::response_writer > --> $1be5#2d
 TRACE gdbstub::protocol::recv_packet     > <-- $m55550040,2#63
 TRACE gdbstub::protocol::response_writer > --> $0830#cb
 TRACE gdbstub::protocol::recv_packet     > <-- $m55550044,2#67
 TRACE gdbstub::protocol::response_writer > --> $0130#c4
 TRACE gdbstub::protocol::recv_packet     > <-- $m55550042,2#65
 TRACE gdbstub::protocol::response_writer > --> $1be5#2d
 TRACE gdbstub::protocol::recv_packet     > <-- $m55550040,2#63
 TRACE gdbstub::protocol::response_writer > --> $0830#cb
 TRACE gdbstub::protocol::recv_packet     > <-- $m55550044,4#69
 TRACE gdbstub::protocol::response_writer > --> $013083e2#c6
 TRACE gdbstub::protocol::recv_packet     > <-- $m55550040,4#65
 TRACE gdbstub::protocol::response_writer > --> $08301be5#f8
 TRACE gdbstub::protocol::recv_packet     > <-- $m55550044,4#69
 TRACE gdbstub::protocol::response_writer > --> $013083e2#c6
 TRACE gdbstub::protocol::recv_packet     > <-- $m55550040,4#65
 TRACE gdbstub::protocol::response_writer > --> $08301be5#f8
 TRACE gdbstub::protocol::recv_packet     > <-- $m55550044,4#69
 TRACE gdbstub::protocol::response_writer > --> $013083e2#c6
 TRACE gdbstub::protocol::recv_packet     > <-- $m55550044,4#69
 TRACE gdbstub::protocol::response_writer > --> $013083e2#c6
 TRACE gdbstub::protocol::recv_packet     > <-- $m55550044,4#69
 TRACE gdbstub::protocol::response_writer > --> $013083e2#c6
 TRACE gdbstub::protocol::recv_packet     > <-- $QTStop#4b
 TRACE gdbstub::protocol::response_writer > --> $OK#9a
 TRACE gdbstub::protocol::recv_packet     > <-- $QTNotes:tstop:;#97
 INFO  gdbstub::stub::core_impl           > Unknown command: Ok("QTNotes:tstop:;")
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $qTStatus#49
 TRACE gdbstub::protocol::response_writer > --> $T0;tframes:01#4c
 TRACE gdbstub::protocol::recv_packet     > <-- $qTP:1:55550040#52
 TRACE gdbstub::protocol::response_writer > --> $V01:00#51
 TRACE gdbstub::protocol::recv_packet     > <-- $QTFrame:0#fa
 TRACE gdbstub::protocol::response_writer > --> $F 00T 01#9b
 TRACE gdbstub::protocol::recv_packet     > <-- $QTFrame:0#fa
 TRACE gdbstub::protocol::response_writer > --> $F 00T 01#9b
 TRACE gdbstub::protocol::recv_packet     > <-- $g#67
 TRACE gdbstub::protocol::response_writer > --> $0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fcffff0f00000000e8ffff0f7856341240005555xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx1000008078563412#df
 TRACE gdbstub::protocol::recv_packet     > <-- $m55550040,4#65
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $m5555003c,4#97
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $m55550040,4#65
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $m5555003c,4#97
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $m55550040,2#63
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $m5555003e,2#97
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $m5555003c,2#95
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $m55550040,2#63
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $m5555003e,2#97
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $m5555003c,2#95
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $m55550040,4#65
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $m5555003c,4#97
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $m55550040,4#65
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $m5555003c,4#97
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $m55550040,4#65
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $m55550040,4#65
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $m55550040,4#65
 TRACE gdbstub::protocol::response_writer > --> $#00
 TRACE gdbstub::protocol::recv_packet     > <-- $p1b#03
 TRACE gdbstub::protocol::response_writer > --> $ecf9fdb6#c9
 TRACE gdbstub::protocol::recv_packet     > <-- $p1c#04
 TRACE gdbstub::protocol::response_writer > --> $xxxxxxxx#c6
Before/After `./example_no_std/check_size.sh` output

Before

Analyzing target/release/gdbstub-nostd

File  .text    Size          Crate Name
2.4%  71.2% 11.9KiB      [Unknown] main
0.2%   5.7%    969B        gdbstub gdbstub::stub::state_machine::GdbStubStateMachineInner<gdbstub::stub::state_machi...
0.1%   2.2%    374B        gdbstub gdbstub::protocol::commands::breakpoint::BasicBreakpoint::from_slice
0.1%   1.7%    295B        gdbstub <gdbstub::protocol::common::thread_id::ThreadId as core::convert::TryFrom<&[u8]>>...
0.1%   1.7%    295B        gdbstub gdbstub::stub::core_impl::resume::<impl gdbstub::stub::core_impl::GdbStubImpl<T,C...
0.1%   1.6%    278B        gdbstub gdbstub::protocol::packet::PacketBuf::new
0.1%   1.6%    277B        gdbstub gdbstub::protocol::common::hex::decode_hex_buf
0.1%   1.6%    273B        gdbstub gdbstub::protocol::response_writer::ResponseWriter<C>::write_specific_thread_id
0.0%   1.2%    213B        gdbstub gdbstub::protocol::response_writer::ResponseWriter<C>::write
0.0%   0.8%    132B        gdbstub gdbstub::protocol::common::hex::decode_hex
0.0%   0.7%    122B        gdbstub gdbstub::protocol::response_writer::ResponseWriter<C>::inner_write
0.0%   0.6%    111B        gdbstub gdbstub::protocol::response_writer::ResponseWriter<C>::flush
0.0%   0.6%    107B        gdbstub gdbstub::protocol::common::hex::decode_hex
0.0%   0.6%    106B gdbstub_nostd? <gdbstub_nostd::gdb::DummyTarget as gdbstub::target::ext::base::multithread::Mult...
0.0%   0.6%    104B        gdbstub gdbstub::protocol::response_writer::ResponseWriter<C>::write_num
0.0%   0.6%    101B      [Unknown] __libc_csu_init
0.0%   0.6%     97B        gdbstub gdbstub::protocol::response_writer::ResponseWriter<C>::write_num
0.0%   0.6%     95B        gdbstub gdbstub::protocol::response_writer::ResponseWriter<C>::write_hex
0.0%   0.5%     92B        gdbstub <gdbstub::protocol::common::thread_id::IdKind as core::convert::TryFrom<&[u8]>>::...
0.0%   0.5%     87B        gdbstub <usize as gdbstub::internal::be_bytes::BeBytes>::from_be_bytes
0.0%   0.5%     85B        gdbstub <u32 as gdbstub::internal::be_bytes::BeBytes>::from_be_bytes
0.0%   0.4%     71B   gdbstub_arch <gdbstub_arch::arm::reg::arm_core::ArmCoreRegs as gdbstub::arch::Registers>::gdb_...
0.0%   0.4%     65B gdbstub_nostd? <gdbstub_nostd::gdb::DummyTarget as gdbstub::target::ext::base::multithread::Mult...
0.0%   0.4%     65B gdbstub_nostd? <gdbstub_nostd::gdb::DummyTarget as gdbstub::target::ext::base::multithread::Mult...
0.0%   0.4%     65B gdbstub_nostd? <gdbstub_nostd::gdb::DummyTarget as gdbstub::target::ext::base::multithread::Mult...
0.0%   0.3%     50B gdbstub_nostd? <gdbstub_nostd::gdb::DummyTarget as gdbstub::target::ext::base::multithread::Mult...
0.0%   0.3%     50B gdbstub_nostd? <gdbstub_nostd::gdb::DummyTarget as gdbstub::target::ext::base::multithread::Mult...
0.0%   0.3%     50B gdbstub_nostd? <gdbstub_nostd::gdb::DummyTarget as gdbstub::target::ext::base::multithread::Mult...
0.0%   0.3%     43B      [Unknown] _start
0.0%   0.0%      6B gdbstub_nostd? <gdbstub_nostd::gdb::DummyTarget as gdbstub::target::ext::breakpoints::SwBreakpoi...
0.0%   0.0%      2B      [Unknown] __libc_csu_fini
3.4% 100.0% 16.7KiB                .text section size, the file size is 488.4KiB
target/release/gdbstub-nostd  :
section               size      addr
.interp                 28       568
.note.ABI-tag           32       596
.note.gnu.build-id      36       628
.gnu.hash               28       664
.dynsym                360       696
.dynstr                193      1056
.gnu.version            30      1250
.gnu.version_r          48      1280
.rela.dyn              408      1328
.init                   23      1736
.plt                    16      1760
.plt.got                 8      1776
.text                17106      1792
.fini                    9     18900
.rodata                920     18912
.eh_frame_hdr          276     19832
.eh_frame             1488     20112
.init_array              8   2121128
.fini_array              8   2121136
.dynamic               448   2121144
.got                   136   2121592
.data                    8   2121728
.bss                     8   2121736
.comment                93         0
Total                21718

After

Analyzing target/release/gdbstub-nostd

File  .text    Size          Crate Name
2.1%  71.1% 11.8KiB      [Unknown] main
0.2%   5.7%    969B        gdbstub gdbstub::stub::state_machine::GdbStubStateMachineInner<gdbstub::stub::state_machine::state::Running,T,C>::r...
0.1%   2.2%    374B        gdbstub gdbstub::protocol::commands::breakpoint::BasicBreakpoint::from_slice
0.1%   1.7%    295B        gdbstub <gdbstub::protocol::common::thread_id::ThreadId as core::convert::TryFrom<&[u8]>>::try_from
0.1%   1.7%    295B        gdbstub gdbstub::stub::core_impl::resume::<impl gdbstub::stub::core_impl::GdbStubImpl<T,C>>::write_stop_common
0.0%   1.6%    278B        gdbstub gdbstub::protocol::packet::PacketBuf::new
0.0%   1.6%    277B        gdbstub gdbstub::protocol::common::hex::decode_hex_buf
0.0%   1.6%    273B        gdbstub gdbstub::protocol::response_writer::ResponseWriter<C>::write_specific_thread_id
0.0%   1.3%    213B        gdbstub gdbstub::protocol::response_writer::ResponseWriter<C>::write
0.0%   0.8%    132B        gdbstub gdbstub::protocol::common::hex::decode_hex
0.0%   0.7%    122B        gdbstub gdbstub::protocol::response_writer::ResponseWriter<C>::inner_write
0.0%   0.7%    111B        gdbstub gdbstub::protocol::response_writer::ResponseWriter<C>::flush
0.0%   0.6%    107B        gdbstub gdbstub::protocol::common::hex::decode_hex
0.0%   0.6%    106B gdbstub_nostd? <gdbstub_nostd::gdb::DummyTarget as gdbstub::target::ext::base::multithread::MultiThreadBase>::read_addrs
0.0%   0.6%    104B        gdbstub gdbstub::protocol::response_writer::ResponseWriter<C>::write_num
0.0%   0.6%    101B      [Unknown] __libc_csu_init
0.0%   0.6%     97B        gdbstub gdbstub::protocol::response_writer::ResponseWriter<C>::write_num
0.0%   0.6%     95B        gdbstub gdbstub::protocol::response_writer::ResponseWriter<C>::write_hex
0.0%   0.5%     92B        gdbstub <gdbstub::protocol::common::thread_id::IdKind as core::convert::TryFrom<&[u8]>>::try_from
0.0%   0.5%     87B        gdbstub <usize as gdbstub::internal::be_bytes::BeBytes>::from_be_bytes
0.0%   0.5%     85B        gdbstub <u32 as gdbstub::internal::be_bytes::BeBytes>::from_be_bytes
0.0%   0.4%     71B   gdbstub_arch <gdbstub_arch::arm::reg::arm_core::ArmCoreRegs as gdbstub::arch::Registers>::gdb_deserialize
0.0%   0.4%     65B gdbstub_nostd? <gdbstub_nostd::gdb::DummyTarget as gdbstub::target::ext::base::multithread::MultiThreadBase>::write_addrs
0.0%   0.4%     65B gdbstub_nostd? <gdbstub_nostd::gdb::DummyTarget as gdbstub::target::ext::base::multithread::MultiThreadBase>::write_registers
0.0%   0.4%     65B gdbstub_nostd? <gdbstub_nostd::gdb::DummyTarget as gdbstub::target::ext::base::multithread::MultiThreadBase>::read_registers
0.0%   0.3%     50B gdbstub_nostd? <gdbstub_nostd::gdb::DummyTarget as gdbstub::target::ext::base::multithread::MultiThreadResume>::set_resume...
0.0%   0.3%     50B gdbstub_nostd? <gdbstub_nostd::gdb::DummyTarget as gdbstub::target::ext::base::multithread::MultiThreadResume>::clear_resu...
0.0%   0.3%     50B gdbstub_nostd? <gdbstub_nostd::gdb::DummyTarget as gdbstub::target::ext::base::multithread::MultiThreadResume>::resume
0.0%   0.3%     43B      [Unknown] _start
0.0%   0.0%      6B gdbstub_nostd? <gdbstub_nostd::gdb::DummyTarget as gdbstub::target::ext::breakpoints::SwBreakpoint>::add_sw_breakpoint
0.0%   0.0%      2B      [Unknown] __libc_csu_fini
3.0% 100.0% 16.6KiB                .text section size, the file size is 553.4KiB
target/release/gdbstub-nostd  :
section               size      addr
.interp                 28       568
.note.ABI-tag           32       596
.note.gnu.build-id      36       628
.gnu.hash               28       664
.dynsym                360       696
.dynstr                193      1056
.gnu.version            30      1250
.gnu.version_r          48      1280
.rela.dyn              408      1328
.init                   23      1736
.plt                    16      1760
.plt.got                 8      1776
.text                17010      1792
.fini                    9     18804
.rodata                920     18816
.eh_frame_hdr          276     19736
.eh_frame             1488     20016
.init_array              8   2121128
.fini_array              8   2121136
.dynamic               448   2121144
.got                   136   2121592
.data                    8   2121728
.bss                     8   2121736
.comment                93         0
Total                21622

@cczetier
Copy link
Author

I wasn't sure how the zero panic verification guarantee can be checked - there isn't a how to section in the README about it. I tried to write code as panic-free as I could, but I may have missed some pieces.

@daniel5151
Copy link
Owner

Thanks for sending in this PR - I'm excited to dig in here!

Unfortunately, I'm just about to leave for a vacation where I'll be completely AFK, so I'll only be able to take a look sometime in the range of ~Dec 20th to ~Dec 23rd.

Just wanted to give you a heads up, so that you're not worried about the lack of movement here 😅

Copy link
Owner

@daniel5151 daniel5151 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello again, and happy holidays!

I've finally found some time to sit down and give this PR a review.
Please find a plethora of comments attached.


Broadly speaking, I think this is an impressive chunk of work, implementing what appears to be a very annoying and non-trivial part of the GDB RSP. From an organizational and syntactical POV, there's nothing that a few review comments can't polish up, and the overall vision here appears to be consistent and well put together. Kudos!

That said, I do have some concerns about the amount of API surface area we're taking on here, and the feasibility of testing all of it. Its great that the you've got some things working in the armv4t example, but from looking at the code (notably: obvious errors such as handlers which send responses with spaces delimiting various part of the packet), it seems that you've got a lot of code here that theoretically works, but hasn't been directly validated.

That's not to say we shouldn't try to land this work!

I think we should certainly try to get this PR landed... but to temper expectations for end-users, I might suggest we land this code under a mod experimental, with some documentation mentioning that this code covers a lot of surface area, and may not be fully tested.

Alternatively, if you're so inclined, I'd be happy to see more investment into the armv4t example code, with some corresponding logs that show all these codepaths having been smoke-tested. Or, of course, logs from whatever project you're implementing this feature for (and ideally, a link to the implementation itself - assuming you're working on something open-source).


I wasn't sure how the zero panic verification guarantee can be checked - there isn't a how to section in the README about it. I tried to write code as panic-free as I could, but I may have missed some pieces.

CI has a check for this, but it seems that the check doesn't run if clippy fails. Oops.

I should probably re-jig CI a bit so that clippy failing still gives no_panic feedback... my apologies.

"QTDP" => _QTDP::QTDP<'a>,
"QTinit" => _QTinit::QTinit,
"QTBuffer" => _QTBuffer::QTBuffer<'a>,
// TODO: QTNotes?
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if its worth singling out this one particular packet, when there are plenty of other tracepoint related packets that are presently unimplemented.

alternatively, might I suggest fully enumerating which remaining tracepoint packets are left to implement, and providing brief descriptions why they aren't necessary for v0? That would give any future folks working in this space some useful context.

i => Some(decode_hex(i).ok()?)
})}
},
req => {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on my reading on the packet spec, it looks like you're conflating the qTBuffer packet with the QTBuffer packets.

i.e: from https://sourceware.org/gdb/current/onlinedocs/gdb.html/Tracepoint-Packets.html

‘qTBuffer:offset,len’

Return up to len bytes of the current contents of trace buffer, starting at offset. The trace buffer is treated as if it were a contiguous collection of traceframes, as per the trace file format. The reply consists as many hex-encoded bytes as > the target can deliver in a packet; it is not an error to return fewer than were asked for. A reply consisting of just l indicates that no bytes are available.

which is distinct from

‘QTBuffer:circular:value’

This packet directs the target to use a circular trace buffer if value is 1, or a linear buffer if the value is 0.

‘QTBuffer:size:size’

This packet directs the target to make the trace buffer be of size size if possible. A value of -1 tells the target to use whatever size it prefers.

I think this implementation needs to be split into 2 (or 3) files, to handle the 3 packet variants.

@@ -172,6 +172,11 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
}
}

if let Some(_ops) = target.support_tracepoints() {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there are quite a few other tracepoint-related features in the docs. could you explain why these are the only two that were enabled, and maybe leave a comment mentioning what other features may need to be enabled in the future if/when additional functionality is ever implemented?

Comment on lines +41 to +43
// Our response has to be a hex encoded buffer that fits within
// our packet size, which means we actually have half as much space
// as our slice would indicate.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so, interestingly enough, this is not the approach gdbstub has taken thus-far when it comes to responses.

While we certainly have a fixed buffer for incoming packets (i.e: the buffer we are parsing from here), we assume that the GDB client is capable of accepting any amount of data we stream out as part of our responses. This aligns with my particular reading of the GDB spec, which discusses the size of packets the stub can accept... but doesn't make a judgement on the size of response packets the client can receive.

as such - I would suggest skipping this buffer-slicing step entirely, and instead offering the handler a callback function they can write an arbirarily sized &[u8] into, which gdbstub can then simply stream out. If you poke around some of the other target APIs, you'll see examples of this callback-based / streaming-based pattern being employed to great effect.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alternatively, if you think that from a purely ergonomic POV, its nicer to provide end-users with a buffer to write data into... lets make sure to pass along the entire buffer we have access to, and let downstream response-writer code stream out the corresponding hex bytes.

let body = buf.into_body();
match body {
[b':', b'-', actions @ ..] => {
let mut params = actions.splitn_mut(4, |b| matches!(*b, b':'));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this matches should just be a comparison, since you only have one thing you're matching against

mut f: impl FnMut(&TracepointAction<'_, U>),
) -> Option<bool> {
let mut more = false;
let mut unparsed: Option<&mut [u8]> = Some(actions);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't this loop be replaced with a split iterator, matching on b'S' | b'R' | b'M' | b'X' | b'-'?

actions: &mut [u8],
mut f: impl FnMut(&TracepointAction<'_, U>),
) -> Option<bool> {
let mut more = false;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is another case where strip_suffix could simplify some logic

Some([b'R', mask @ ..]) => {
let mask_end = mask
.iter()
.cloned()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

raising my eyebrows at cloned instead of copied 👀.

let status = ops.trace_experiment_status().handle_error()?;
res.write_str("T")?;
res.write_dec(if status.running { 1 } else { 0 })?;
for explanation in status.explanations.iter() {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using ManagedSlice in the return value of the target method is certainly one approach... but can't we avoid the need to expose ManagedSlice in the API entirely by having trace_experiment_status accept a callback instead?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to write the T0/T1 running status before we write any of the explanations. If we pass the res writing callback to the target implementation, then it would need to have some mechanism of reporting the experiment state before it can run the explanation callback. We can't do like an &mut FnOnce(ExperimentStatus)->TargetResult<&mut FnMut(ExperimentExplantion)->TargetResult<(),Self>, Self> because we can't return a borrow from the closure due to lifetime issues, and can't return a trait object by value due to it being unsized.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking about it, one solution would be to split the API into trace_experiment_status and then trace_experiment_statistics or something? Does gdbstub particularly care about matching API surface 1:1 with gdb packets or would that be ok?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does gdbstub particularly care about matching API surface 1:1 with gdb packets or would that be ok?

Reflecting the underlying packet structure to end users is actually an anti-goal of gdbstub 😄

It just-so-happens that the many times, the protocol is relatively trivial, so the resulting API ends up matching the packets 1:1... but there are other cases where gdbstub goes out of its way to expose a more "user-friendly" API, and then take on whatever heavy-lifting it needs to do in order to map those friendly end-user semantics onto the underlying protocol.

let e = (|| -> Result<_, _> {
match desc {
FrameDescription::FrameNumber(n) => {
res.write_str("F ")?;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again, this sort of space isn't really a thing in the GDB RSP...

We include spaces in some of the templates for clarity; these are not part of the packet’s syntax. No GDB packet uses spaces to separate its components. For example, a template like ‘foo bar baz’ describes a packet beginning with the three ASCII bytes ‘foo’, followed by a bar, followed directly by a baz. GDB does not transmit a space character between the ‘foo’ and the bar, or between the bar and the baz.

https://sourceware.org/gdb/current/onlinedocs/gdb.html/Packets.html#Packets

This strongly implies to me that this codepath hasn't been tested...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I definitely missed that section of the docs! This codepath does actually work, however: in the armv4t example that's included in this MR I can do

(gdb) tfind 3
Found trace frame 3, tracepoint 1
#0  main () at test.c:10
10	in test.c
(gdb) tfind 2
Found trace frame 2, tracepoint 1
10	in test.c

Copy link
Owner

@daniel5151 daniel5151 Dec 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh GDB... thou art truly exceptional software... /s

If we peek inside GDB and look at how it parses the packet response, we find this code

      case 'F':
	p = ++reply;
	target_frameno = (int) strtol (p, &reply, 16);

(via https://github.com/bminor/binutils-gdb/blob/e16e638/gdb/remote.c#L14258-L14263)

And lo, reading the docs of strtol reveals this lovely feature:

Discards any whitespace characters (as identified by calling isspace) until the first non-whitespace character is found [...]

So that solves that mystery...

In any case, even though this "works", lets make sure gdbstub remains spec-compliant, in case other GDB RSP clients aren't quite so forgiving of bonus whitespace here.


Apologies for assuming that you hadn't tested this code, hopefully you understand why I might've gotten that assumption from reading just this code + the corresponding spec 😅

@cczetier
Copy link
Author

Thanks for the feedback! I'll look over and resolve them. For some background, we're using gdbstub in order to build out debugging introspection for a project we have. Our current tooling is via Python, and we're using gdbstub via some PyO3 bindings I hacked together (which I do intend to open source eventually, once I find the time), but that also isn't very "interesting" code. I'm working on this tracepoint support in tandem with building out the rest of the debugging stack, and so the API surface here was the minimal amount I needed in order to get gdb to not error out with "not supported" and implement the tracepoint functionality. We do have functionality using this, however, so none of it should be untested code (although some parts, like trace_buffer_request, we're also stubbing out in our closed source implementation similarly to the armv4t example).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Tracepoints support
2 participants