Rust调试适配器协议库dap的使用,dap为Rust开发者提供强大的调试功能支持

Rust调试适配器协议库dap的使用,dap为Rust开发者提供强大的调试功能支持

简介

dap-rs是Rust实现的调试适配器协议(简称DAP)。可以将DAP类比为语言服务器协议(LSP),但是用于调试器。

核心思想相同:提供一个协议作为编辑器和调试器之间通信的共同语言。这意味着实现DAP的编辑器可以使用同样实现DAP的调试器。

稳定性

该库目前处于早期阶段,可能会频繁出现破坏性变更。1.0版本之前的任何版本都可能是不兼容版本。

最小示例

创建一个二进制项目并添加dap到Cargo.toml:

[package]
name = "dummy-server"
version = "*"
edition = "2021"

[dependencies]
dap = "*"

我们的示例服务器将从文本文件读取输入并将输出写入stdout:

use std::fs::File;
use std::io::{BufReader, BufWriter};

use thiserror::Error;

use dap::prelude::*;

#[derive(Error, Debug)]
enum MyAdapterError {
  #[error("Unhandled command")]
  UnhandledCommandError,

  #[error("Missing command")]
  MissingCommandError,
}

type DynResult<T] = std::result::Result<T, Box<dyn std::error::Error>>;

fn main() -> DynResult<()> {
  let output = BufWriter::new(std::io::stdout());
  let f = File::open("testinput.txt")?;
  let input = BufReader::new(f);
  let mut server = Server::new(input, output);

  let req = match server.poll_request()? {
    Some(req) => req,
    None => return Err(Box::new(MyAdapterError::MissingCommandError)),
  };
  if let Command::Initialize(_) = req.command {
    let rsp = req.success(
      ResponseBody::Initialize(Some(types::Capabilities {
        ..Default::default()
      })),
    );

    // 当调用respond、send_event等时,消息将被包装在带有适当序列号的基本消息中,
    // 因此您不必自己跟踪序列号
    server.respond(rsp)?;

    server.send_event(Event::Initialized)?;
  } else {
    return Err(Box::new(MyAdapterError::UnhandledCommandError));
  }
  Ok(())
}

完整示例

以下是一个更完整的DAP调试适配器实现示例:

use std::fs::File;
use std::io::{BufReader, BufWriter};
use std::path::PathBuf;
use std::sync::mpsc;
use std::thread;

use dap::prelude::*;
use dap::types::*;

// 自定义调试适配器
struct MyDebugAdapter {
    server: Server<BufReader<File>, BufWriter<File>>,
    // 添加您需要的其他字段
}

impl MyDebugAdapter {
    fn new(input: BufReader<File>, output: BufWriter<File>) -> Self {
        Self {
            server: Server::new(input, output),
        }
    }

    // 处理初始化请求
    fn handle_initialize(&mut self, req: Request) -> Result<(), Box<dyn std::error::Error>> {
        let capabilities = Capabilities {
            supports_configuration_done_request: Some(true),
            supports_conditional_breakpoints: Some(true),
            supports_hit_conditional_breakpoints: Some(true),
            supports_function_breakpoints: Some(true),
            supports_evaluate_for_hovers: Some(true),
            supports_step_back: Some(true),
            supports_restart_frame: Some(true),
            ..Default::default()
        };

        let rsp = req.success(ResponseBody::Initialize(Some(capabilities)));
        self.server.respond(rsp)?;
        
        // 发送Initialized事件
        self.server.send_event(Event::Initialized)?;
        
        Ok(())
    }

    // 主事件循环
    fn run(&mut self) -> Result<(), Box<dyn std::error::Error>> {
        loop {
            match self.server.poll_request()? {
                Some(req) => {
                    match req.command {
                        Command::Initialize(_) => self.handle_initialize(req)?,
                        Command::Launch(_) => {
                            // 处理启动请求
                            let rsp = req.success(ResponseBody::Launch);
                            self.server.respond(rsp)?;
                        },
                        Command::SetBreakpoints(_) => {
                            // 处理断点设置
                            let rsp = req.success(ResponseBody::SetBreakpoints(Default::default()));
                            self.server.respond(rsp)?;
                        },
                        Command::ConfigurationDone => {
                            // 配置完成
                            let rsp = req.success(ResponseBody::ConfigurationDone);
                            self.server.respond(rsp)?;
                        },
                        Command::Threads => {
                            // 获取线程列表
                            let rsp = req.success(ResponseBody::Threads(ThreadsResponse {
                                threads: vec![Thread {
                                    id: 1,
                                    name: "main".to_string(),
                                }],
                            }));
                            self.server.respond(rsp)?;
                        },
                        Command::StackTrace(_) => {
                            // 获取堆栈跟踪
                            let rsp = req.success(ResponseBody::StackTrace(StackTraceResponse {
                                stack_frames: vec![],
                                total_frames: Some(0),
                            }));
                            self.server.respond(rsp)?;
                        },
                        _ => {
                            // 未处理的命令
                            let rsp = req.error("Unhandled command");
                            self.server.respond(rsp)?;
                        }
                    }
                },
                None => break,
            }
        }
        Ok(())
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 设置输入输出
    let input = BufReader::new(File::open("dap_input.txt")?);
    let output = BufWriter::new(File::create("dap_output.txt")?);
    
    // 创建并运行调试适配器
    let mut adapter = MyDebugAdapter::new(input, output);
    adapter.run()?;
    
    Ok(())
}

许可证

该库采用MIT和Apache 2.0双重许可。用户可以选择其中任一许可证。

商业支持可通过合同方式获得。


1 回复

Rust调试适配器协议库dap的使用

介绍

DAP(Debug Adapter Protocol)是微软开发的一种通用调试协议,允许开发工具与调试器进行通信。Rust的dap库为开发者提供了实现DAP协议的能力,使得可以为Rust项目构建强大的调试功能支持。

这个库主要特点包括:

