OSDK GDB server args with QEMU-style options

This commit is contained in:
Zhang Junyang
2024-09-30 13:37:05 +08:00
committed by Tate, Hongliang Tian
parent 43e3f8cf6b
commit ab8b6afee5
7 changed files with 135 additions and 131 deletions

View File

@ -199,8 +199,7 @@ endif
.PHONY: gdb_server .PHONY: gdb_server
gdb_server: initramfs $(CARGO_OSDK) gdb_server: initramfs $(CARGO_OSDK)
@cargo osdk run $(CARGO_OSDK_ARGS) --gdb-server --gdb-wait-client --gdb-vsc \ @cargo osdk run $(CARGO_OSDK_ARGS) --gdb-server wait-client,vscode,addr=:$(GDB_TCP_PORT)
--gdb-server-addr :$(GDB_TCP_PORT)
.PHONY: gdb_client .PHONY: gdb_client
gdb_client: $(CARGO_OSDK) gdb_client: $(CARGO_OSDK)
@ -208,7 +207,7 @@ gdb_client: $(CARGO_OSDK)
.PHONY: profile_server .PHONY: profile_server
profile_server: initramfs $(CARGO_OSDK) 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 .PHONY: profile_client
profile_client: $(CARGO_OSDK) profile_client: $(CARGO_OSDK)

View File

