mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-10 13:56:48 +00:00
Introduce cargo osdk profile
for flame graph profiling
This commit is contained in:
parent
ee1656ba35
commit
eca9aacbf0
30
Makefile
30
Makefile
@ -1,13 +1,14 @@
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
# Global options.
|
||||
# =========================== Makefile options. ===============================
|
||||
|
||||
# Global build options.
|
||||
ARCH ?= x86_64
|
||||
BENCHMARK ?= none
|
||||
BOOT_METHOD ?= grub-rescue-iso
|
||||
BOOT_PROTOCOL ?= multiboot2
|
||||
BUILD_SYSCALL_TEST ?= 0
|
||||
ENABLE_KVM ?= 1
|
||||
GDB_TCP_PORT ?= 1234
|
||||
INTEL_TDX ?= 0
|
||||
MEM ?= 8G
|
||||
RELEASE ?= 0
|
||||
@ -16,7 +17,14 @@ LOG_LEVEL ?= error
|
||||
SCHEME ?= ""
|
||||
SMP ?= 1
|
||||
OSTD_TASK_STACK_SIZE_IN_PAGES ?= 64
|
||||
# End of global options.
|
||||
# End of global build options.
|
||||
|
||||
# GDB debugging and profiling options.
|
||||
GDB_TCP_PORT ?= 1234
|
||||
GDB_PROFILE_FORMAT ?= folded
|
||||
GDB_PROFILE_COUNT ?= 200
|
||||
GDB_PROFILE_INTERVAL ?= 0.1
|
||||
# End of GDB options.
|
||||
|
||||
# The Makefile provides a way to run arbitrary tests in the kernel
|
||||
# mode using the kernel command line.
|
||||
@ -26,6 +34,8 @@ EXTRA_BLOCKLISTS_DIRS ?= ""
|
||||
SYSCALL_TEST_DIR ?= /tmp
|
||||
# End of auto test features.
|
||||
|
||||
# ========================= End of Makefile options. ==========================
|
||||
|
||||
CARGO_OSDK := ~/.cargo/bin/cargo-osdk
|
||||
|
||||
CARGO_OSDK_ARGS := --target-arch=$(ARCH) --kcmd-args="ostd.log_level=$(LOG_LEVEL)"
|
||||
@ -189,11 +199,21 @@ endif
|
||||
|
||||
.PHONY: gdb_server
|
||||
gdb_server: initramfs $(CARGO_OSDK)
|
||||
@cargo osdk run $(CARGO_OSDK_ARGS) -G --vsc --gdb-server-addr :$(GDB_TCP_PORT)
|
||||
@cargo osdk run $(CARGO_OSDK_ARGS) --gdb-server --gdb-wait-client --gdb-vsc \
|
||||
--gdb-server-addr :$(GDB_TCP_PORT)
|
||||
|
||||
.PHONY: gdb_client
|
||||
gdb_client: $(CARGO_OSDK)
|
||||
@cd kernel && cargo osdk debug $(CARGO_OSDK_ARGS) --remote :$(GDB_TCP_PORT)
|
||||
@cargo osdk debug $(CARGO_OSDK_ARGS) --remote :$(GDB_TCP_PORT)
|
||||
|
||||
.PHONY: profile_server
|
||||
profile_server: initramfs $(CARGO_OSDK)
|
||||
@cargo osdk run $(CARGO_OSDK_ARGS) --gdb-server --gdb-server-addr :$(GDB_TCP_PORT)
|
||||
|
||||
.PHONY: profile_client
|
||||
profile_client: $(CARGO_OSDK)
|
||||
@cargo osdk profile $(CARGO_OSDK_ARGS) --remote :$(GDB_TCP_PORT) \
|
||||
--samples $(GDB_PROFILE_COUNT) --interval $(GDB_PROFILE_INTERVAL) --format $(GDB_PROFILE_FORMAT)
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
|
222
osdk/Cargo.lock
generated
222
osdk/Cargo.lock
generated
@ -35,6 +35,21 @@ version = "0.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.12"
|
||||
@ -98,6 +113,12 @@ dependencies = [
|
||||
"wait-timeout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
@ -124,6 +145,12 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.17.0"
|
||||
@ -149,9 +176,11 @@ name = "cargo-osdk"
|
||||
version = "0.8.3"
|
||||
dependencies = [
|
||||
"assert_cmd",
|
||||
"chrono",
|
||||
"clap",
|
||||
"env_logger",
|
||||
"indexmap",
|
||||
"indicatif",
|
||||
"lazy_static",
|
||||
"linux-bzimage-builder",
|
||||
"log",
|
||||
@ -166,12 +195,35 @@ dependencies = [
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.1.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"wasm-bindgen",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.1"
|
||||
@ -218,6 +270,25 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.15.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb"
|
||||
dependencies = [
|
||||
"encode_unicode",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"unicode-width",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||
|
||||
[[package]]
|
||||
name = "core2"
|
||||
version = "0.4.0"
|
||||
@ -283,6 +354,12 @@ version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
||||
|
||||
[[package]]
|
||||
name = "env_filter"
|
||||
version = "0.1.0"
|
||||
@ -344,6 +421,29 @@ version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.2.3"
|
||||
@ -354,12 +454,43 @@ dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indicatif"
|
||||
version = "0.17.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3"
|
||||
dependencies = [
|
||||
"console",
|
||||
"instant",
|
||||
"number_prefix",
|
||||
"portable-atomic",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
@ -419,12 +550,33 @@ version = "2.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "number_prefix"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d30538d42559de6b034bc76fd6dd4c38961b1ee5c6c56e3808c50128fdbc22ce"
|
||||
|
||||
[[package]]
|
||||
name = "predicates"
|
||||
version = "3.1.0"
|
||||
@ -647,6 +799,12 @@ version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
@ -668,6 +826,70 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
|
@ -19,8 +19,10 @@ version = "0.2.0"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.4.17", features = ["cargo", "derive"] }
|
||||
chrono = "0.4.38"
|
||||
env_logger = "0.11.0"
|
||||
indexmap = "2.2.1"
|
||||
indicatif = "0.17.8" # For a commandline progress bar
|
||||
lazy_static = "1.4.0"
|
||||
log = "0.4.20"
|
||||
quote = "1.0.35"
|
||||
|
151
osdk/src/cli.rs
151
osdk/src/cli.rs
@ -2,13 +2,13 @@
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::{crate_version, Args, Parser};
|
||||
use clap::{crate_version, Args, Parser, ValueEnum};
|
||||
|
||||
use crate::{
|
||||
arch::Arch,
|
||||
commands::{
|
||||
execute_build_command, execute_debug_command, execute_forwarded_command,
|
||||
execute_new_command, execute_run_command, execute_test_command,
|
||||
execute_new_command, execute_profile_command, execute_run_command, execute_test_command,
|
||||
},
|
||||
config::{
|
||||
manifest::{ProjectType, TomlManifest},
|
||||
@ -46,6 +46,12 @@ pub fn main() {
|
||||
debug_args,
|
||||
);
|
||||
}
|
||||
OsdkSubcommand::Profile(profile_args) => {
|
||||
execute_profile_command(
|
||||
&load_config(&profile_args.common_args).run.build.profile,
|
||||
profile_args,
|
||||
);
|
||||
}
|
||||
OsdkSubcommand::Test(test_args) => {
|
||||
execute_test_command(&load_config(&test_args.common_args), test_args);
|
||||
}
|
||||
@ -79,6 +85,8 @@ pub enum OsdkSubcommand {
|
||||
Run(RunArgs),
|
||||
#[command(about = "Debug a remote target via GDB")]
|
||||
Debug(DebugArgs),
|
||||
#[command(about = "Profile a remote GDB debug target to collect stack traces for flame graph")]
|
||||
Profile(ProfileArgs),
|
||||
#[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")]
|
||||
@ -168,19 +176,25 @@ pub struct RunArgs {
|
||||
|
||||
#[derive(Debug, Args, Clone, Default)]
|
||||
pub struct GdbServerArgs {
|
||||
/// Whether to enable QEMU GDB server for debugging
|
||||
#[arg(
|
||||
long = "enable-gdb",
|
||||
short = 'G',
|
||||
help = "Enable QEMU GDB server for debugging",
|
||||
long = "gdb-server",
|
||||
help = "Enable the QEMU GDB server for debugging",
|
||||
default_value_t
|
||||
)]
|
||||
pub is_gdb_enabled: bool,
|
||||
pub enabled: bool,
|
||||
#[arg(
|
||||
long = "vsc",
|
||||
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
|
||||
default_value_t,
|
||||
requires = "enabled"
|
||||
)]
|
||||
pub vsc_launch_file: bool,
|
||||
#[arg(
|
||||
@ -188,9 +202,10 @@ pub struct GdbServerArgs {
|
||||
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 = ".aster-gdb-socket"
|
||||
default_value = ".aster-gdb-socket",
|
||||
requires = "enabled"
|
||||
)]
|
||||
pub gdb_server_addr: String,
|
||||
pub host_addr: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
@ -205,6 +220,120 @@ pub struct DebugArgs {
|
||||
pub common_args: CommonArgs,
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct ProfileArgs {
|
||||
#[arg(
|
||||
long,
|
||||
help = "Specify the address of the remote target",
|
||||
default_value = ".aster-gdb-socket"
|
||||
)]
|
||||
pub remote: String,
|
||||
#[arg(long, help = "The number of samples to collect", default_value = "200")]
|
||||
pub samples: usize,
|
||||
#[arg(
|
||||
long,
|
||||
help = "The interval between samples in seconds",
|
||||
default_value = "0.1"
|
||||
)]
|
||||
pub interval: f64,
|
||||
#[arg(
|
||||
long,
|
||||
help = "Parse a collected JSON profile file into other formats",
|
||||
value_name = "PATH",
|
||||
conflicts_with = "samples",
|
||||
conflicts_with = "interval"
|
||||
)]
|
||||
pub parse: Option<PathBuf>,
|
||||
#[command(flatten)]
|
||||
pub out_args: DebugProfileOutArgs,
|
||||
#[command(flatten)]
|
||||
pub common_args: CommonArgs,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, ValueEnum)]
|
||||
pub enum ProfileFormat {
|
||||
/// The raw stack trace log parsed from GDB in JSON
|
||||
Json,
|
||||
/// The folded stack trace for a
|
||||
/// [flame graph](https://github.com/brendangregg/FlameGraph)
|
||||
Folded,
|
||||
}
|
||||
|
||||
impl ProfileFormat {
|
||||
pub fn file_extension(&self) -> &'static str {
|
||||
match self {
|
||||
ProfileFormat::Json => "json",
|
||||
ProfileFormat::Folded => "folded",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct DebugProfileOutArgs {
|
||||
#[arg(long, help = "The output format for the profile data")]
|
||||
format: Option<ProfileFormat>,
|
||||
#[arg(
|
||||
long,
|
||||
help = "The mask of the CPU to generate traces for in the output profile data",
|
||||
default_value_t = u128::MAX
|
||||
)]
|
||||
pub cpu_mask: u128,
|
||||
#[arg(
|
||||
long,
|
||||
help = "The path to the output profile data file",
|
||||
value_name = "PATH"
|
||||
)]
|
||||
output: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl DebugProfileOutArgs {
|
||||
/// Get the output format for the profile data.
|
||||
///
|
||||
/// If the user does not specify the format, it will be inferred from the
|
||||
/// output file extension. If the output file does not have an extension,
|
||||
/// the default format is folded stack traces.
|
||||
pub fn format(&self) -> ProfileFormat {
|
||||
self.format.unwrap_or_else(|| {
|
||||
if self.output.is_some() {
|
||||
match self.output.as_ref().unwrap().extension() {
|
||||
Some(ext) if ext == "folded" => ProfileFormat::Folded,
|
||||
Some(ext) if ext == "json" => ProfileFormat::Json,
|
||||
_ => ProfileFormat::Folded,
|
||||
}
|
||||
} else {
|
||||
ProfileFormat::Folded
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the output path for the profile data.
|
||||
///
|
||||
/// If the user does not specify the output path, it will be generated from
|
||||
/// the current time stamp and the format. The caller can provide a hint
|
||||
/// output path to the file to override the file name.
|
||||
pub fn output_path(&self, hint: Option<&PathBuf>) -> PathBuf {
|
||||
self.output.clone().unwrap_or_else(|| {
|
||||
use chrono::{offset::Local, DateTime};
|
||||
let file_stem = if let Some(hint) = hint {
|
||||
format!(
|
||||
"{}",
|
||||
hint.parent()
|
||||
.unwrap()
|
||||
.join(hint.file_stem().unwrap())
|
||||
.display()
|
||||
)
|
||||
} else {
|
||||
let crate_name = crate::util::get_current_crate_info().name;
|
||||
let time_stamp = std::time::SystemTime::now();
|
||||
let time_stamp: DateTime<Local> = time_stamp.into();
|
||||
let time_stamp = time_stamp.format("%H%M%S");
|
||||
format!("{}-profile-{}", crate_name, time_stamp)
|
||||
};
|
||||
PathBuf::from(format!("{}.{}", file_stem, self.format().file_extension()))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct TestArgs {
|
||||
#[arg(
|
||||
|
@ -5,13 +5,14 @@
|
||||
mod build;
|
||||
mod debug;
|
||||
mod new;
|
||||
mod profile;
|
||||
mod run;
|
||||
mod test;
|
||||
mod util;
|
||||
|
||||
pub use self::{
|
||||
build::execute_build_command, debug::execute_debug_command, new::execute_new_command,
|
||||
run::execute_run_command, test::execute_test_command,
|
||||
profile::execute_profile_command, run::execute_run_command, test::execute_test_command,
|
||||
};
|
||||
|
||||
use crate::arch::get_default_arch;
|
||||
|
363
osdk/src/commands/profile.rs
Normal file
363
osdk/src/commands/profile.rs
Normal file
@ -0,0 +1,363 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! OSDK profile command implementation.
|
||||
//!
|
||||
//! The profile command is used to collect stack traces when running the target
|
||||
//! kernel in QEMU. It attaches to the GDB server initiated with [`super::run`]
|
||||
//! and collects the stack trace periodically. The collected data can be
|
||||
//! further analyzed using tools like
|
||||
//! [flame graph](https://github.com/brendangregg/FlameGraph).
|
||||
|
||||
use crate::{
|
||||
cli::{ProfileArgs, ProfileFormat},
|
||||
commands::util::bin_file_name,
|
||||
util::{get_current_crate_info, get_target_directory},
|
||||
};
|
||||
use regex::Regex;
|
||||
use std::{collections::HashMap, fs::File, io::Write, path::PathBuf, process::Command};
|
||||
|
||||
pub fn execute_profile_command(_profile: &str, args: &ProfileArgs) {
|
||||
if let Some(parse_input) = &args.parse {
|
||||
do_parse_stack_traces(parse_input, args);
|
||||
} else {
|
||||
do_collect_stack_traces(args);
|
||||
}
|
||||
}
|
||||
|
||||
fn do_parse_stack_traces(target_file: &PathBuf, args: &ProfileArgs) {
|
||||
let out_args = &args.out_args;
|
||||
let in_file = File::open(target_file).expect("Failed to open input file");
|
||||
let profile: Profile =
|
||||
serde_json::from_reader(in_file).expect("Failed to parse the input JSON file");
|
||||
let out_file = File::create(out_args.output_path(Some(target_file)))
|
||||
.expect("Failed to create output file");
|
||||
|
||||
let out_format = out_args.format();
|
||||
if matches!(out_format, ProfileFormat::Json) {
|
||||
println!("Warning: parsing JSON profile to the same format.");
|
||||
return;
|
||||
}
|
||||
profile.serialize_to(out_format, out_args.cpu_mask, out_file);
|
||||
}
|
||||
|
||||
fn do_collect_stack_traces(args: &ProfileArgs) {
|
||||
let file_path = get_target_directory()
|
||||
.join("osdk")
|
||||
.join(get_current_crate_info().name)
|
||||
.join(bin_file_name());
|
||||
|
||||
let remote = &args.remote;
|
||||
let samples = &args.samples;
|
||||
let interval = &args.interval;
|
||||
|
||||
let mut profile_buffer = ProfileBuffer::new();
|
||||
|
||||
println!("Profiling \"{}\" at \"{}\".", file_path.display(), remote);
|
||||
use indicatif::{ProgressIterator, ProgressStyle};
|
||||
let style = ProgressStyle::default_bar().progress_chars("#>-");
|
||||
for _ in (0..*samples).progress_with_style(style) {
|
||||
// Use GDB to halt the remote, get stack traces, and resume
|
||||
let output = Command::new("gdb")
|
||||
.args([
|
||||
"-batch",
|
||||
"-ex",
|
||||
"set pagination 0",
|
||||
"-ex",
|
||||
&format!("file {}", file_path.display()),
|
||||
"-ex",
|
||||
&format!("target remote {}", remote),
|
||||
"-ex",
|
||||
"thread apply all bt -frame-arguments presence -frame-info short-location",
|
||||
])
|
||||
.output()
|
||||
.expect("Failed to execute gdb");
|
||||
|
||||
for line in String::from_utf8_lossy(&output.stdout).lines() {
|
||||
profile_buffer.append_raw_line(line);
|
||||
}
|
||||
|
||||
// Sleep between samples
|
||||
std::thread::sleep(std::time::Duration::from_secs_f64(*interval));
|
||||
}
|
||||
|
||||
let out_args = &args.out_args;
|
||||
let out_path = out_args.output_path(None);
|
||||
println!(
|
||||
"Profile data collected. Writing the output to \"{}\".",
|
||||
out_path.display()
|
||||
);
|
||||
|
||||
let out_file = File::create(out_path).expect("Failed to create output file");
|
||||
profile_buffer
|
||||
.cur_profile
|
||||
.serialize_to(out_args.format(), out_args.cpu_mask, out_file);
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
struct Profile {
|
||||
// Index 0: capture; Index 1: CPU ID; Index 2: stack frame
|
||||
stack_traces: Vec<HashMap<u32, Vec<String>>>,
|
||||
}
|
||||
|
||||
impl Profile {
|
||||
fn serialize_to<W: Write>(&self, format: ProfileFormat, cpu_mask: u128, mut target: W) {
|
||||
match format {
|
||||
ProfileFormat::Folded => {
|
||||
let mut folded = HashMap::new();
|
||||
|
||||
// Process each stack trace and fold it for flame graph format
|
||||
for capture in &self.stack_traces {
|
||||
for (cpu_id, stack) in capture {
|
||||
if *cpu_id >= 128 || cpu_mask & (1u128 << *cpu_id) == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Fold the stack trace
|
||||
let folded_key = stack.iter().rev().cloned().collect::<Vec<_>>().join(";");
|
||||
*folded.entry(folded_key).or_insert(0) += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Write the folded traces
|
||||
for (key, count) in folded {
|
||||
writeln!(&mut target, "{} {}", key, count)
|
||||
.expect("Failed to write folded output");
|
||||
}
|
||||
}
|
||||
ProfileFormat::Json => {
|
||||
// Filter out the stack traces based on the CPU mask
|
||||
let filtered_traces = self
|
||||
.stack_traces
|
||||
.iter()
|
||||
.map(|capture| {
|
||||
capture
|
||||
.iter()
|
||||
.filter(|(cpu_id, _)| {
|
||||
**cpu_id < 128 && cpu_mask & (1u128 << **cpu_id) != 0
|
||||
})
|
||||
.map(|(cpu_id, stack)| (*cpu_id, stack.clone()))
|
||||
.collect::<HashMap<_, _>>()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let filtered = Profile {
|
||||
stack_traces: filtered_traces,
|
||||
};
|
||||
|
||||
serde_json::to_writer(target, &filtered).expect("Failed to write JSON output");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ProfileBuffer {
|
||||
cur_profile: Profile,
|
||||
// Pre-compile regex patterns for cleaning the input.
|
||||
hex_in_pattern: Regex,
|
||||
impl_pattern: Regex,
|
||||
// The state
|
||||
cur_cpu: Option<u32>,
|
||||
}
|
||||
|
||||
impl ProfileBuffer {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
cur_profile: Profile::default(),
|
||||
hex_in_pattern: Regex::new(r"0x[0-9a-f]+ in").unwrap(),
|
||||
impl_pattern: Regex::new(r"::\{.*?\}").unwrap(),
|
||||
cur_cpu: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn append_raw_line(&mut self, line: &str) {
|
||||
// Lines starting with '#' are stack frames
|
||||
if !line.starts_with('#') {
|
||||
// Otherwise it may initiate a new capture or a new CPU stack trace
|
||||
|
||||
// Check if this is a new CPU trace (starts with `Thread` and contains `CPU#N`)
|
||||
if line.starts_with("Thread") {
|
||||
let cpu_id_idx = line.find("CPU#").unwrap();
|
||||
let cpu_id = line[cpu_id_idx + 4..]
|
||||
.split_whitespace()
|
||||
.next()
|
||||
.unwrap()
|
||||
.parse::<u32>()
|
||||
.unwrap();
|
||||
self.cur_cpu = Some(cpu_id);
|
||||
|
||||
// if the new CPU id is already in the stack traces, start a new capture
|
||||
match self.cur_profile.stack_traces.last() {
|
||||
Some(capture) => {
|
||||
if capture.contains_key(&cpu_id) {
|
||||
self.cur_profile.stack_traces.push(HashMap::new());
|
||||
}
|
||||
}
|
||||
None => {
|
||||
self.cur_profile.stack_traces.push(HashMap::new());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Clean the input line
|
||||
let mut processed = line.trim().to_string();
|
||||
|
||||
// Remove everything between angle brackets '<...>'
|
||||
processed = Self::remove_generics(&processed);
|
||||
|
||||
// Remove "::impl{}" and hex addresses
|
||||
processed = self.impl_pattern.replace_all(&processed, "").to_string();
|
||||
processed = self.hex_in_pattern.replace_all(&processed, "").to_string();
|
||||
|
||||
// Remove unnecessary parts like "()" and "(...)"
|
||||
processed = processed.replace("(...)", "");
|
||||
processed = processed.replace("()", "");
|
||||
|
||||
// Split the line by spaces and expect the second part to be the function name
|
||||
let parts: Vec<&str> = processed.split_whitespace().collect();
|
||||
if parts.len() > 1 {
|
||||
let func_name = parts[1].to_string();
|
||||
|
||||
// Append the function name to the latest stack trace
|
||||
let current_capture = self.cur_profile.stack_traces.last_mut().unwrap();
|
||||
let cur_cpu = self.cur_cpu.unwrap();
|
||||
current_capture.entry(cur_cpu).or_default().push(func_name);
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_generics(line: &str) -> String {
|
||||
let mut result = String::new();
|
||||
let mut bracket_depth = 0;
|
||||
|
||||
for c in line.chars() {
|
||||
match c {
|
||||
'<' => bracket_depth += 1,
|
||||
'>' => {
|
||||
if bracket_depth > 0 {
|
||||
bracket_depth -= 1;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if bracket_depth == 0 {
|
||||
result.push(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn test_profile_parse_raw() {
|
||||
let test_case = r#"
|
||||
0xffffffff880b0f6f in aster_nix::sched::priority_scheduler::{impl#4}::pick_next_current<ostd::task::Task> (self=0xffffffff88489808 <_ZN4ostd2mm14heap_allocator10HEAP_SPACE17h85a5340e6564f69dE.llvm.15305379556759765072+992480>) at src/sched/priority_scheduler.rs:156
|
||||
156 let next_entity = if !self.real_time_entities.is_empty() {
|
||||
|
||||
Thread 2 (Thread 1.2 (CPU#1 [running])):
|
||||
#0 ostd::sync::spin::SpinLock<aster_nix::sched::priority_scheduler::PreemptRunQueue<ostd::task::Task>, ostd::sync::spin::PreemptDisabled>::acquire_lock<aster_nix::sched::priority_scheduler::PreemptRunQueue<ostd::task::Task>, ostd::sync::spin::PreemptDisabled> (...)
|
||||
#1 ostd::sync::spin::SpinLock<aster_nix::sched::priority_scheduler::PreemptRunQueue<ostd::task::Task>, ostd::sync::spin::PreemptDisabled>::lock<aster_nix::sched::priority_scheduler::PreemptRunQueue<ostd::task::Task>, ostd::sync::spin::PreemptDisabled> (...)
|
||||
#2 aster_nix::sched::priority_scheduler::{impl#1}::local_mut_rq_with<ostd::task::Task> (...)
|
||||
#3 0xffffffff8826b205 in ostd::task::scheduler::reschedule<ostd::task::scheduler::yield_now::{closure_env#0}> (...)
|
||||
#4 ostd::task::scheduler::yield_now ()
|
||||
#5 0xffffffff880a92c5 in ostd::task::Task::yield_now ()
|
||||
#6 aster_nix::thread::Thread::yield_now ()
|
||||
#7 aster_nix::ap_init::ap_idle_thread ()
|
||||
#8 core::ops::function::Fn::call<fn(), ()> ()
|
||||
#9 0xffffffff880b341e in alloc::boxed::{impl#50}::call<(), (dyn core::ops::function::Fn<(), Output=()> + core::marker::Send + core::marker::Sync), alloc::alloc::Global> (...)
|
||||
#10 aster_nix::thread::kernel_thread::create_new_kernel_task::{closure#0} ()
|
||||
#11 0xffffffff882a3ea8 in alloc::boxed::{impl#50}::call<(), (dyn core::ops::function::Fn<(), Output=()> + core::marker::Send + core::marker::Sync), alloc::alloc::Global> (...)
|
||||
#12 ostd::task::{impl#2}::build::kernel_task_entry ()
|
||||
#13 0x0000000000000000 in ?? ()
|
||||
|
||||
Thread 1 (Thread 1.1 (CPU#0 [running])):
|
||||
#0 aster_nix::sched::priority_scheduler::{impl#1}::local_mut_rq_with<ostd::task::Task> (...)
|
||||
#1 0xffffffff8826b205 in ostd::task::scheduler::reschedule<ostd::task::scheduler::yield_now::{closure_env#0}> (...)
|
||||
#2 ostd::task::scheduler::yield_now ()
|
||||
#3 0xffffffff880a92c5 in ostd::task::Task::yield_now ()
|
||||
#4 aster_nix::thread::Thread::yield_now ()
|
||||
#5 aster_nix::ap_init::ap_idle_thread ()
|
||||
#6 core::ops::function::Fn::call<fn(), ()> ()
|
||||
#7 0xffffffff880b341e in alloc::boxed::{impl#50}::call<(), (dyn core::ops::function::Fn<(), Output=()> + core::marker::Send + core::marker::Sync), alloc::alloc::Global> (...)
|
||||
#8 aster_nix::thread::kernel_thread::create_new_kernel_task::{closure#0} ()
|
||||
#9 0xffffffff882a3ea8 in alloc::boxed::{impl#50}::call<(), (dyn core::ops::function::Fn<(), Output=()> + core::marker::Send + core::marker::Sync), alloc::alloc::Global> (...)
|
||||
#10 ostd::task::{impl#2}::build::kernel_task_entry ()
|
||||
#11 0x0000000000000000 in ?? ()
|
||||
[Inferior 1 (process 1) detached]
|
||||
0xffffffff880b0f6f in aster_nix::sched::priority_scheduler::{impl#4}::pick_next_current<ostd::task::Task> (self=0xffffffff88489808 <_ZN4ostd2mm14heap_allocator10HEAP_SPACE17h85a5340e6564f69dE.llvm.15305379556759765072+992480>) at src/sched/priority_scheduler.rs:156
|
||||
156 let next_entity = if !self.real_time_entities.is_empty() {
|
||||
|
||||
Thread 2 (Thread 1.2 (CPU#1 [running])):
|
||||
#0 0xffffffff880b0f6f in aster_nix::sched::priority_scheduler::{impl#4}::pick_next_current<ostd::task::Task> (...)
|
||||
#1 0xffffffff8826b3e0 in ostd::task::scheduler::yield_now::{closure#0} (...)
|
||||
#2 ostd::task::scheduler::reschedule::{closure#0}<ostd::task::scheduler::yield_now::{closure_env#0}> (...)
|
||||
#3 0xffffffff880b0cff in aster_nix::sched::priority_scheduler::{impl#1}::local_mut_rq_with<ostd::task::Task> (...)
|
||||
#4 0xffffffff8826b205 in ostd::task::scheduler::reschedule<ostd::task::scheduler::yield_now::{closure_env#0}> (...)
|
||||
#5 ostd::task::scheduler::yield_now ()
|
||||
#6 0xffffffff880a92c5 in ostd::task::Task::yield_now ()
|
||||
#7 aster_nix::thread::Thread::yield_now ()
|
||||
#8 aster_nix::ap_init::ap_idle_thread ()
|
||||
#9 core::ops::function::Fn::call<fn(), ()> ()
|
||||
#10 0xffffffff880b341e in alloc::boxed::{impl#50}::call<(), (dyn core::ops::function::Fn<(), Output=()> + core::marker::Send + core::marker::Sync), alloc::alloc::Global> (...)
|
||||
#11 aster_nix::thread::kernel_thread::create_new_kernel_task::{closure#0} ()
|
||||
#12 0xffffffff882a3ea8 in alloc::boxed::{impl#50}::call<(), (dyn core::ops::function::Fn<(), Output=()> + core::marker::Send + core::marker::Sync), alloc::alloc::Global> (...)
|
||||
#13 ostd::task::{impl#2}::build::kernel_task_entry ()
|
||||
#14 0x0000000000000000 in ?? ()
|
||||
|
||||
Thread 1 (Thread 1.1 (CPU#0 [running])):
|
||||
#0 ostd::arch::x86::interrupts_ack (...)
|
||||
#1 0xffffffff8828d704 in ostd::trap::handler::call_irq_callback_functions (...)
|
||||
#2 0xffffffff88268e48 in ostd::arch::x86::trap::trap_handler (...)
|
||||
#3 0xffffffff88274db6 in __from_kernel ()
|
||||
#4 0x0000000000000001 in ?? ()
|
||||
#5 0x0000000000000001 in ?? ()
|
||||
#6 0x00000000000001c4 in ?? ()
|
||||
#7 0xffffffff882c8580 in ?? ()
|
||||
#8 0x0000000000000002 in ?? ()
|
||||
#9 0xffffffff88489808 in _ZN4ostd2mm14heap_allocator10HEAP_SPACE17h85a5340e6564f69dE.llvm.15305379556759765072 ()
|
||||
#10 0x0000000000000000 in ?? ()
|
||||
[Inferior 1 (process 1) detached]
|
||||
"#;
|
||||
|
||||
let mut buffer = ProfileBuffer::new();
|
||||
for line in test_case.lines() {
|
||||
buffer.append_raw_line(line);
|
||||
}
|
||||
|
||||
let profile = &buffer.cur_profile;
|
||||
assert_eq!(profile.stack_traces.len(), 2);
|
||||
assert_eq!(profile.stack_traces[0].len(), 2);
|
||||
assert_eq!(profile.stack_traces[1].len(), 2);
|
||||
|
||||
let stack00 = profile.stack_traces[0].get(&0).unwrap();
|
||||
assert_eq!(stack00.len(), 12);
|
||||
assert_eq!(
|
||||
stack00[0],
|
||||
"aster_nix::sched::priority_scheduler::local_mut_rq_with"
|
||||
);
|
||||
assert_eq!(stack00[11], "??");
|
||||
|
||||
let stack01 = profile.stack_traces[0].get(&1).unwrap();
|
||||
assert_eq!(stack01.len(), 14);
|
||||
assert_eq!(stack01[9], "alloc::boxed::call");
|
||||
|
||||
let stack10 = profile.stack_traces[1].get(&0).unwrap();
|
||||
assert_eq!(stack10.len(), 11);
|
||||
assert_eq!(
|
||||
stack10[9],
|
||||
"_ZN4ostd2mm14heap_allocator10HEAP_SPACE17h85a5340e6564f69dE.llvm.15305379556759765072"
|
||||
);
|
||||
|
||||
let stack11 = profile.stack_traces[1].get(&1).unwrap();
|
||||
assert_eq!(stack11.len(), 15);
|
||||
assert_eq!(
|
||||
stack11[0],
|
||||
"aster_nix::sched::priority_scheduler::pick_next_current"
|
||||
);
|
||||
assert_eq!(stack11[14], "??");
|
||||
}
|
@ -8,7 +8,7 @@ use crate::{
|
||||
};
|
||||
|
||||
pub fn execute_run_command(config: &Config, gdb_server_args: &GdbServerArgs) {
|
||||
if gdb_server_args.is_gdb_enabled {
|
||||
if gdb_server_args.enabled {
|
||||
use std::env;
|
||||
env::set_var(
|
||||
"RUSTFLAGS",
|
||||
@ -21,19 +21,19 @@ pub fn execute_run_command(config: &Config, gdb_server_args: &GdbServerArgs) {
|
||||
let target_name = get_current_crate_info().name;
|
||||
|
||||
let mut config = config.clone();
|
||||
if gdb_server_args.is_gdb_enabled {
|
||||
if gdb_server_args.enabled {
|
||||
let qemu_gdb_args = {
|
||||
let gdb_stub_addr = gdb_server_args.gdb_server_addr.as_str();
|
||||
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 -S",
|
||||
" -chardev socket,path={},server=on,wait=off,id=gdb0 -gdb chardev:gdb0",
|
||||
gdb_stub_addr
|
||||
)
|
||||
}
|
||||
gdb::StubAddrType::Tcp => {
|
||||
format!(
|
||||
" -gdb tcp:{} -S",
|
||||
" -gdb tcp:{}",
|
||||
gdb::tcp_addr_util::format_tcp_addr(gdb_stub_addr)
|
||||
)
|
||||
}
|
||||
@ -41,6 +41,10 @@ pub fn execute_run_command(config: &Config, gdb_server_args: &GdbServerArgs) {
|
||||
};
|
||||
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
|
||||
@ -53,7 +57,7 @@ pub fn execute_run_command(config: &Config, gdb_server_args: &GdbServerArgs) {
|
||||
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.gdb_server_addr)
|
||||
vsc::VscLaunchConfig::new(profile, &gdb_server_args.host_addr)
|
||||
});
|
||||
|
||||
let default_bundle_directory = osdk_output_directory.join(target_name);
|
||||
@ -205,7 +209,7 @@ mod vsc {
|
||||
use crate::{error::Errno, error_msg};
|
||||
use std::process::exit;
|
||||
|
||||
if !args.is_gdb_enabled {
|
||||
if !args.enabled {
|
||||
error_msg!(
|
||||
"No need for a VSCode launch file without launching GDB server,\
|
||||
pass '-h' for help"
|
||||
@ -214,7 +218,7 @@ mod vsc {
|
||||
}
|
||||
|
||||
// check GDB server address
|
||||
let gdb_stub_addr = args.gdb_server_addr.as_str();
|
||||
let gdb_stub_addr = args.host_addr.as_str();
|
||||
if gdb_stub_addr.is_empty() {
|
||||
error_msg!("GDB server address is required to generate a VSCode launch file");
|
||||
exit(Errno::ParseMetadata as _);
|
||||
|
@ -79,7 +79,13 @@ mod qemu_gdb_feature {
|
||||
path.to_string_lossy().to_string()
|
||||
};
|
||||
|
||||
let mut instance = cargo_osdk(["run", "-G", "--gdb-server-addr", unix_socket.as_str()]);
|
||||
let mut instance = cargo_osdk([
|
||||
"run",
|
||||
"--gdb-server",
|
||||
"--gdb-wait-client",
|
||||
"--gdb-server-addr",
|
||||
unix_socket.as_str(),
|
||||
]);
|
||||
instance.current_dir(&workspace.os_dir());
|
||||
|
||||
let sock = unix_socket.clone();
|
||||
@ -123,7 +129,14 @@ mod qemu_gdb_feature {
|
||||
let workspace = workspace::WorkSpace::new(WORKSPACE, kernel_name);
|
||||
let addr = ":50001";
|
||||
|
||||
let mut instance = cargo_osdk(["run", "-G", "--vsc", "--gdb-server-addr", addr]);
|
||||
let mut instance = cargo_osdk([
|
||||
"run",
|
||||
"--gdb-server",
|
||||
"--gdb-wait-client",
|
||||
"--gdb-vsc",
|
||||
"--gdb-server-addr",
|
||||
addr,
|
||||
]);
|
||||
instance.current_dir(&workspace.os_dir());
|
||||
|
||||
let dir = workspace.os_dir();
|
||||
|
Loading…
x
Reference in New Issue
Block a user