debug helper
This commit is contained in:
parent
ff32b48633
commit
a6bf4871f4
14
README.md
14
README.md
@ -21,3 +21,17 @@ To run this example:
|
|||||||
cargo build
|
cargo build
|
||||||
cargo build --example pipes
|
cargo build --example pipes
|
||||||
sudo target/debug/clone-shim -s examples/pipes/spec.json target/debug/examples/pipes
|
sudo target/debug/clone-shim -s examples/pipes/spec.json target/debug/examples/pipes
|
||||||
|
|
||||||
|
## Debugging the shim
|
||||||
|
|
||||||
|
The shim can be debugged as with most processes, but it is exceptionally forky. Breaking before a clone in `rust-gdb` then running `set follow-fork-mode child` is often necessary. The best approach is to go in with a plan of attack.
|
||||||
|
|
||||||
|
## Debugging the child
|
||||||
|
|
||||||
|
Debugging the child processes is vastly more difficult than in other more Linux-like containerisation solutions.
|
||||||
|
|
||||||
|
The `--debug` flag on the shim attempts to stop application spawned processes as soon as they are voided. This gives you a chance to attach with a debugger.
|
||||||
|
|
||||||
|
The debugger must be run from the ambient namespace and not within the void, as none of the prerequisites will exist within the void.
|
||||||
|
|
||||||
|
Good luck!
|
||||||
|
@ -28,6 +28,7 @@ pub fn run() -> Result<()> {
|
|||||||
.arg(clap::Arg::new("spec").long("specification").short('s').help("Provide the specification as an external JSON file.").takes_value(true))
|
.arg(clap::Arg::new("spec").long("specification").short('s').help("Provide the specification as an external JSON file.").takes_value(true))
|
||||||
.setting(AppSettings::TrailingVarArg)
|
.setting(AppSettings::TrailingVarArg)
|
||||||
.arg(clap::Arg::new("verbose").long("verbose").short('v').help("Use verbose logging.").takes_value(false))
|
.arg(clap::Arg::new("verbose").long("verbose").short('v').help("Use verbose logging.").takes_value(false))
|
||||||
|
.arg(clap::Arg::new("debug").long("debug").short('d').help("Stop each spawned application process so that it can be attached to.").takes_value(false))
|
||||||
.arg(clap::Arg::new("binary").index(1).help("Binary and arguments to launch with the shim").required(true).multiple_values(true))
|
.arg(clap::Arg::new("binary").index(1).help("Binary and arguments to launch with the shim").required(true).multiple_values(true))
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
@ -74,15 +75,20 @@ pub fn run() -> Result<()> {
|
|||||||
let sockets = create_sockets(sockets)?;
|
let sockets = create_sockets(sockets)?;
|
||||||
|
|
||||||
// spawn all processes
|
// spawn all processes
|
||||||
|
let debug = matches.is_present("debug");
|
||||||
|
|
||||||
Spawner {
|
Spawner {
|
||||||
spec: &spec,
|
spec: &spec,
|
||||||
binary,
|
binary,
|
||||||
trailing: &trailing,
|
trailing: &trailing,
|
||||||
|
debug,
|
||||||
|
|
||||||
pipes,
|
pipes,
|
||||||
sockets,
|
sockets,
|
||||||
}
|
}
|
||||||
.spawn()
|
.spawn()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_pipes(names: Vec<&str>) -> Result<HashMap<String, PipePair>> {
|
fn create_pipes(names: Vec<&str>) -> Result<HashMap<String, PipePair>> {
|
||||||
|
@ -16,8 +16,9 @@ use std::io::Read;
|
|||||||
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
|
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use nix::sys::signal::{kill, Signal};
|
||||||
use nix::sys::socket::{recvmsg, ControlMessageOwned, MsgFlags};
|
use nix::sys::socket::{recvmsg, ControlMessageOwned, MsgFlags};
|
||||||
use nix::unistd;
|
use nix::unistd::{self, Pid};
|
||||||
|
|
||||||
const BUFFER_SIZE: usize = 1024;
|
const BUFFER_SIZE: usize = 1024;
|
||||||
|
|
||||||
@ -25,6 +26,7 @@ pub struct Spawner<'a> {
|
|||||||
pub spec: &'a Specification,
|
pub spec: &'a Specification,
|
||||||
pub binary: &'a str,
|
pub binary: &'a str,
|
||||||
pub trailing: &'a Vec<&'a str>,
|
pub trailing: &'a Vec<&'a str>,
|
||||||
|
pub debug: bool,
|
||||||
|
|
||||||
pub pipes: HashMap<String, PipePair>,
|
pub pipes: HashMap<String, PipePair>,
|
||||||
pub sockets: HashMap<String, SocketPair>,
|
pub sockets: HashMap<String, SocketPair>,
|
||||||
@ -72,6 +74,10 @@ impl<'a> Spawner<'a> {
|
|||||||
PreparedArgs::prepare_ambient_mut(self, &mut builder, &entrypoint.args)?;
|
PreparedArgs::prepare_ambient_mut(self, &mut builder, &entrypoint.args)?;
|
||||||
|
|
||||||
let closure = || {
|
let closure = || {
|
||||||
|
if self.debug {
|
||||||
|
Self::stop_self(name).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
let args = args
|
let args = args
|
||||||
.prepare_void(self, name, &mut TriggerData::None)
|
.prepare_void(self, name, &mut TriggerData::None)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -89,7 +95,8 @@ impl<'a> Spawner<'a> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
builder.spawn(closure)?;
|
let void = builder.spawn(closure)?;
|
||||||
|
info!("spawned entrypoint `{}` as {}", name.as_str(), void);
|
||||||
}
|
}
|
||||||
|
|
||||||
Trigger::Pipe(s) => {
|
Trigger::Pipe(s) => {
|
||||||
@ -110,7 +117,12 @@ impl<'a> Spawner<'a> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
builder.spawn(closure)?;
|
let void = builder.spawn(closure)?;
|
||||||
|
info!(
|
||||||
|
"prepared pipe trigger for entrypoint `{}` as {}",
|
||||||
|
name.as_str(),
|
||||||
|
void
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Trigger::FileSocket(s) => {
|
Trigger::FileSocket(s) => {
|
||||||
@ -131,7 +143,12 @@ impl<'a> Spawner<'a> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
builder.spawn(closure)?;
|
let void = builder.spawn(closure)?;
|
||||||
|
info!(
|
||||||
|
"prepared socket trigger for entrypoint `{}` as {}",
|
||||||
|
name.as_str(),
|
||||||
|
void
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -167,6 +184,10 @@ impl<'a> Spawner<'a> {
|
|||||||
|
|
||||||
let closure =
|
let closure =
|
||||||
|| {
|
|| {
|
||||||
|
if self.debug {
|
||||||
|
Self::stop_self(name).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
let pipe_trigger = std::str::from_utf8(&buf[0..read_bytes]).unwrap();
|
let pipe_trigger = std::str::from_utf8(&buf[0..read_bytes]).unwrap();
|
||||||
|
|
||||||
let args = args
|
let args = args
|
||||||
@ -230,6 +251,10 @@ impl<'a> Spawner<'a> {
|
|||||||
let args = PreparedArgs::prepare_ambient(&mut builder, &spec.args)?;
|
let args = PreparedArgs::prepare_ambient(&mut builder, &spec.args)?;
|
||||||
|
|
||||||
let closure = || {
|
let closure = || {
|
||||||
|
if self.debug {
|
||||||
|
Self::stop_self(name).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
let args = args
|
let args = args
|
||||||
.prepare_void(self, name, &mut TriggerData::FileSocket(fds))
|
.prepare_void(self, name, &mut TriggerData::FileSocket(fds))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -257,6 +282,19 @@ impl<'a> Spawner<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn stop_self(name: &str) -> Result<()> {
|
||||||
|
let pid = Pid::this();
|
||||||
|
info!("stopping process `{}`", name);
|
||||||
|
|
||||||
|
kill(pid, Signal::SIGSTOP).map_err(|e| Error::Nix {
|
||||||
|
msg: "kill",
|
||||||
|
src: e,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
info!("process `{}` resumed", name);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn prepare_env<'b>(
|
fn prepare_env<'b>(
|
||||||
&self,
|
&self,
|
||||||
builder: &mut VoidBuilder,
|
builder: &mut VoidBuilder,
|
||||||
|
14
src/void.rs
14
src/void.rs
@ -4,9 +4,9 @@ use crate::clone::{clone3, CloneArgs, CloneFlags};
|
|||||||
use crate::{Error, Result};
|
use crate::{Error, Result};
|
||||||
|
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::fs;
|
|
||||||
use std::os::unix::io::{AsRawFd, RawFd};
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::{fmt, fs};
|
||||||
|
|
||||||
use nix::fcntl::{FcntlArg, FdFlag};
|
use nix::fcntl::{FcntlArg, FdFlag};
|
||||||
use nix::mount::{mount, umount2, MntFlags, MsFlags};
|
use nix::mount::{mount, umount2, MntFlags, MsFlags};
|
||||||
@ -16,7 +16,15 @@ use nix::unistd::{pivot_root, Pid};
|
|||||||
|
|
||||||
use close_fds::CloseFdsBuilder;
|
use close_fds::CloseFdsBuilder;
|
||||||
|
|
||||||
pub struct VoidHandle {}
|
pub struct VoidHandle {
|
||||||
|
pid: Pid,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for VoidHandle {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "Void{{Pid:{}}}", self.pid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct VoidBuilder {
|
pub struct VoidBuilder {
|
||||||
mounts: HashMap<PathBuf, PathBuf>,
|
mounts: HashMap<PathBuf, PathBuf>,
|
||||||
@ -90,7 +98,7 @@ impl VoidBuilder {
|
|||||||
// be a problem.
|
// be a problem.
|
||||||
std::mem::forget(child_fn);
|
std::mem::forget(child_fn);
|
||||||
|
|
||||||
Ok(VoidHandle {})
|
Ok(VoidHandle { pid: child })
|
||||||
}
|
}
|
||||||
|
|
||||||
// per-namespace void creation
|
// per-namespace void creation
|
||||||
|
Loading…
Reference in New Issue
Block a user