ctoolbox/utilities/
debug_tools.rs

1use anyhow::{Context, Result};
2use std::io::{self, BufRead, Write};
3
4/// Read list of symbols from stdin and print GDB instructions to stdout.
5pub fn generate_gdb_instructions_streaming() -> Result<()> {
6    let stdin = io::stdin();
7    let mut stdout = io::stdout();
8    generate_gdb_instructions_from_data(stdin.lock(), &mut stdout)
9}
10
11/// Generate GDB instructions from any Read/Write source.
12pub fn generate_gdb_instructions_from_data<R: BufRead, W: Write>(
13    reader: R,
14    writer: &mut W,
15) -> Result<()> {
16    const CHUNK_SIZE: usize = 1000;
17    let mut symbols = Vec::new();
18
19    // Read symbols from reader
20    for line in reader.lines() {
21        let sym = line.context("Failed to read line")?;
22        symbols.push(sym);
23    }
24
25    for sym in symbols {
26        // Skip overly long symbols that confuse GDB
27        if sym.len() > 200 {
28            continue;
29        }
30        // Escape backslashes and double quotes for GDB
31        let esc = sym.replace('\\', r"\\").replace('"', r#"\""#);
32        writeln!(writer, "break {esc}")
33            .context("Failed to write breakpoint")?;
34        writeln!(writer, "commands").context("Failed to write commands")?;
35        writeln!(writer, "  silent").context("Failed to write silent")?;
36        writeln!(writer, "  bt 1").context("Failed to write bt 1")?;
37        writeln!(writer, "  continue").context("Failed to write continue")?;
38        writeln!(writer, "end").context("Failed to write end")?;
39    }
40    writer.flush().context("Failed to flush output")?;
41
42    Ok(())
43}
44
45pub fn generate_gdb_instructions(data: Vec<u8>) -> Result<Vec<u8>> {
46    let mut output = Vec::new();
47    generate_gdb_instructions_from_data(&data[..], &mut output)?;
48    Ok(output)
49}
50
51#[cfg(test)]
52#[allow(clippy::unwrap_in_result, clippy::panic_in_result_fn)]
53mod tests {
54
55    use crate::utilities::assert_vec_u8_ok_eq;
56
57    use super::*;
58
59    #[crate::ctb_test]
60    fn test_generate_gdb_instructions() {
61        let input = "<F as core::future::into_future::IntoFuture>::into_future::h952eb23145f23b7d\ntokio::runtime::scheduler::multi_thread::MultiThread::block_on::h9acd86b63968a313\ntokio::runtime::scheduler::multi_thread::MultiThread::block_on::hd8141c28d0d6be82\nbreak core::ptr::drop_in_place<core::array::drain::Drain<(&str,alloc::boxed::Box<[jaq_core::Bind]>,(fn(&jaq_core::compile::Lut<jaq_core::filter::Native<jaq_json::Val>>,(jaq_core::filter::Ctx<jaq_json::Val>,jaq_json::Val)) .> alloc::boxed::Box<dyn core::iter::traits::iterator::Iterator+Item = core::result::Result<jaq_json::Val,jaq_core::exn::Exn<jaq_json::Val>>>,fn(&jaq_core::compile::Lut<jaq_core::filter::Native<jaq_json::Val>>,(jaq_core::filter::Ctx<jaq_json::Val>,jaq_json::Val),alloc::boxed::Box<dyn jaq_core::filter::Update<jaq_json::Val>+Output = alloc::boxed::Box<dyn core::iter::traits::iterator::Iterator+Item = core::result::Result<jaq_json::Val,jaq_core::exn::Exn<jaq_json::Val>>>>) .> alloc::boxed::Box<dyn core::iter::traits::iterator::Iterator+Item = core::result::Result<jaq_json::Val,jaq_core::exn::Exn<jaq_json::Val>>>))>>::h3979d882214be3bc\n<&mut T as core::ops::deref::DerefMut>::deref_mut::h6564409efaf38817\n";
62        let expected_output = "break <F as core::future::into_future::IntoFuture>::into_future::h952eb23145f23b7d\ncommands\n  silent\n  bt 1\n  continue\nend\nbreak tokio::runtime::scheduler::multi_thread::MultiThread::block_on::h9acd86b63968a313\ncommands\n  silent\n  bt 1\n  continue\nend\nbreak tokio::runtime::scheduler::multi_thread::MultiThread::block_on::hd8141c28d0d6be82\ncommands\n  silent\n  bt 1\n  continue\nend\nbreak <&mut T as core::ops::deref::DerefMut>::deref_mut::h6564409efaf38817\ncommands\n  silent\n  bt 1\n  continue\nend\n";
63
64        let output = generate_gdb_instructions(input.as_bytes().to_vec());
65
66        assert_vec_u8_ok_eq(expected_output.as_bytes(), output);
67    }
68}