mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-10 13:56:48 +00:00
Support GDB server for cargo osdk run
Feature: generate launch config for debugging via VS Code.
This commit is contained in:
parent
a18fc9eb25
commit
5fd3b1bc2f
3
.gitignore
vendored
3
.gitignore
vendored
@ -17,3 +17,6 @@ qemu.log
|
||||
|
||||
# packet dump file
|
||||
virtio-net.pcap
|
||||
|
||||
# vscode launch config file
|
||||
.vscode/launch.json
|
||||
|
@ -6,13 +6,13 @@ use clap::{crate_version, Args, Parser};
|
||||
|
||||
use crate::{
|
||||
commands::{
|
||||
execute_build_command, execute_forwarded_command, execute_new_command, execute_run_command,
|
||||
execute_test_command,
|
||||
execute_build_command, execute_debug_command, execute_forwarded_command,
|
||||
execute_new_command, execute_run_command, execute_test_command,
|
||||
},
|
||||
config_manager::{
|
||||
boot::{BootLoader, BootProtocol},
|
||||
qemu::QemuMachine,
|
||||
BuildConfig, RunConfig, TestConfig,
|
||||
BuildConfig, DebugConfig, RunConfig, TestConfig,
|
||||
},
|
||||
};
|
||||
|
||||
@ -33,6 +33,10 @@ pub fn main() {
|
||||
let run_config = RunConfig::parse(run_args);
|
||||
execute_run_command(&run_config);
|
||||
}
|
||||
OsdkSubcommand::Debug(debug_args) => {
|
||||
let debug_config = DebugConfig::parse(debug_args);
|
||||
execute_debug_command(&debug_config);
|
||||
}
|
||||
OsdkSubcommand::Test(test_args) => {
|
||||
let test_config = TestConfig::parse(test_args);
|
||||
execute_test_command(&test_config);
|
||||
@ -67,6 +71,8 @@ pub enum OsdkSubcommand {
|
||||
Build(BuildArgs),
|
||||
#[command(about = "Run the kernel with a VMM")]
|
||||
Run(RunArgs),
|
||||
#[command(about = "Debug the kernel with a VMM as a client or a server")]
|
||||
Debug(DebugArgs),
|
||||
#[command(about = "Execute kernel mode unit test by starting a VMM")]
|
||||
Test(TestArgs),
|
||||
#[command(about = "Check a local package and all of its dependencies for errors")]
|
||||
@ -109,6 +115,24 @@ pub struct RunArgs {
|
||||
pub cargo_args: CargoArgs,
|
||||
#[command(flatten)]
|
||||
pub osdk_args: OsdkArgs,
|
||||
#[arg(long, short = 'G', help = "Enable QEMU GDB server for debugging")]
|
||||
pub gdb_server: bool,
|
||||
#[arg(
|
||||
long = "vsc",
|
||||
help = "Generate a '.vscode/launch.json' for debugging with Visual Studio Code \
|
||||
(only works when '--gdb-server' is enabled)"
|
||||
)]
|
||||
pub vsc_launch_file: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct DebugArgs {
|
||||
#[command(flatten)]
|
||||
pub cargo_args: CargoArgs,
|
||||
#[command(flatten)]
|
||||
pub osdk_args: OsdkArgs,
|
||||
#[arg(name = "ROLE", help = "'client' or 'server'", default_value = "server")]
|
||||
pub role: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
|
30
osdk/src/commands/debug.rs
Normal file
30
osdk/src/commands/debug.rs
Normal file
@ -0,0 +1,30 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::commands::util::{bin_file_name, profile_adapter};
|
||||
use crate::config_manager::DebugConfig;
|
||||
|
||||
use crate::util::get_target_directory;
|
||||
use std::process::Command;
|
||||
|
||||
pub fn execute_debug_command(config: &DebugConfig) {
|
||||
let DebugConfig {
|
||||
manifest,
|
||||
cargo_args,
|
||||
} = config;
|
||||
|
||||
let profile = profile_adapter(&cargo_args.profile);
|
||||
let file_path = get_target_directory()
|
||||
.join("x86_64-unknown-none")
|
||||
.join(profile)
|
||||
.join(bin_file_name());
|
||||
println!("Debugging {:?}", file_path);
|
||||
|
||||
let mut gdb = Command::new("gdb");
|
||||
gdb.args([
|
||||
"-ex",
|
||||
"target remote :1234",
|
||||
"-ex",
|
||||
format!("file {}", file_path.display()).as_str(),
|
||||
]);
|
||||
gdb.status().unwrap();
|
||||
}
|
12
osdk/src/commands/launch.json.template
Normal file
12
osdk/src/commands/launch.json.template
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Debug Asterinas(#PROFILE#)",
|
||||
"type": "lldb",
|
||||
"request": "custom",
|
||||
"targetCreateCommands": ["target create ${workspaceFolder}/target/x86_64-unknown-none/#PROFILE#/#BIN_NAME#"],
|
||||
"processCreateCommands": ["gdb-remote 1234"]
|
||||
}
|
||||
]
|
||||
}
|
@ -3,14 +3,15 @@
|
||||
//! This module contains subcommands of cargo-osdk.
|
||||
|
||||
mod build;
|
||||
mod debug;
|
||||
mod new;
|
||||
mod run;
|
||||
mod test;
|
||||
mod util;
|
||||
|
||||
pub use self::{
|
||||
build::execute_build_command, new::execute_new_command, run::execute_run_command,
|
||||
test::execute_test_command,
|
||||
build::execute_build_command, debug::execute_debug_command, new::execute_new_command,
|
||||
run::execute_run_command, test::execute_test_command,
|
||||
};
|
||||
|
||||
/// Execute the forwarded cargo command with args containing the subcommand and its arguments.
|
||||
|
@ -9,10 +9,20 @@ use super::{build::create_base_and_build, util::DEFAULT_TARGET_RELPATH};
|
||||
use crate::{
|
||||
bundle::Bundle,
|
||||
config_manager::{BuildConfig, RunConfig},
|
||||
error::Errno,
|
||||
error_msg,
|
||||
util::{get_cargo_metadata, get_current_crate_info, get_target_directory},
|
||||
};
|
||||
|
||||
pub fn execute_run_command(config: &RunConfig) {
|
||||
if config.gdb_server {
|
||||
use std::env;
|
||||
env::set_var(
|
||||
"RUSTFLAGS",
|
||||
env::var("RUSTFLAGS").unwrap_or_default() + " -g",
|
||||
);
|
||||
}
|
||||
|
||||
let ws_target_directory = get_target_directory();
|
||||
let osdk_target_directory = ws_target_directory.join(DEFAULT_TARGET_RELPATH);
|
||||
let target_name = get_current_crate_info().name;
|
||||
@ -40,8 +50,21 @@ pub fn execute_run_command(config: &RunConfig) {
|
||||
}
|
||||
}
|
||||
|
||||
let manifest = if config.gdb_server {
|
||||
let qemu_dbg_args: Vec<_> = vec!["-s", "-S"]
|
||||
.into_iter()
|
||||
.filter(|arg| !config.manifest.qemu.args.iter().any(|x| x == arg))
|
||||
.map(|x| x.to_string())
|
||||
.collect();
|
||||
let mut manifest = config.manifest.clone();
|
||||
manifest.qemu.args.extend(qemu_dbg_args);
|
||||
manifest
|
||||
} else {
|
||||
config.manifest.clone()
|
||||
};
|
||||
|
||||
let required_build_config = BuildConfig {
|
||||
manifest: config.manifest.clone(),
|
||||
manifest: manifest.clone(),
|
||||
cargo_args: config.cargo_args.clone(),
|
||||
};
|
||||
|
||||
@ -53,7 +76,21 @@ pub fn execute_run_command(config: &RunConfig) {
|
||||
&[],
|
||||
);
|
||||
|
||||
bundle.run(config);
|
||||
let _vsc_launch_file = config.vsc_launch_file.then(|| {
|
||||
if !config.gdb_server {
|
||||
error_msg!(
|
||||
"No need for a VSCode launch file without launching GDB server,\
|
||||
pass '-h' for help"
|
||||
);
|
||||
std::process::exit(Errno::ExecuteCommand as _);
|
||||
}
|
||||
let profile = super::util::profile_adapter(&config.cargo_args.profile);
|
||||
vsc::VscLaunchConfig::new(profile)
|
||||
});
|
||||
bundle.run(&RunConfig {
|
||||
manifest,
|
||||
..config.clone()
|
||||
});
|
||||
}
|
||||
|
||||
fn get_last_modified_time(path: impl AsRef<Path>) -> SystemTime {
|
||||
@ -73,3 +110,147 @@ fn get_last_modified_time(path: impl AsRef<Path>) -> SystemTime {
|
||||
}
|
||||
last_modified
|
||||
}
|
||||
|
||||
mod vsc {
|
||||
use crate::{commands::util::bin_file_name, util::get_cargo_metadata};
|
||||
|
||||
use serde_json::{from_str, Value};
|
||||
|
||||
use std::{
|
||||
fs::{read_to_string, write as write_file},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
const VSC_DIR: &str = ".vscode";
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Existence {
|
||||
vsc_dir: bool,
|
||||
launch_file: bool,
|
||||
}
|
||||
|
||||
impl Default for Existence {
|
||||
fn default() -> Self {
|
||||
Existence {
|
||||
vsc_dir: false,
|
||||
launch_file: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn workspace_root() -> String {
|
||||
get_cargo_metadata(None::<&str>, None::<&[&str]>).unwrap()["workspace_root"]
|
||||
.as_str()
|
||||
.unwrap()
|
||||
.to_owned()
|
||||
}
|
||||
#[derive(Debug, Default)]
|
||||
pub struct VscLaunchConfig {
|
||||
existence: Existence,
|
||||
backup_launch_path: Option<String>,
|
||||
}
|
||||
impl VscLaunchConfig {
|
||||
pub fn new(profile: &str) -> Self {
|
||||
let workspace = workspace_root();
|
||||
let workspace = Path::new(&workspace);
|
||||
let launch_file_path = workspace.join(VSC_DIR).join("launch.json");
|
||||
let existence = Existence {
|
||||
vsc_dir: workspace.join(VSC_DIR).exists(),
|
||||
launch_file: launch_file_path.exists(),
|
||||
};
|
||||
|
||||
let backup_launch_path = existence.launch_file.then(|| {
|
||||
let backup_launch_path = launch_file_path.with_extension("bak");
|
||||
std::fs::copy(&launch_file_path, &backup_launch_path).unwrap();
|
||||
backup_launch_path.to_string_lossy().to_string()
|
||||
});
|
||||
|
||||
if !existence.vsc_dir {
|
||||
std::fs::create_dir(workspace.join(VSC_DIR)).unwrap();
|
||||
}
|
||||
generate_vsc_launch_file(&workspace, profile).unwrap();
|
||||
|
||||
VscLaunchConfig {
|
||||
existence,
|
||||
backup_launch_path,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for VscLaunchConfig {
|
||||
fn drop(&mut self) {
|
||||
// remove generated files
|
||||
if !self.existence.vsc_dir {
|
||||
std::fs::remove_dir_all(Path::new(&workspace_root()).join(VSC_DIR)).unwrap();
|
||||
return;
|
||||
}
|
||||
if !self.existence.launch_file {
|
||||
std::fs::remove_file(
|
||||
Path::new(&workspace_root())
|
||||
.join(VSC_DIR)
|
||||
.join("launch.json"),
|
||||
)
|
||||
.unwrap();
|
||||
return;
|
||||
}
|
||||
// restore backup launch file
|
||||
if let Some(backup_launch_path) = &self.backup_launch_path {
|
||||
std::fs::copy(
|
||||
backup_launch_path,
|
||||
Path::new(&workspace_root())
|
||||
.join(VSC_DIR)
|
||||
.join("launch.json"),
|
||||
)
|
||||
.unwrap();
|
||||
std::fs::remove_file(backup_launch_path).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_vsc_launch_file(
|
||||
workspace: impl AsRef<Path>,
|
||||
profile: &str,
|
||||
) -> Result<(), std::io::Error> {
|
||||
let contents = include_str!("launch.json.template")
|
||||
.replace("#PROFILE#", profile)
|
||||
.replace("#BIN_NAME#", &bin_file_name());
|
||||
|
||||
let original_items: Option<Value> = {
|
||||
let launch_file_path = workspace.as_ref().join(VSC_DIR).join("launch.json");
|
||||
let src_path = if launch_file_path.exists() {
|
||||
launch_file_path
|
||||
} else {
|
||||
launch_file_path.with_extension("bak")
|
||||
};
|
||||
src_path
|
||||
.exists()
|
||||
.then(|| from_str(&read_to_string(&src_path).unwrap()).unwrap())
|
||||
};
|
||||
|
||||
let contents = if let Some(mut original_items) = original_items {
|
||||
let items: Value = from_str(&contents)?;
|
||||
for item in items["configurations"].as_array().unwrap() {
|
||||
if original_items["configurations"]
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.any(|x| x["name"].as_str() == item["name"].as_str())
|
||||
{
|
||||
println!("[{}/launch.json]{} already exists", VSC_DIR, item["name"]);
|
||||
// no override for configurations with the same name
|
||||
continue;
|
||||
}
|
||||
original_items["configurations"]
|
||||
.as_array_mut()
|
||||
.unwrap()
|
||||
.push(item.clone());
|
||||
}
|
||||
serde_json::to_string_pretty(&original_items).unwrap()
|
||||
} else {
|
||||
contents
|
||||
};
|
||||
|
||||
let launch_file_path = workspace.as_ref().join(VSC_DIR).join("launch.json");
|
||||
write_file(launch_file_path, contents)
|
||||
}
|
||||
}
|
||||
|
@ -73,6 +73,8 @@ pub static KTEST_CRATE_WHITELIST: Option<&[&str]> = Some(&{:#?});
|
||||
let required_run_config = RunConfig {
|
||||
manifest: required_build_config.manifest.clone(),
|
||||
cargo_args: required_build_config.cargo_args.clone(),
|
||||
gdb_server: false,
|
||||
vsc_launch_file: false,
|
||||
};
|
||||
|
||||
bundle.run(&required_run_config);
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
use std::process::Command;
|
||||
|
||||
use crate::util::get_current_crate_info;
|
||||
|
||||
pub const COMMON_CARGO_ARGS: &[&str] = &[
|
||||
"-Zbuild-std=core,alloc,compiler_builtins",
|
||||
"-Zbuild-std-features=compiler-builtins-mem",
|
||||
@ -12,3 +14,14 @@ pub const DEFAULT_TARGET_RELPATH: &str = "osdk";
|
||||
pub fn cargo() -> Command {
|
||||
Command::new("cargo")
|
||||
}
|
||||
|
||||
pub fn profile_adapter(profile: &str) -> &str {
|
||||
match profile {
|
||||
"dev" => "debug",
|
||||
_ => profile,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bin_file_name() -> String {
|
||||
get_current_crate_info().name + "-osdk-bin"
|
||||
}
|
||||
|
@ -22,13 +22,20 @@ use self::{
|
||||
manifest::{OsdkManifest, TomlManifest},
|
||||
};
|
||||
use crate::{
|
||||
cli::{BuildArgs, CargoArgs, OsdkArgs, RunArgs, TestArgs},
|
||||
cli::{BuildArgs, CargoArgs, DebugArgs, OsdkArgs, RunArgs, TestArgs},
|
||||
error::Errno,
|
||||
error_msg,
|
||||
util::get_cargo_metadata,
|
||||
warn_msg,
|
||||
};
|
||||
|
||||
fn get_final_manifest(cargo_args: &CargoArgs, osdk_args: &OsdkArgs) -> OsdkManifest {
|
||||
let mut manifest = load_osdk_manifest(cargo_args, osdk_args.select.as_ref());
|
||||
apply_cli_args(&mut manifest, &osdk_args);
|
||||
try_fill_system_configs(&mut manifest);
|
||||
manifest
|
||||
}
|
||||
|
||||
/// Configurations for build subcommand
|
||||
#[derive(Debug)]
|
||||
pub struct BuildConfig {
|
||||
@ -39,31 +46,45 @@ pub struct BuildConfig {
|
||||
impl BuildConfig {
|
||||
pub fn parse(args: &BuildArgs) -> Self {
|
||||
let cargo_args = parse_cargo_args(&args.cargo_args);
|
||||
let mut manifest = load_osdk_manifest(&cargo_args, args.osdk_args.select.as_ref());
|
||||
apply_cli_args(&mut manifest, &args.osdk_args);
|
||||
try_fill_system_configs(&mut manifest);
|
||||
Self {
|
||||
manifest,
|
||||
manifest: get_final_manifest(&cargo_args, &args.osdk_args),
|
||||
cargo_args,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Configurations for run subcommand
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RunConfig {
|
||||
pub manifest: OsdkManifest,
|
||||
pub cargo_args: CargoArgs,
|
||||
pub gdb_server: bool,
|
||||
pub vsc_launch_file: bool,
|
||||
}
|
||||
|
||||
impl RunConfig {
|
||||
pub fn parse(args: &RunArgs) -> Self {
|
||||
let cargo_args = parse_cargo_args(&args.cargo_args);
|
||||
let mut manifest = load_osdk_manifest(&cargo_args, args.osdk_args.select.as_ref());
|
||||
apply_cli_args(&mut manifest, &args.osdk_args);
|
||||
try_fill_system_configs(&mut manifest);
|
||||
Self {
|
||||
manifest,
|
||||
manifest: get_final_manifest(&cargo_args, &args.osdk_args),
|
||||
cargo_args,
|
||||
gdb_server: args.gdb_server,
|
||||
vsc_launch_file: args.vsc_launch_file,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DebugConfig {
|
||||
pub manifest: OsdkManifest,
|
||||
pub cargo_args: CargoArgs,
|
||||
}
|
||||
|
||||
impl DebugConfig {
|
||||
pub fn parse(args: &DebugArgs) -> Self {
|
||||
let cargo_args = split_features(&args.cargo_args);
|
||||
Self {
|
||||
manifest: get_final_manifest(&cargo_args, &args.osdk_args),
|
||||
cargo_args,
|
||||
}
|
||||
}
|
||||
@ -80,11 +101,8 @@ pub struct TestConfig {
|
||||
impl TestConfig {
|
||||
pub fn parse(args: &TestArgs) -> Self {
|
||||
let cargo_args = parse_cargo_args(&args.cargo_args);
|
||||
let mut manifest = load_osdk_manifest(&cargo_args, args.osdk_args.select.as_ref());
|
||||
apply_cli_args(&mut manifest, &args.osdk_args);
|
||||
try_fill_system_configs(&mut manifest);
|
||||
Self {
|
||||
manifest,
|
||||
manifest: get_final_manifest(&cargo_args, &args.osdk_args),
|
||||
cargo_args,
|
||||
test_name: args.test_name.clone(),
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user