From ab8b6afee5e440266233db215d4db73066d541eb Mon Sep 17 00:00:00 2001 From: Zhang Junyang Date: Mon, 30 Sep 2024 13:37:05 +0800 Subject: [PATCH] OSDK GDB server args with QEMU-style options --- Makefile | 5 +- docs/src/osdk/reference/commands/run.md | 25 ++-- osdk/src/cli.rs | 48 ++------ osdk/src/commands/debug.rs | 3 +- osdk/src/commands/run.rs | 151 +++++++++++++++--------- osdk/src/error.rs | 17 +-- osdk/tests/commands/run.rs | 17 +-- 7 files changed, 135 insertions(+), 131 deletions(-) diff --git a/Makefile b/Makefile index 4f2185e9d..b820be36b 100644 --- a/Makefile +++ b/Makefile @@ -199,8 +199,7 @@ endif .PHONY: gdb_server gdb_server: initramfs $(CARGO_OSDK) - @cargo osdk run $(CARGO_OSDK_ARGS) --gdb-server --gdb-wait-client --gdb-vsc \ - --gdb-server-addr :$(GDB_TCP_PORT) + @cargo osdk run $(CARGO_OSDK_ARGS) --gdb-server wait-client,vscode,addr=:$(GDB_TCP_PORT) .PHONY: gdb_client gdb_client: $(CARGO_OSDK) @@ -208,7 +207,7 @@ gdb_client: $(CARGO_OSDK) .PHONY: profile_server profile_server: initramfs $(CARGO_OSDK) - @cargo osdk run $(CARGO_OSDK_ARGS) --gdb-server --gdb-server-addr :$(GDB_TCP_PORT) + @cargo osdk run $(CARGO_OSDK_ARGS) --gdb-server addr=:$(GDB_TCP_PORT) .PHONY: profile_client profile_client: $(CARGO_OSDK) diff --git a/docs/src/osdk/reference/commands/run.md b/docs/src/osdk/reference/commands/run.md index 1b609af7b..7729adb3d 100644 --- a/docs/src/osdk/reference/commands/run.md +++ b/docs/src/osdk/reference/commands/run.md @@ -15,16 +15,15 @@ Most options are the same as those of `cargo osdk build`. Refer to the [documentation](build.md) of `cargo osdk build` for more details. -Options related with debugging: +Additionally, when running the kernel using QEMU, we can setup the QEMU as a +debug server using option `--gdb-server`. This option supports an additional +comma separated configuration list: -- `--gdb-server`: Enable QEMU GDB server for debugging. -- `--gdb-wait-client`: Let the QEMU GDB server wait for the client connection before execution. -- `--gdb-vsc`: Generate a '.vscode/launch.json' for debugging kernel with Visual Studio Code -(only works when QEMU GDB server is enabled, i.e., `--gdb-server`). -Requires [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb). -- `--gdb-server-addr `: The network address on which the GDB server listens, -it can be either a path for the UNIX domain socket or a TCP port on an IP address. -[default: `.osdk-gdb-socket`(a local UNIX socket)] + - `addr=ADDR`: the network or unix socket address on which the GDB server listens + (default: `.osdk-gdb-socket`, a local UNIX socket); + - `wait-client`: let the GDB server wait for the GDB client before execution; + - `vscode`: generate a '.vscode/launch.json' for debugging with Visual Studio Code + (Requires [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb)). See [Debug Command](debug.md) to interact with the GDB server in terminal. @@ -33,17 +32,17 @@ See [Debug Command](debug.md) to interact with the GDB server in terminal. Launch a debug server via QEMU with an unix socket stub, e.g. `.debug`: ```bash -cargo osdk run --gdb-server --gdb-server-addr .debug +cargo osdk run --gdb-server addr=.debug ``` Launch a debug server via QEMU with a TCP stub, e.g., `localhost:1234`: ```bash -cargo osdk run --gdb-server --gdb-server-addr :1234 +cargo osdk run --gdb-server addr=:1234 ``` -Launch a debug server via QEMU and use VSCode to interact: +Launch a debug server via QEMU and use VSCode to interact with: ```bash -cargo osdk run --gdb-server --gdb-vsc --gdb-server-addr :1234 +cargo osdk run --gdb-server wait-client,vscode,addr=:1234 ``` diff --git a/osdk/src/cli.rs b/osdk/src/cli.rs index 1feb8a7ac..0692a1a6d 100644 --- a/osdk/src/cli.rs +++ b/osdk/src/cli.rs @@ -37,7 +37,7 @@ pub fn main() { OsdkSubcommand::Run(run_args) => { execute_run_command( &load_config(&run_args.common_args), - &run_args.gdb_server_args, + run_args.gdb_server.as_deref(), ); } OsdkSubcommand::Debug(debug_args) => { @@ -168,44 +168,20 @@ pub struct BuildArgs { #[derive(Debug, Parser)] pub struct RunArgs { - #[command(flatten)] - pub gdb_server_args: GdbServerArgs, - #[command(flatten)] - pub common_args: CommonArgs, -} - -#[derive(Debug, Args, Clone, Default)] -pub struct GdbServerArgs { #[arg( long = "gdb-server", - help = "Enable the QEMU GDB server for debugging", - default_value_t + help = "Enable the QEMU GDB server for debugging\n\ + This option supports an additional comma separated configuration list:\n\t \ + addr=ADDR: the network or unix socket address on which the GDB server listens, \ + `.osdk-gdb-socket` by default;\n\t \ + wait-client: let the GDB server wait for the GDB client before execution;\n\t \ + vscode: generate a '.vscode/launch.json' for debugging with Visual Studio Code.", + value_name = "[addr=ADDR][,wait-client][,vscode]", + default_missing_value = "" )] - pub enabled: bool, - #[arg( - long = "gdb-wait-client", - help = "Let the QEMU GDB server wait for the GDB client before execution", - default_value_t, - requires = "enabled" - )] - pub wait_client: bool, - #[arg( - long = "gdb-vsc", - help = "Generate a '.vscode/launch.json' for debugging with Visual Studio Code \ - (only works when '--enable-gdb' is enabled)", - default_value_t, - requires = "enabled" - )] - pub vsc_launch_file: bool, - #[arg( - long = "gdb-server-addr", - help = "The network address on which the GDB server listens, \ - it can be either a path for the UNIX domain socket or a TCP port on an IP address.", - value_name = "ADDR", - default_value = ".osdk-gdb-socket", - requires = "enabled" - )] - pub host_addr: String, + pub gdb_server: Option, + #[command(flatten)] + pub common_args: CommonArgs, } #[derive(Debug, Parser)] diff --git a/osdk/src/commands/debug.rs b/osdk/src/commands/debug.rs index 46247abed..a80f9c289 100644 --- a/osdk/src/commands/debug.rs +++ b/osdk/src/commands/debug.rs @@ -18,10 +18,9 @@ pub fn execute_debug_command(_profile: &str, args: &DebugArgs) { let mut gdb = Command::new("gdb"); gdb.args([ + format!("{}", file_path.display()).as_str(), "-ex", format!("target remote {}", remote).as_str(), - "-ex", - format!("file {}", file_path.display()).as_str(), ]); gdb.status().unwrap(); } diff --git a/osdk/src/commands/run.rs b/osdk/src/commands/run.rs index 3bbb9d7f2..223f9ddf6 100644 --- a/osdk/src/commands/run.rs +++ b/osdk/src/commands/run.rs @@ -1,64 +1,29 @@ // SPDX-License-Identifier: MPL-2.0 +use std::process::exit; + +use vsc::VscLaunchConfig; + use super::{build::create_base_and_cached_build, util::DEFAULT_TARGET_RELPATH}; use crate::{ - cli::GdbServerArgs, config::{scheme::ActionChoice, Config}, + error::Errno, + error_msg, util::{get_current_crate_info, get_target_directory}, }; -pub fn execute_run_command(config: &Config, gdb_server_args: &GdbServerArgs) { - if gdb_server_args.enabled { - use std::env; - env::set_var( - "RUSTFLAGS", - env::var("RUSTFLAGS").unwrap_or_default() + " -g", - ); - } - +pub fn execute_run_command(config: &Config, gdb_server_args: Option<&str>) { let cargo_target_directory = get_target_directory(); let osdk_output_directory = cargo_target_directory.join(DEFAULT_TARGET_RELPATH); let target_name = get_current_crate_info().name; let mut config = config.clone(); - if gdb_server_args.enabled { - let qemu_gdb_args = { - let gdb_stub_addr = gdb_server_args.host_addr.as_str(); - match gdb::stub_type_of(gdb_stub_addr) { - gdb::StubAddrType::Unix => { - format!( - " -chardev socket,path={},server=on,wait=off,id=gdb0 -gdb chardev:gdb0", - gdb_stub_addr - ) - } - gdb::StubAddrType::Tcp => { - format!( - " -gdb tcp:{}", - gdb::tcp_addr_util::format_tcp_addr(gdb_stub_addr) - ) - } - } - }; - config.run.qemu.args += &qemu_gdb_args; - if gdb_server_args.wait_client { - config.run.qemu.args += " -S"; - } - - // Ensure debug info added when debugging in the release profile. - if config.run.build.profile.contains("release") { - config - .run - .build - .override_configs - .push(format!("profile.{}.debug=true", config.run.build.profile)); - } - } - let _vsc_launch_file = gdb_server_args.vsc_launch_file.then(|| { - vsc::check_gdb_config(gdb_server_args); - let profile = super::util::profile_name_adapter(&config.run.build.profile); - vsc::VscLaunchConfig::new(profile, &gdb_server_args.host_addr) - }); + let _vsc_launch_file = if let Some(gdb_server_str) = gdb_server_args { + adapt_for_gdb_server(&mut config, gdb_server_str) + } else { + None + }; let default_bundle_directory = osdk_output_directory.join(target_name); let bundle = create_base_and_cached_build( @@ -73,6 +38,82 @@ pub fn execute_run_command(config: &Config, gdb_server_args: &GdbServerArgs) { bundle.run(&config, ActionChoice::Run); } +fn adapt_for_gdb_server(config: &mut Config, gdb_server_str: &str) -> Option { + let gdb_server_args = GdbServerArgs::from_str(gdb_server_str); + + // Add GDB server arguments to QEMU. + let qemu_gdb_args = { + let gdb_stub_addr = gdb_server_args.host_addr.as_str(); + match gdb::stub_type_of(gdb_stub_addr) { + gdb::StubAddrType::Unix => { + format!( + " -chardev socket,path={},server=on,wait=off,id=gdb0 -gdb chardev:gdb0", + gdb_stub_addr + ) + } + gdb::StubAddrType::Tcp => { + format!( + " -gdb tcp:{}", + gdb::tcp_addr_util::format_tcp_addr(gdb_stub_addr) + ) + } + } + }; + config.run.qemu.args += &qemu_gdb_args; + + if gdb_server_args.wait_client { + config.run.qemu.args += " -S"; + } + + // Ensure debug info added when debugging in the release profile. + if config.run.build.profile.contains("release") { + config + .run + .build + .override_configs + .push(format!("profile.{}.debug=true", config.run.build.profile)); + } + + gdb_server_args.vsc_launch_file.then(|| { + vsc::check_gdb_config(&gdb_server_args); + let profile = super::util::profile_name_adapter(&config.run.build.profile); + vsc::VscLaunchConfig::new(profile, &gdb_server_args.host_addr) + }) +} + +struct GdbServerArgs { + host_addr: String, + wait_client: bool, + vsc_launch_file: bool, +} + +impl GdbServerArgs { + fn from_str(args: &str) -> Self { + let mut host_addr = ".osdk-gdb-socket".to_string(); + let mut wait_client = false; + let mut vsc_launch_file = false; + + for arg in args.split(",") { + let kv = arg.split('=').collect::>(); + match kv.as_slice() { + ["addr", addr] => host_addr = addr.to_string(), + ["wait-client"] => wait_client = true, + ["vscode"] => vsc_launch_file = true, + _ => { + error_msg!("Invalid GDB server argument: {}", arg); + exit(Errno::Cli as _); + } + } + } + + GdbServerArgs { + host_addr, + wait_client, + vsc_launch_file, + } + } +} + mod gdb { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum StubAddrType { @@ -115,7 +156,6 @@ mod gdb { mod vsc { use crate::{ - cli::GdbServerArgs, commands::util::bin_file_name, util::{get_cargo_metadata, get_current_crate_info}, }; @@ -125,7 +165,7 @@ mod vsc { path::Path, }; - use super::gdb; + use super::{gdb, GdbServerArgs}; const VSC_DIR: &str = ".vscode"; @@ -174,6 +214,7 @@ mod vsc { } } } + impl Drop for VscLaunchConfig { fn drop(&mut self) { // remove generated files @@ -209,14 +250,6 @@ mod vsc { use crate::{error::Errno, error_msg}; use std::process::exit; - if !args.enabled { - error_msg!( - "No need for a VSCode launch file without launching GDB server,\ - pass '-h' for help" - ); - exit(Errno::ParseMetadata as _); - } - // check GDB server address let gdb_stub_addr = args.host_addr.as_str(); if gdb_stub_addr.is_empty() { @@ -224,7 +257,9 @@ mod vsc { exit(Errno::ParseMetadata as _); } if gdb::stub_type_of(gdb_stub_addr) != gdb::StubAddrType::Tcp { - error_msg!("Non-TCP GDB server address is not supported under '--gdb-vsc' currently"); + error_msg!( + "Non-TCP GDB server address is not supported under '--gdb-server vscode' currently" + ); exit(Errno::ParseMetadata as _); } } diff --git a/osdk/src/error.rs b/osdk/src/error.rs index 1dfdeaa17..41260a993 100644 --- a/osdk/src/error.rs +++ b/osdk/src/error.rs @@ -3,14 +3,15 @@ #[repr(i32)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Errno { - CreateCrate = 1, - GetMetadata = 2, - AddRustToolchain = 3, - ParseMetadata = 4, - ExecuteCommand = 5, - BuildCrate = 6, - RunBundle = 7, - BadCrateName = 8, + Cli = 1, + CreateCrate = 2, + GetMetadata = 3, + AddRustToolchain = 4, + ParseMetadata = 5, + ExecuteCommand = 6, + BuildCrate = 7, + RunBundle = 8, + BadCrateName = 9, } /// Print error message to console diff --git a/osdk/tests/commands/run.rs b/osdk/tests/commands/run.rs index e4f996d5d..716286abf 100644 --- a/osdk/tests/commands/run.rs +++ b/osdk/tests/commands/run.rs @@ -82,9 +82,7 @@ mod qemu_gdb_feature { let mut instance = cargo_osdk([ "run", "--gdb-server", - "--gdb-wait-client", - "--gdb-server-addr", - unix_socket.as_str(), + format!("addr={},wait-client", unix_socket.as_str()).as_str(), ]); instance.current_dir(&workspace.os_dir()); @@ -112,8 +110,8 @@ mod qemu_gdb_feature { let mut gdb = Command::new("gdb"); gdb.args(["-ex", format!("target remote {}", addr).as_str()]); gdb.write_stdin("\n") - .write_stdin("c\n") - .write_stdin("quit\n"); + .write_stdin("quit\n") + .write_stdin("y\n"); gdb.assert().success(); } mod vsc { @@ -132,18 +130,15 @@ mod qemu_gdb_feature { let mut instance = cargo_osdk([ "run", "--gdb-server", - "--gdb-wait-client", - "--gdb-vsc", - "--gdb-server-addr", - addr, + format!("wait-client,vscode,addr={}", addr).as_str(), ]); instance.current_dir(&workspace.os_dir()); let dir = workspace.os_dir(); let bin_file_path = Path::new(&workspace.os_dir()) .join("target") - .join("x86_64-unknown-none") - .join("debug") + .join("osdk") + .join(kernel_name) .join(format!("{}-osdk-bin", kernel_name)); let _gdb = std::thread::spawn(move || { while !bin_file_path.exists() {