@ -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` Refer to the [documentation](build.md) of `cargo osdk build`
for more details. 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. - `addr=ADDR`: the network or unix socket address on which the GDB server listens
- `--gdb-wait-client`: Let the QEMU GDB server wait for the client connection before execution. (default: `.osdk-gdb-socket`, a local UNIX socket);
- `--gdb-vsc`: Generate a '.vscode/launch.json' for debugging kernel with Visual Studio Code - `wait-client`: let the GDB server wait for the GDB client before execution;
(only works when QEMU GDB server is enabled, i.e., `--gdb-server`). - `vscode`: generate a '.vscode/launch.json' for debugging with Visual Studio Code
Requires [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb). (Requires [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb)).
- `--gdb-server-addr <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)]
See [Debug Command](debug.md) to interact with the GDB server in terminal. 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`: Launch a debug server via QEMU with an unix socket stub, e.g. `.debug`:
```bash ```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`: Launch a debug server via QEMU with a TCP stub, e.g., `localhost:1234`:
```bash ```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 ```bash
cargo osdk run --gdb-server --gdb-vsc --gdb-server-addr :1234 cargo osdk run --gdb-server wait-client,vscode,addr=:1234
``` ```

View File

@ -37,7 +37,7 @@ pub fn main() {
OsdkSubcommand::Run(run_args) => { OsdkSubcommand::Run(run_args) => {
execute_run_command( execute_run_command(
&load_config(&run_args.common_args), &load_config(&run_args.common_args),
&run_args.gdb_server_args, run_args.gdb_server.as_deref(),
); );
} }
OsdkSubcommand::Debug(debug_args) => { OsdkSubcommand::Debug(debug_args) => {
@ -168,44 +168,20 @@ pub struct BuildArgs {
#[derive(Debug, Parser)] #[derive(Debug, Parser)]
pub struct RunArgs { 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( #[arg(
long = "gdb-server", long = "gdb-server",
help = "Enable the QEMU GDB server for debugging", help = "Enable the QEMU GDB server for debugging\n\
default_value_t 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, pub gdb_server: Option<String>,
#[arg( #[command(flatten)]
long = "gdb-wait-client", pub common_args: CommonArgs,
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,
} }
#[derive(Debug, Parser)] #[derive(Debug, Parser)]

View File

@ -18,10 +18,9 @@ pub fn execute_debug_command(_profile: &str, args: &DebugArgs) {
let mut gdb = Command::new("gdb"); let mut gdb = Command::new("gdb");
gdb.args([ gdb.args([
format!("{}", file_path.display()).as_str(),
"-ex", "-ex",
format!("target remote {}", remote).as_str(), format!("target remote {}", remote).as_str(),
"-ex",
format!("file {}", file_path.display()).as_str(),
]); ]);
gdb.status().unwrap(); gdb.status().unwrap();
} }

View File

@ -1,64 +1,29 @@
// SPDX-License-Identifier: MPL-2.0 // 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 super::{build::create_base_and_cached_build, util::DEFAULT_TARGET_RELPATH};
use crate::{ use crate::{
cli::GdbServerArgs,
config::{scheme::ActionChoice, Config}, config::{scheme::ActionChoice, Config},
error::Errno,
error_msg,
util::{get_current_crate_info, get_target_directory}, util::{get_current_crate_info, get_target_directory},
}; };
pub fn execute_run_command(config: &Config, gdb_server_args: &GdbServerArgs) { pub fn execute_run_command(config: &Config, gdb_server_args: Option<&str>) {
if gdb_server_args.enabled {
use std::env;
env::set_var(
"RUSTFLAGS",
env::var("RUSTFLAGS").unwrap_or_default() + " -g",
);
}
let cargo_target_directory = get_target_directory(); let cargo_target_directory = get_target_directory();
let osdk_output_directory = cargo_target_directory.join(DEFAULT_TARGET_RELPATH); let osdk_output_directory = cargo_target_directory.join(DEFAULT_TARGET_RELPATH);
let target_name = get_current_crate_info().name; let target_name = get_current_crate_info().name;
let mut config = config.clone(); 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 { let _vsc_launch_file = if let Some(gdb_server_str) = gdb_server_args {
config.run.qemu.args += " -S"; adapt_for_gdb_server(&mut config, gdb_server_str)
} } else {
None
// 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 default_bundle_directory = osdk_output_directory.join(target_name); let default_bundle_directory = osdk_output_directory.join(target_name);
let bundle = create_base_and_cached_build( 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); bundle.run(&config, ActionChoice::Run);
} }
fn adapt_for_gdb_server(config: &mut Config, gdb_server_str: &str) -> Option<VscLaunchConfig> {
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::<Vec<_>>();
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 { mod gdb {
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StubAddrType { pub enum StubAddrType {
@ -115,7 +156,6 @@ mod gdb {
mod vsc { mod vsc {
use crate::{ use crate::{
cli::GdbServerArgs,
commands::util::bin_file_name, commands::util::bin_file_name,
util::{get_cargo_metadata, get_current_crate_info}, util::{get_cargo_metadata, get_current_crate_info},
}; };
@ -125,7 +165,7 @@ mod vsc {
path::Path, path::Path,
}; };
use super::gdb; use super::{gdb, GdbServerArgs};
const VSC_DIR: &str = ".vscode"; const VSC_DIR: &str = ".vscode";
@ -174,6 +214,7 @@ mod vsc {
} }
} }
} }
impl Drop for VscLaunchConfig { impl Drop for VscLaunchConfig {
fn drop(&mut self) { fn drop(&mut self) {
// remove generated files // remove generated files
@ -209,14 +250,6 @@ mod vsc {
use crate::{error::Errno, error_msg}; use crate::{error::Errno, error_msg};
use std::process::exit; 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 // check GDB server address
let gdb_stub_addr = args.host_addr.as_str(); let gdb_stub_addr = args.host_addr.as_str();
if gdb_stub_addr.is_empty() { if gdb_stub_addr.is_empty() {
@ -224,7 +257,9 @@ mod vsc {
exit(Errno::ParseMetadata as _); exit(Errno::ParseMetadata as _);
} }
if gdb::stub_type_of(gdb_stub_addr) != gdb::StubAddrType::Tcp { 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 _); exit(Errno::ParseMetadata as _);
} }
} }

View File

@ -3,14 +3,15 @@
#[repr(i32)] #[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Errno { pub enum Errno {
CreateCrate = 1, Cli = 1,
GetMetadata = 2, CreateCrate = 2,
AddRustToolchain = 3, GetMetadata = 3,
ParseMetadata = 4, AddRustToolchain = 4,
ExecuteCommand = 5, ParseMetadata = 5,
BuildCrate = 6, ExecuteCommand = 6,
RunBundle = 7, BuildCrate = 7,
BadCrateName = 8, RunBundle = 8,
BadCrateName = 9,
} }
/// Print error message to console /// Print error message to console

View File

@ -82,9 +82,7 @@ mod qemu_gdb_feature {
let mut instance = cargo_osdk([ let mut instance = cargo_osdk([
"run", "run",
"--gdb-server", "--gdb-server",
"--gdb-wait-client", format!("addr={},wait-client", unix_socket.as_str()).as_str(),
"--gdb-server-addr",
unix_socket.as_str(),
]); ]);
instance.current_dir(&workspace.os_dir()); instance.current_dir(&workspace.os_dir());
@ -112,8 +110,8 @@ mod qemu_gdb_feature {
let mut gdb = Command::new("gdb"); let mut gdb = Command::new("gdb");
gdb.args(["-ex", format!("target remote {}", addr).as_str()]); gdb.args(["-ex", format!("target remote {}", addr).as_str()]);
gdb.write_stdin("\n") gdb.write_stdin("\n")
.write_stdin("c\n") .write_stdin("quit\n")
.write_stdin("quit\n"); .write_stdin("y\n");
gdb.assert().success(); gdb.assert().success();
} }
mod vsc { mod vsc {
@ -132,18 +130,15 @@ mod qemu_gdb_feature {
let mut instance = cargo_osdk([ let mut instance = cargo_osdk([
"run", "run",
"--gdb-server", "--gdb-server",
"--gdb-wait-client", format!("wait-client,vscode,addr={}", addr).as_str(),
"--gdb-vsc",
"--gdb-server-addr",
addr,
]); ]);
instance.current_dir(&workspace.os_dir()); instance.current_dir(&workspace.os_dir());
let dir = workspace.os_dir(); let dir = workspace.os_dir();
let bin_file_path = Path::new(&workspace.os_dir()) let bin_file_path = Path::new(&workspace.os_dir())
.join("target") .join("target")
.join("x86_64-unknown-none") .join("osdk")
.join("debug") .join(kernel_name)
.join(format!("{}-osdk-bin", kernel_name)); .join(format!("{}-osdk-bin", kernel_name));
let _gdb = std::thread::spawn(move || { let _gdb = std::thread::spawn(move || {
while !bin_file_path.exists() { while !bin_file_path.exists() {