  • 实现了DAP协议的客户端和服务器端
  • 支持构建自定义调试器或集成现有调试器
  • 提供类型安全的API来处理DAP消息
  • 与VSCode等支持DAP的编辑器兼容

使用方法

基本安装

首先在Cargo.toml中添加依赖:

[dependencies]
dap = "0.5"

创建简单的DAP服务器

以下是一个简单的DAP服务器示例,它接收请求并返回响应:

use dap::prelude::*;
use std::io::{BufRead, BufReader, Write};
use std::net::{TcpListener, TcpStream};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let listener = TcpListener::bind("127.0.0.1:9000")?;
    
    for stream in listener.incoming() {
        let mut stream = stream?;
        let mut reader = BufReader::new(stream.t极Clone()?);
        
        // 处理DAP请求
        let mut adapter = ServerAdapter::new(stream.try_clone()?);
        
        loop {
            let mut buf = String::new();
            if reader.read_line(&mut buf)? == 0 {
                break;
            }
            
            // 解析DAP请求
            if let Ok(request) = serde_json::from_str::<Request>(&buf) {
                match request.command {
                    Command::Initialize(_) => {
                        // 响应初始化请求
                        let response = Response::new_initialize(
                            request.seq,
                            Some(Capabilities {
                                supports_configuration_done_request: Some(true),
                                ..Default::default()
                            }),
                        );
                        adapter.send(response)?;
                    }
                    Command::Launch(_) => {
                        // 响应启动请求
                        let response = Response::new_launch(request.seq);
                        adapter.send(response)?;
                    }
                    _ => {
                        // 默认响应
                        let response = Response::new_empty(request.seq);
                        adapter.send(response)?;
                    }
                }
            }
        }
    }
    
    Ok(())
}

与调试器交互

使用dap库与调试器交互的基本流程:

use dap::prelude::*;
use dap::client::Client;

async fn debug_session() -> Result<(), Box<dyn std::error::Error>> {
    // 创建DAP客户端
    let mut client = Client::connect("127.0.0.1:9000").await?;
    
    // 发送初始化请求
    let capabilities = client.initialize(InitializeRequestArguments {
        adapter_id: "rust-debugger".to_string(),
        ..Default::default()
    }).await?;
    
    println!("Debugger capabilities: {:?}", capabilities);
    
    // 启动调试会话
    let launch_response = client.launch(LaunchRequestArguments {
        no_debug: Some(false),
        ..Default::default()
    }).await?;
    
    // 设置断点
    let breakpoint_response = client.set_breakpoints(SetBreakpointsArguments {
        source: Source {
            name: Some("main.rs".to_string()),
            path: Some("/path/to/main.rs".to_string()),
            ..Default::default()
        },
        breakpoints: vec![
            SourceBreakpoint {
                line: 10,
                ..Default::default()
            }
        ],
        ..Default::default()
    }).await?;
    
    println!("Breakpoints set: {:?}", breakpoint_response.breakpoints);
    
    // 开始执行
    client.configuration_done(ConfigurationDoneArguments::default()).await?;
    
    // 处理调试事件
    while let Some(event) = client.next_event().await {
        match event {
            Event::Stopped(stopped) => {
                println!("Program stopped: {:?}", stopped);
                // 可以在这里检查变量、调用栈等
            }
            Event::Exited(exited) => {
                println!("Program exited with code: {}", exited.exit_code);
                break;
            }
            _ => {}
        }
    }
    
    Ok(())
}

集成现有调试器

如果你已经有一个Rust调试器,可以使用dap库为其添加DAP支持:

use dap::server::Server;
use dap::types::*;

struct MyDebugger {
    // 你的调试器状态
}

impl Server for MyDebugger {
    fn initialize(
        &mut self,
        _args: InitializeRequestArguments,
    ) -> Result<Capabilities, Box<dyn std::error::Error>> {
        Ok(Capabilities {
            supports_configuration_done_request: Some(true),
            supports_set_variable: Some(true),
            // 其他能力声明
            ..Default::default()
        })
    }
    
