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
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)

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`
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 <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
```

View File

@ -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<String>,
#[command(flatten)]
pub common_args: CommonArgs,
}
#[derive(Debug, Parser)]

View File

@ -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();
}

View File

@ -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<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 {
#[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 _);
}
}

View File

@ -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

View File

@ -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() {