    fn launch(
        &mut self,
        args: LaunchRequestArguments,
    ) -> Result<Option<Message>, Box极dyn std::error::Error>> {
        // 实现启动逻辑
        Ok(None)
    }
    
    fn set_breakpoints(
        &mut self,
        args: SetBreakpointsArguments,
    ) -> Result<SetBreakpointsResponseBody, Box<dyn std::error::Error>> {
        // 实现断点设置
        Ok(SetBreakpointsResponseBody {
            breakpoints: vec![],
        })
    }
    
    // 实现其他必要的Server trait方法
}

高级用法

自定义事件处理

use dap::base_message::Sendable;
use dap::events::Event;

// 发送自定义事件
adapter.send_event(Event::Custom {
    event: "myCustomEvent".to_string(),
    body: Some(serde_json::json!({
        "data": "custom data here"
    })),
})?;

// 接收自定义事件
while let Some(event) = client.next_event().await {
    if let Event::Custom { event, body } = event {
        if event == "myCustomEvent" {
            println!("Received custom event: {:?}", body);
        }
    }
}

处理变量和作用域

fn variables(
    &mut self,
    args: VariablesArguments,
) -> Result<VariablesResponseBody, Box<dyn std::error::Error>> {
    let variables = vec![
        Variable {
            name: "counter".to_string(),
            value: "42".to_string(),
            type_: Some("i32".to_string()),
            variables_reference: 0,
            ..Default::default()
        },
    ];
    
    Ok(VariablesResponseBody { variables })
}

完整示例

以下是一个完整的DAP服务器和客户端交互示例:

// DAP服务器端
use dap::prelude::*;
use std::io::{BufRead, BufReader};
use std::net::{TcpListener, TcpStream};

struct MyDebugAdapter {
    // 调试器状态
}

impl Server for MyDebugAdapter {
    fn initialize(
        &mut self,
        _args: InitializeRequestArguments,
    ) -> Result<Capabilities, Box<dyn std::error::Error>> {
        Ok(Capabilities {
            supports_configuration_done_request: Some(true),
            supports_set_variable: Some(true),
            supports_breakpoints: Some(true),
            ..Default::default()
        })
    }

    fn launch(
        &mut self,
        _args: LaunchRequestArguments,
    ) -> Result<(), Box<dyn std::error::Error>> {
        println!("Debug session launched");
        Ok(())
    }
}

fn run_server() -> Result<(), Box<dyn std::error::Error>> {
    let listener = TcpListener::bind("127.0.0.1:9000")?;
    println!("DAP server listening on 127.0.0.1:9000");

    for stream in listener.incoming() {
        let stream = stream?;
        let reader = BufReader::new(stream.try_clone()?);
        let mut adapter = ServerAdapter::new(stream);
        let mut debug_adapter = MyDebugAdapter {};

        for line in reader.lines() {
            let line = line?;
            if let Ok(request) = serde_json::from_str::<Request>(&line) {
                match request.command {
                    Command::Initialize(args) => {
                        let response = debug_adapter.initialize(args)?;
                        adapter.send(Response::new_initialize(request.seq, Some(response)))?;
                    }
                    Command::Launch(args) => {
                        debug_adapter.launch(args)?;
                        adapter.send(Response::new_launch(request.seq))?;
                    }
                    _ => {
                        adapter.send(Response::new_empty(request.seq))?;
                    }
                }
            }
        }
    }
    Ok(())
}

// DAP客户端
async fn run_client() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = Client::connect("127.0.0.1:9000").await?;
    
    // 初始化调试会话
    let capabilities = client.initialize(InitializeRequestArguments {
        adapter_id: "example-debugger".to_string(),
        ..Default::default()
    }).await?;
    
    println!("Debugger capabilities: {:?}", capabilities);
    
    // 启动调试会话
    client.launch(LaunchRequestArguments {
        no_debug: Some(false),
        ..Default::default()
    }).await?;
    
    println!("Debug session started");
    
    // 处理调试事件
    while let Some(event) = client.next_event().await {
        match event {
            Event::Initialized(_) => {
                println!("Debugger initialized");
                client.configuration_done(ConfigurationDoneArguments::default()).await?;
            }
            _ => {}
        }
    }
    
    Ok(())
}

#[tokio::main]
async fn main() {
    // 启动服务器和客户端
    tokio::spawn(async {
        run_server().unwrap();
    });
    
    tokio::time::sleep(std::time::Duration::from_secs(1)).await;
    
    if let Err(e) = run_client().await {
        eprintln!("Client error: {}", e);
    }
}

总结

Rust的dap库为开发者提供了强大的调试功能支持,可以用于:

  1. 构建自定义调试器
  2. 为现有工具添加调试支持
  3. 集成到支持DAP协议的IDE中
  4. 创建调试代理或中间件

通过实现DAP协议,你的调试工具可以与VSCode等编辑器无缝集成,提供丰富的调试体验。

回到顶部