Merge branch 'main' into feature/breakpoint-support

reviewable/pr1365/r6
Friday Ortiz 3 weeks ago committed by GitHub
commit 38e3f7fce2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -1,6 +1,7 @@
{
"rust-analyzer.check.allTargets": true,
"rust-analyzer.check.command": "clippy",
"rust-analyzer.cargo.target": "x86_64-unknown-linux-musl",
"search.exclude": {
"/xtask/public-api/*.txt": true
},

@ -97,6 +97,10 @@ impl LowerMacFormatter for [u8; 6] {}
pub trait UpperMacFormatter {}
impl UpperMacFormatter for [u8; 6] {}
pub trait PointerFormatter {}
impl<T> PointerFormatter for *const T {}
impl<T> PointerFormatter for *mut T {}
#[repr(u8)]
#[derive(Copy, Clone, Debug, IntoPrimitive)]
pub enum RecordFieldKind {
@ -144,6 +148,8 @@ pub enum ArgumentKind {
Bytes,
Str,
Pointer,
}
/// All display hints
@ -162,6 +168,8 @@ pub enum DisplayHint {
LowerMac,
/// `:MAC`
UpperMac,
/// `:p`
Pointer,
}
mod sealed {
@ -282,6 +290,20 @@ impl Argument for DisplayHint {
}
}
impl<T> sealed::Sealed for *const T {}
impl<T> Argument for *const T {
fn as_argument(&self) -> (ArgumentKind, impl AsRef<[u8]>) {
(ArgumentKind::Pointer, (*self as usize).to_ne_bytes())
}
}
impl<T> sealed::Sealed for *mut T {}
impl<T> Argument for *mut T {
fn as_argument(&self) -> (ArgumentKind, impl AsRef<[u8]>) {
(ArgumentKind::Pointer, (*self as usize).to_ne_bytes())
}
}
fn wire_len(value: &[u8]) -> Option<[u8; 2]> {
match LogValueLength::try_from(value.len()) {
Ok(wire_len) => Some(wire_len.to_ne_bytes()),

@ -127,6 +127,9 @@ pub(crate) fn log(args: LogArgs, level_expr: Option<TokenStream>) -> Result<Toke
DisplayHint::UpperMac => {
(quote!(DisplayHint::UpperMac), quote!(UpperMacFormatter))
}
DisplayHint::Pointer => {
(quote!(DisplayHint::Pointer), quote!(PointerFormatter))
}
};
let hint = quote!(::aya_log_ebpf::macro_support::#hint);
let arg = quote!(

@ -59,11 +59,12 @@ fn parse_param(input: &str) -> Result<Parameter, String> {
let hint = match input.strip_prefix(":") {
Some(input) => match input {
"" => return Err("malformed format string (missing display hint after ':')".into()),
"p" | "x" => DisplayHint::LowerHex,
"x" => DisplayHint::LowerHex,
"X" => DisplayHint::UpperHex,
"i" => DisplayHint::Ip,
"mac" => DisplayHint::LowerMac,
"MAC" => DisplayHint::UpperMac,
"p" => DisplayHint::Pointer,
input => return Err(format!("unknown display hint: {input:?}")),
},
None => {
@ -155,7 +156,7 @@ mod test {
}),
Fragment::Literal(" lmao {} {something} ".into()),
Fragment::Parameter(Parameter {
hint: DisplayHint::LowerHex
hint: DisplayHint::Pointer
}),
])
);

@ -284,6 +284,19 @@ impl Formatter<[u8; 6]> for UpperMacFormatter {
}
}
pub struct PointerFormatter;
impl<T> Formatter<*const T> for PointerFormatter {
fn format(v: *const T) -> String {
format!("{v:p}")
}
}
impl<T> Formatter<*mut T> for PointerFormatter {
fn format(v: *mut T) -> String {
format!("{v:p}")
}
}
trait Format {
fn format(&self, last_hint: Option<DisplayHintWrapper>) -> Result<String, ()>;
}
@ -307,6 +320,7 @@ impl Format for u32 {
Some(DisplayHint::Ip) => Ok(Ipv4Formatter::format(*self)),
Some(DisplayHint::LowerMac) => Err(()),
Some(DisplayHint::UpperMac) => Err(()),
Some(DisplayHint::Pointer) => Err(()),
None => Ok(DefaultFormatter::format(self)),
}
}
@ -321,6 +335,7 @@ impl Format for Ipv4Addr {
Some(DisplayHint::Ip) => Ok(Ipv4Formatter::format(*self)),
Some(DisplayHint::LowerMac) => Err(()),
Some(DisplayHint::UpperMac) => Err(()),
Some(DisplayHint::Pointer) => Err(()),
None => Ok(Ipv4Formatter::format(*self)),
}
}
@ -335,6 +350,7 @@ impl Format for Ipv6Addr {
Some(DisplayHint::Ip) => Ok(Ipv6Formatter::format(*self)),
Some(DisplayHint::LowerMac) => Err(()),
Some(DisplayHint::UpperMac) => Err(()),
Some(DisplayHint::Pointer) => Err(()),
None => Ok(Ipv6Formatter::format(*self)),
}
}
@ -349,6 +365,7 @@ impl Format for [u8; 4] {
Some(DisplayHint::Ip) => Ok(Ipv4Formatter::format(*self)),
Some(DisplayHint::LowerMac) => Err(()),
Some(DisplayHint::UpperMac) => Err(()),
Some(DisplayHint::Pointer) => Err(()),
None => Ok(Ipv4Formatter::format(*self)),
}
}
@ -363,6 +380,7 @@ impl Format for [u8; 6] {
Some(DisplayHint::Ip) => Err(()),
Some(DisplayHint::LowerMac) => Ok(LowerMacFormatter::format(*self)),
Some(DisplayHint::UpperMac) => Ok(UpperMacFormatter::format(*self)),
Some(DisplayHint::Pointer) => Err(()),
None => Err(()),
}
}
@ -377,6 +395,7 @@ impl Format for [u8; 16] {
Some(DisplayHint::Ip) => Ok(Ipv6Formatter::format(*self)),
Some(DisplayHint::LowerMac) => Err(()),
Some(DisplayHint::UpperMac) => Err(()),
Some(DisplayHint::Pointer) => Err(()),
None => Err(()),
}
}
@ -391,6 +410,7 @@ impl Format for [u16; 8] {
Some(DisplayHint::Ip) => Ok(Ipv6Formatter::format(*self)),
Some(DisplayHint::LowerMac) => Err(()),
Some(DisplayHint::UpperMac) => Err(()),
Some(DisplayHint::Pointer) => Err(()),
None => Err(()),
}
}
@ -407,6 +427,7 @@ macro_rules! impl_format {
Some(DisplayHint::Ip) => Err(()),
Some(DisplayHint::LowerMac) => Err(()),
Some(DisplayHint::UpperMac) => Err(()),
Some(DisplayHint::Pointer) => Err(()),
None => Ok(DefaultFormatter::format(self)),
}
}
@ -436,6 +457,7 @@ macro_rules! impl_format_float {
Some(DisplayHint::Ip) => Err(()),
Some(DisplayHint::LowerMac) => Err(()),
Some(DisplayHint::UpperMac) => Err(()),
Some(DisplayHint::Pointer) => Err(()),
None => Ok(DefaultFormatter::format(self)),
}
}
@ -446,6 +468,24 @@ macro_rules! impl_format_float {
impl_format_float!(f32);
impl_format_float!(f64);
impl<T> Format for *const T {
fn format(&self, last_hint: Option<DisplayHintWrapper>) -> Result<String, ()> {
match last_hint.map(|DisplayHintWrapper(dh)| dh) {
Some(DisplayHint::Pointer) => Ok(PointerFormatter::format(*self)),
_ => Err(()),
}
}
}
impl<T> Format for *mut T {
fn format(&self, last_hint: Option<DisplayHintWrapper>) -> Result<String, ()> {
match last_hint.map(|DisplayHintWrapper(dh)| dh) {
Some(DisplayHint::Pointer) => Ok(PointerFormatter::format(*self)),
_ => Err(()),
}
}
}
#[derive(Error, Debug)]
pub enum Error {
#[error("{} not found", MAP_NAME)]
@ -732,6 +772,13 @@ fn log_buf<T: ?Sized + Log>(mut buf: &[u8], logger: &T) -> Result<(), ()> {
}
Err(e) => error!("received invalid utf8 string: {e}"),
},
ArgumentKind::Pointer => {
let value = value
.try_into()
.map_err(|std::array::TryFromSliceError { .. }| ())?;
let ptr = usize::from_ne_bytes(value) as *const ();
full_log_msg.push_str(&ptr.format(last_hint.take())?);
}
}
buf = rest;

@ -99,21 +99,21 @@ pub enum BtfError {
},
/// unknown BTF type id
#[error("Unknown BTF type id `{type_id}`")]
#[error("unknown BTF type id `{type_id}`")]
UnknownBtfType {
/// type id
type_id: u32,
},
/// unexpected btf type id
#[error("Unexpected BTF type id `{type_id}`")]
#[error("unexpected BTF type id `{type_id}`")]
UnexpectedBtfType {
/// type id
type_id: u32,
},
/// unknown BTF type
#[error("Unknown BTF type `{type_name}`")]
#[error("unknown BTF type `{type_name}`")]
UnknownBtfTypeName {
/// type name
type_name: String,
@ -128,7 +128,7 @@ pub enum BtfError {
#[cfg(feature = "std")]
/// Loading the btf failed
#[error("the BPF_BTF_LOAD syscall failed. Verifier output: {verifier_log}")]
#[error("the BPF_BTF_LOAD syscall returned {io_error}. Verifier output: {verifier_log}")]
LoadError {
/// The [`std::io::Error`] returned by the `BPF_BTF_LOAD` syscall.
#[source]

@ -9,7 +9,7 @@ use aya_obj::generated::{
use crate::{
programs::{
CgroupAttachMode, FdLink, Link, ProgAttachLink, ProgramData, ProgramError, ProgramType,
define_link_wrapper, id_as_key, load_program,
define_link_wrapper, id_as_key, impl_try_into_fdlink, load_program,
},
sys::{LinkTarget, SyscallError, bpf_link_create},
util::KernelVersion,
@ -155,3 +155,5 @@ define_link_wrapper!(
FlowDissectorLinkIdInner,
FlowDissector,
);
impl_try_into_fdlink!(FlowDissectorLink, FlowDissectorLinkInner);

@ -158,7 +158,7 @@ pub enum ProgramError {
NotAttached,
/// Loading the program failed.
#[error("the BPF_PROG_LOAD syscall failed. Verifier output: {verifier_log}")]
#[error("the BPF_PROG_LOAD syscall returned {io_error}. Verifier output: {verifier_log}")]
LoadError {
/// The [`io::Error`] returned by the `BPF_PROG_LOAD` syscall.
#[source]

@ -40,8 +40,6 @@ pub(crate) enum NetlinkErrorInternal {
#[error(transparent)]
IoError(#[from] io::Error),
#[error(transparent)]
NulError(#[from] std::ffi::NulError),
#[error(transparent)]
NlAttrError(#[from] NlAttrError),
}
@ -699,12 +697,6 @@ pub(crate) enum NlAttrError {
InvalidHeaderLength(usize),
}
impl From<NlAttrError> for io::Error {
fn from(err: NlAttrError) -> Self {
Self::other(err)
}
}
unsafe fn request_attributes<T>(req: &mut T, msg_len: usize) -> &mut [u8] {
let req: *mut _ = req;
let req: *mut u8 = req.cast();

@ -2,15 +2,6 @@
set -eux
cargo +nightly hack clippy \
--target bpfel-unknown-none -Zbuild-std=core \
--package aya-ebpf-bindings \
--package aya-ebpf \
--package aya-log-ebpf \
--package integration-ebpf \
--feature-powerset \
-- --deny warnings
# `-C panic=abort` because "unwinding panics are not supported without std"; integration-ebpf
# contains `#[no_std]` binaries.
#
@ -26,3 +17,12 @@ cargo +nightly hack clippy "$@" \
-- --deny warnings \
-C panic=abort \
-Zpanic_abort_tests
cargo +nightly hack clippy \
--target bpfel-unknown-none -Zbuild-std=core \
--package aya-ebpf-bindings \
--package aya-ebpf \
--package aya-log-ebpf \
--package integration-ebpf \
--feature-powerset \
-- --deny warnings

@ -14,7 +14,8 @@ pub mod macro_support {
use aya_ebpf::maps::RingBuf;
pub use aya_log_common::{
Argument, DefaultFormatter, DisplayHint, Field, Header, IpFormatter, Level, LogValueLength,
LowerHexFormatter, LowerMacFormatter, UpperHexFormatter, UpperMacFormatter,
LowerHexFormatter, LowerMacFormatter, PointerFormatter, UpperHexFormatter,
UpperMacFormatter,
};
// This cfg_attr prevents compilation failures on macOS where the generated section name doesn't

@ -89,6 +89,8 @@ fn test_log(ctx: ProbeContext) {
&ctx,
"{} {} {} {} {} {} {}", header, tmp, kind, value, size, op, buf
);
let ptr = 0xdeadbeef as *const u8;
debug!(&ctx, "ptr: {:p}", ptr);
// Testing compilation only.
if false {

@ -3,10 +3,16 @@ use std::{convert::TryInto as _, fs::remove_file, path::Path, thread, time::Dura
use aya::{
Ebpf,
maps::Array,
pin::PinError,
programs::{
FlowDissector, KProbe, TracePoint, UProbe, Xdp, XdpFlags,
links::{FdLink, PinnedLink},
FlowDissector, KProbe, ProbeKind, Program, ProgramError, TracePoint, UProbe, Xdp, XdpFlags,
flow_dissector::{FlowDissectorLink, FlowDissectorLinkId},
kprobe::{KProbeLink, KProbeLinkId},
links::{FdLink, LinkError, PinnedLink},
loaded_links, loaded_programs,
trace_point::{TracePointLink, TracePointLinkId},
uprobe::{UProbeLink, UProbeLinkId},
xdp::{XdpLink, XdpLinkId},
},
util::KernelVersion,
};
@ -31,6 +37,13 @@ fn long_name() {
// Therefore, as long as we were able to load the program, this is good enough.
}
#[test_log::test]
fn memmove() {
let mut bpf = Ebpf::load(crate::MEMMOVE_TEST).unwrap();
let prog: &mut Xdp = bpf.program_mut("do_dnat").unwrap().try_into().unwrap();
prog.load().unwrap();
}
#[test_log::test]
fn multiple_btf_maps() {
let mut bpf = Ebpf::load(crate::MULTIMAP_BTF).unwrap();
@ -190,156 +203,128 @@ fn assert_unloaded(name: &str) {
)
}
#[test_log::test]
fn unload_xdp() {
let mut bpf = Ebpf::load(crate::TEST).unwrap();
let prog: &mut Xdp = bpf.program_mut("pass").unwrap().try_into().unwrap();
prog.load().unwrap();
assert_loaded("pass");
let link = prog.attach("lo", XdpFlags::default()).unwrap();
{
let _link_owned = prog.take_link(link).unwrap();
prog.unload().unwrap();
assert_loaded_and_linked("pass");
};
trait UnloadProgramOps {
type LinkId;
type OwnedLink;
assert_unloaded("pass");
prog.load().unwrap();
fn load(&mut self) -> Result<(), ProgramError>;
fn unload(&mut self) -> Result<(), ProgramError>;
fn take_link(&mut self, id: Self::LinkId) -> Result<Self::OwnedLink, ProgramError>;
}
assert_loaded("pass");
prog.attach("lo", XdpFlags::default()).unwrap();
macro_rules! impl_unload_program_ops {
($program:ty, $link_id:ty, $link:ty) => {
impl UnloadProgramOps for $program {
type LinkId = $link_id;
type OwnedLink = $link;
assert_loaded("pass");
prog.unload().unwrap();
fn load(&mut self) -> Result<(), ProgramError> {
<$program>::load(self)
}
assert_unloaded("pass");
fn unload(&mut self) -> Result<(), ProgramError> {
<$program>::unload(self)
}
fn take_link(&mut self, id: Self::LinkId) -> Result<Self::OwnedLink, ProgramError> {
<$program>::take_link(self, id)
}
}
};
}
impl_unload_program_ops!(Xdp, XdpLinkId, XdpLink);
impl_unload_program_ops!(KProbe, KProbeLinkId, KProbeLink);
impl_unload_program_ops!(TracePoint, TracePointLinkId, TracePointLink);
impl_unload_program_ops!(UProbe, UProbeLinkId, UProbeLink);
impl_unload_program_ops!(FlowDissector, FlowDissectorLinkId, FlowDissectorLink);
#[test_log::test]
fn unload_kprobe() {
let mut bpf = Ebpf::load(crate::TEST).unwrap();
let prog: &mut KProbe = bpf.program_mut("test_kprobe").unwrap().try_into().unwrap();
fn unload_xdp() {
type P = Xdp;
let program_name = "pass";
let attach = |prog: &mut P| prog.attach("lo", XdpFlags::default()).unwrap();
run_unload_program_test(crate::TEST, program_name, attach);
}
fn run_unload_program_test<P>(bpf_image: &[u8], program_name: &str, attach: fn(&mut P) -> P::LinkId)
where
P: UnloadProgramOps,
for<'a> &'a mut Program: TryInto<&'a mut P, Error = ProgramError>,
{
let mut bpf = Ebpf::load(bpf_image).unwrap();
let prog: &mut P = bpf.program_mut(program_name).unwrap().try_into().unwrap();
prog.load().unwrap();
assert_loaded("test_kprobe");
let link = prog.attach("try_to_wake_up", 0).unwrap();
assert_loaded(program_name);
let link = attach(prog);
{
let _link_owned = prog.take_link(link).unwrap();
let _link_owned: P::OwnedLink = prog.take_link(link).unwrap();
prog.unload().unwrap();
assert_loaded_and_linked("test_kprobe");
};
assert_loaded_and_linked(program_name);
}
assert_unloaded("test_kprobe");
assert_unloaded(program_name);
prog.load().unwrap();
assert_loaded("test_kprobe");
prog.attach("try_to_wake_up", 0).unwrap();
assert_loaded(program_name);
attach(prog);
assert_loaded("test_kprobe");
assert_loaded(program_name);
prog.unload().unwrap();
assert_unloaded("test_kprobe");
assert_unloaded(program_name);
}
#[test_log::test]
fn memmove() {
let mut bpf = Ebpf::load(crate::MEMMOVE_TEST).unwrap();
let prog: &mut Xdp = bpf.program_mut("do_dnat").unwrap().try_into().unwrap();
fn unload_kprobe() {
type P = KProbe;
prog.load().unwrap();
assert_loaded("do_dnat");
let program_name = "test_kprobe";
let attach = |prog: &mut P| prog.attach("try_to_wake_up", 0).unwrap();
run_unload_program_test(crate::TEST, program_name, attach);
}
#[test_log::test]
fn basic_tracepoint() {
let mut bpf = Ebpf::load(crate::TEST).unwrap();
let prog: &mut TracePoint = bpf
.program_mut("test_tracepoint")
.unwrap()
.try_into()
.unwrap();
prog.load().unwrap();
assert_loaded("test_tracepoint");
let link = prog.attach("syscalls", "sys_enter_kill").unwrap();
{
let _link_owned = prog.take_link(link).unwrap();
prog.unload().unwrap();
assert_loaded_and_linked("test_tracepoint");
};
type P = TracePoint;
assert_unloaded("test_tracepoint");
prog.load().unwrap();
assert_loaded("test_tracepoint");
prog.attach("syscalls", "sys_enter_kill").unwrap();
assert_loaded("test_tracepoint");
prog.unload().unwrap();
assert_unloaded("test_tracepoint");
let program_name = "test_tracepoint";
let attach = |prog: &mut P| prog.attach("syscalls", "sys_enter_kill").unwrap();
run_unload_program_test(crate::TEST, program_name, attach);
}
#[test_log::test]
fn basic_uprobe() {
let mut bpf = Ebpf::load(crate::TEST).unwrap();
let prog: &mut UProbe = bpf.program_mut("test_uprobe").unwrap().try_into().unwrap();
prog.load().unwrap();
assert_loaded("test_uprobe");
let link = prog
.attach("uprobe_function", "/proc/self/exe", None, None)
.unwrap();
type P = UProbe;
{
let _link_owned = prog.take_link(link).unwrap();
prog.unload().unwrap();
assert_loaded_and_linked("test_uprobe");
let program_name = "test_uprobe";
let attach = |prog: &mut P| {
prog.attach("uprobe_function", "/proc/self/exe", None, None)
.unwrap()
};
assert_unloaded("test_uprobe");
prog.load().unwrap();
assert_loaded("test_uprobe");
prog.attach("uprobe_function", "/proc/self/exe", None, None)
.unwrap();
assert_loaded("test_uprobe");
prog.unload().unwrap();
assert_unloaded("test_uprobe");
run_unload_program_test(crate::TEST, program_name, attach);
}
#[test_log::test]
fn basic_flow_dissector() {
let mut bpf = Ebpf::load(crate::TEST).unwrap();
let prog: &mut FlowDissector = bpf.program_mut("test_flow").unwrap().try_into().unwrap();
prog.load().unwrap();
assert_loaded("test_flow");
type P = FlowDissector;
let net_ns = std::fs::File::open("/proc/self/ns/net").unwrap();
let link = prog.attach(net_ns.try_clone().unwrap()).unwrap();
{
let _link_owned = prog.take_link(link).unwrap();
prog.unload().unwrap();
assert_loaded_and_linked("test_flow");
let program_name = "test_flow";
let attach = |prog: &mut P| {
let net_ns = std::fs::File::open("/proc/self/ns/net").unwrap();
prog.attach(net_ns).unwrap()
};
assert_unloaded("test_flow");
prog.load().unwrap();
assert_loaded("test_flow");
prog.attach(net_ns).unwrap();
assert_loaded("test_flow");
prog.unload().unwrap();
assert_unloaded("test_flow");
run_unload_program_test(crate::TEST, program_name, attach);
}
#[test_log::test]
fn pin_link() {
type P = Xdp;
let program_name = "pass";
let attach = |prog: &mut P| prog.attach("lo", XdpFlags::default()).unwrap();
let kernel_version = KernelVersion::current().unwrap();
if kernel_version < KernelVersion::new(5, 9, 0) {
eprintln!("skipping test on kernel {kernel_version:?}, XDP uses netlink");
@ -347,30 +332,62 @@ fn pin_link() {
}
let mut bpf = Ebpf::load(crate::TEST).unwrap();
let prog: &mut Xdp = bpf.program_mut("pass").unwrap().try_into().unwrap();
let prog: &mut P = bpf.program_mut(program_name).unwrap().try_into().unwrap();
prog.load().unwrap();
let link_id = prog.attach("lo", XdpFlags::default()).unwrap();
let link_id = attach(prog);
let link = prog.take_link(link_id).unwrap();
assert_loaded("pass");
assert_loaded(program_name);
let fd_link: FdLink = link.try_into().unwrap();
let pinned = fd_link.pin("/sys/fs/bpf/aya-xdp-test-lo").unwrap();
// because of the pin, the program is still attached
prog.unload().unwrap();
assert_loaded("pass");
assert_loaded(program_name);
// delete the pin, but the program is still attached
let new_link = pinned.unpin().unwrap();
assert_loaded("pass");
assert_loaded(program_name);
// finally when new_link is dropped we're detached
drop(new_link);
assert_unloaded("pass");
assert_unloaded(program_name);
}
trait PinProgramOps {
fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<(), PinError>;
fn unpin(self) -> Result<(), std::io::Error>;
}
macro_rules! impl_pin_program_ops {
($program:ty) => {
impl PinProgramOps for $program {
fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<(), PinError> {
<$program>::pin(self, path)
}
fn unpin(self) -> Result<(), std::io::Error> {
<$program>::unpin(self)
}
}
};
}
impl_pin_program_ops!(Xdp);
impl_pin_program_ops!(KProbe);
impl_pin_program_ops!(TracePoint);
impl_pin_program_ops!(UProbe);
#[test_log::test]
fn pin_lifecycle() {
type P = Xdp;
let program_name = "pass";
let attach = |prog: &mut P| prog.attach("lo", XdpFlags::default()).unwrap();
let program_pin = "/sys/fs/bpf/aya-xdp-test-prog";
let link_pin = "/sys/fs/bpf/aya-xdp-test-lo";
let from_pin = |program_pin: &str| P::from_pin(program_pin, XdpAttachType::Interface).unwrap();
let kernel_version = KernelVersion::current().unwrap();
if kernel_version < KernelVersion::new(5, 18, 0) {
eprintln!(
@ -378,170 +395,120 @@ fn pin_lifecycle() {
);
return;
}
run_pin_program_lifecycle_test(
crate::PASS,
program_name,
program_pin,
link_pin,
from_pin,
attach,
Some(|prog: &mut P, pinned: FdLink| {
prog.attach_to_link(pinned.try_into().unwrap()).unwrap()
}),
);
}
fn run_pin_program_lifecycle_test<P>(
bpf_image: &[u8],
program_name: &str,
program_pin: &str,
link_pin: &str,
from_pin: fn(&str) -> P,
attach: fn(&mut P) -> P::LinkId,
attach_to_link: Option<fn(&mut P, FdLink) -> P::LinkId>,
) where
P: UnloadProgramOps + PinProgramOps,
P::OwnedLink: TryInto<FdLink, Error = LinkError>,
for<'a> &'a mut Program: TryInto<&'a mut P, Error = ProgramError>,
{
// 1. Load Program and Pin
{
let mut bpf = Ebpf::load(crate::PASS).unwrap();
let prog: &mut Xdp = bpf.program_mut("pass").unwrap().try_into().unwrap();
let mut bpf = Ebpf::load(bpf_image).unwrap();
let prog: &mut P = bpf.program_mut(program_name).unwrap().try_into().unwrap();
prog.load().unwrap();
prog.pin("/sys/fs/bpf/aya-xdp-test-prog").unwrap();
prog.pin(program_pin).unwrap();
}
// should still be loaded since prog was pinned
assert_loaded("pass");
assert_loaded(program_name);
// 2. Load program from bpffs but don't attach it
{
let _ = Xdp::from_pin("/sys/fs/bpf/aya-xdp-test-prog", XdpAttachType::Interface).unwrap();
let _: P = from_pin(program_pin);
}
// should still be loaded since prog was pinned
assert_loaded("pass");
assert_loaded(program_name);
// 3. Load program from bpffs and attach
{
let mut prog =
Xdp::from_pin("/sys/fs/bpf/aya-xdp-test-prog", XdpAttachType::Interface).unwrap();
let link_id = prog.attach("lo", XdpFlags::default()).unwrap();
let mut prog = from_pin(program_pin);
let link_id = attach(&mut prog);
let link = prog.take_link(link_id).unwrap();
let fd_link: FdLink = link.try_into().unwrap();
fd_link.pin("/sys/fs/bpf/aya-xdp-test-lo").unwrap();
fd_link.pin(link_pin).unwrap();
// Unpin the program. It will stay attached since its links were pinned.
prog.unpin().unwrap();
}
// should still be loaded since link was pinned
assert_loaded_and_linked("pass");
assert_loaded_and_linked(program_name);
// 4. Load a new version of the program, unpin link, and atomically replace old program
{
let mut bpf = Ebpf::load(crate::PASS).unwrap();
let prog: &mut Xdp = bpf.program_mut("pass").unwrap().try_into().unwrap();
prog.load().unwrap();
let link = PinnedLink::from_pin("/sys/fs/bpf/aya-xdp-test-lo")
.unwrap()
.unpin()
.unwrap();
prog.attach_to_link(link.try_into().unwrap()).unwrap();
assert_loaded("pass");
let link = PinnedLink::from_pin(link_pin).unwrap().unpin().unwrap();
if let Some(attach_to_link) = attach_to_link {
let mut bpf = Ebpf::load(bpf_image).unwrap();
let prog: &mut P = bpf.program_mut(program_name).unwrap().try_into().unwrap();
prog.load().unwrap();
attach_to_link(prog, link);
assert_loaded(program_name);
}
}
// program should be unloaded
assert_unloaded("pass");
assert_unloaded(program_name);
}
#[test_log::test]
fn pin_lifecycle_tracepoint() {
// 1. Load Program and Pin
{
let mut bpf = Ebpf::load(crate::TEST).unwrap();
let prog: &mut TracePoint = bpf
.program_mut("test_tracepoint")
.unwrap()
.try_into()
.unwrap();
prog.load().unwrap();
prog.pin("/sys/fs/bpf/aya-tracepoint-test-prog").unwrap();
}
// should still be loaded since prog was pinned
assert_loaded("test_tracepoint");
// 2. Load program from bpffs but don't attach it
{
let _ = TracePoint::from_pin("/sys/fs/bpf/aya-tracepoint-test-prog").unwrap();
}
// should still be loaded since prog was pinned
assert_loaded("test_tracepoint");
// 3. Load program from bpffs and attach
{
let mut prog = TracePoint::from_pin("/sys/fs/bpf/aya-tracepoint-test-prog").unwrap();
let link_id = prog.attach("syscalls", "sys_enter_kill").unwrap();
let link = prog.take_link(link_id).unwrap();
let fd_link: FdLink = link.try_into().unwrap();
fd_link
.pin("/sys/fs/bpf/aya-tracepoint-test-sys-enter-kill")
.unwrap();
// Unpin the program. It will stay attached since its links were pinned.
prog.unpin().unwrap();
}
// should still be loaded since link was pinned
assert_loaded_and_linked("test_tracepoint");
// 4. unpin link, and make sure everything is unloaded
{
PinnedLink::from_pin("/sys/fs/bpf/aya-tracepoint-test-sys-enter-kill")
.unwrap()
.unpin()
.unwrap();
}
// program should be unloaded
assert_unloaded("test_tracepoint");
type P = TracePoint;
let program_name = "test_tracepoint";
let attach = |prog: &mut P| prog.attach("syscalls", "sys_enter_kill").unwrap();
let program_pin = "/sys/fs/bpf/aya-tracepoint-test-prog";
let link_pin = "/sys/fs/bpf/aya-tracepoint-test-sys-enter-kill";
let from_pin = |program_pin: &str| P::from_pin(program_pin).unwrap();
run_pin_program_lifecycle_test(
crate::TEST,
program_name,
program_pin,
link_pin,
from_pin,
attach,
None,
);
}
#[test_log::test]
fn pin_lifecycle_kprobe() {
// 1. Load Program and Pin
{
let mut bpf = Ebpf::load(crate::TEST).unwrap();
let prog: &mut KProbe = bpf.program_mut("test_kprobe").unwrap().try_into().unwrap();
prog.load().unwrap();
prog.pin("/sys/fs/bpf/aya-kprobe-test-prog").unwrap();
}
// should still be loaded since prog was pinned
assert_loaded("test_kprobe");
// 2. Load program from bpffs but don't attach it
{
let _ = KProbe::from_pin(
"/sys/fs/bpf/aya-kprobe-test-prog",
aya::programs::ProbeKind::KProbe,
)
.unwrap();
}
// should still be loaded since prog was pinned
assert_loaded("test_kprobe");
// 3. Load program from bpffs and attach
{
let mut prog = KProbe::from_pin(
"/sys/fs/bpf/aya-kprobe-test-prog",
aya::programs::ProbeKind::KProbe,
)
.unwrap();
let link_id = prog.attach("try_to_wake_up", 0).unwrap();
let link = prog.take_link(link_id).unwrap();
let fd_link: FdLink = link.try_into().unwrap();
fd_link
.pin("/sys/fs/bpf/aya-kprobe-test-try-to-wake-up")
.unwrap();
// Unpin the program. It will stay attached since its links were pinned.
prog.unpin().unwrap();
}
// should still be loaded since link was pinned
assert_loaded_and_linked("test_kprobe");
// 4. unpin link, and make sure everything is unloaded
{
PinnedLink::from_pin("/sys/fs/bpf/aya-kprobe-test-try-to-wake-up")
.unwrap()
.unpin()
.unwrap();
}
// program should be unloaded
assert_unloaded("test_kprobe");
type P = KProbe;
let program_name = "test_kprobe";
let attach = |prog: &mut P| prog.attach("try_to_wake_up", 0).unwrap();
let program_pin = "/sys/fs/bpf/aya-kprobe-test-prog";
let link_pin = "/sys/fs/bpf/aya-kprobe-test-try-to-wake-up";
let from_pin = |program_pin: &str| P::from_pin(program_pin, ProbeKind::KProbe).unwrap();
run_pin_program_lifecycle_test(
crate::TEST,
program_name,
program_pin,
link_pin,
from_pin,
attach,
None,
);
}
#[unsafe(no_mangle)]
@ -552,55 +519,25 @@ extern "C" fn uprobe_function() {
#[test_log::test]
fn pin_lifecycle_uprobe() {
const FIRST_PIN_PATH: &str = "/sys/fs/bpf/aya-uprobe-test-prog-1";
const SECOND_PIN_PATH: &str = "/sys/fs/bpf/aya-uprobe-test-prog-2";
// 1. Load Program and Pin
{
let mut bpf = Ebpf::load(crate::TEST).unwrap();
let prog: &mut UProbe = bpf.program_mut("test_uprobe").unwrap().try_into().unwrap();
prog.load().unwrap();
prog.pin(FIRST_PIN_PATH).unwrap();
}
// should still be loaded since prog was pinned
assert_loaded("test_uprobe");
type P = UProbe;
// 2. Load program from bpffs but don't attach it
{
let _ = UProbe::from_pin(FIRST_PIN_PATH, aya::programs::ProbeKind::UProbe).unwrap();
}
// should still be loaded since prog was pinned
assert_loaded("test_uprobe");
// 3. Load program from bpffs and attach
{
let mut prog = UProbe::from_pin(FIRST_PIN_PATH, aya::programs::ProbeKind::UProbe).unwrap();
let link_id = prog
.attach("uprobe_function", "/proc/self/exe", None, None)
.unwrap();
let link = prog.take_link(link_id).unwrap();
let fd_link: FdLink = link.try_into().unwrap();
fd_link.pin(SECOND_PIN_PATH).unwrap();
// Unpin the program. It will stay attached since its links were pinned.
prog.unpin().unwrap();
}
// should still be loaded since link was pinned
assert_loaded_and_linked("test_uprobe");
// 4. unpin link, and make sure everything is unloaded
{
PinnedLink::from_pin(SECOND_PIN_PATH)
let program_name = "test_uprobe";
let attach = |prog: &mut P| {
prog.attach("uprobe_function", "/proc/self/exe", None, None)
.unwrap()
.unpin()
.unwrap();
}
// program should be unloaded
assert_unloaded("test_uprobe");
};
let program_pin = "/sys/fs/bpf/aya-uprobe-test-prog";
let link_pin = "/sys/fs/bpf/aya-uprobe-test-uprobe-function";
let from_pin = |program_pin: &str| P::from_pin(program_pin, ProbeKind::UProbe).unwrap();
run_pin_program_lifecycle_test(
crate::TEST,
program_name,
program_pin,
link_pin,
from_pin,
attach,
None,
);
// Make sure the function isn't optimized out.
uprobe_function();

@ -187,6 +187,15 @@ fn log() {
})
);
assert_eq!(
records.next(),
Some(&CapturedLog {
body: "ptr: 0xdeadbeef".into(),
level: Level::Debug,
target: "log".into(),
})
);
assert_eq!(
records.next(),
Some(&CapturedLog {

@ -15,6 +15,7 @@ pub aya_log_common::ArgumentKind::I8
pub aya_log_common::ArgumentKind::Ipv4Addr
pub aya_log_common::ArgumentKind::Ipv6Addr
pub aya_log_common::ArgumentKind::Isize
pub aya_log_common::ArgumentKind::Pointer
pub aya_log_common::ArgumentKind::Str
pub aya_log_common::ArgumentKind::U16
pub aya_log_common::ArgumentKind::U32
@ -57,6 +58,7 @@ pub aya_log_common::DisplayHint::Default = 1
pub aya_log_common::DisplayHint::Ip
pub aya_log_common::DisplayHint::LowerHex
pub aya_log_common::DisplayHint::LowerMac
pub aya_log_common::DisplayHint::Pointer
pub aya_log_common::DisplayHint::UpperHex
pub aya_log_common::DisplayHint::UpperMac
impl aya_log_common::Argument for aya_log_common::DisplayHint
@ -215,6 +217,10 @@ impl aya_log_common::Argument for u8
pub fn u8::as_argument(&self) -> (aya_log_common::ArgumentKind, impl core::convert::AsRef<[u8]>)
impl aya_log_common::Argument for usize
pub fn usize::as_argument(&self) -> (aya_log_common::ArgumentKind, impl core::convert::AsRef<[u8]>)
impl<T> aya_log_common::Argument for *const T
pub fn *const T::as_argument(&self) -> (aya_log_common::ArgumentKind, impl core::convert::AsRef<[u8]>)
impl<T> aya_log_common::Argument for *mut T
pub fn *mut T::as_argument(&self) -> (aya_log_common::ArgumentKind, impl core::convert::AsRef<[u8]>)
impl<const N: usize> aya_log_common::Argument for [u8; N]
pub fn [u8; N]::as_argument(&self) -> (aya_log_common::ArgumentKind, impl core::convert::AsRef<[u8]>)
pub trait aya_log_common::DefaultFormatter
@ -260,6 +266,9 @@ impl aya_log_common::LowerHexFormatter for usize
impl<const N: usize> aya_log_common::LowerHexFormatter for &[u8; N]
pub trait aya_log_common::LowerMacFormatter
impl aya_log_common::LowerMacFormatter for [u8; 6]
pub trait aya_log_common::PointerFormatter
impl<T> aya_log_common::PointerFormatter for *const T
impl<T> aya_log_common::PointerFormatter for *mut T
pub trait aya_log_common::UpperHexFormatter
impl aya_log_common::UpperHexFormatter for &[u8]
impl aya_log_common::UpperHexFormatter for i16

@ -223,6 +223,33 @@ impl<T> core::borrow::BorrowMut<T> for aya_log::LowerMacFormatter where T: ?core
pub fn aya_log::LowerMacFormatter::borrow_mut(&mut self) -> &mut T
impl<T> core::convert::From<T> for aya_log::LowerMacFormatter
pub fn aya_log::LowerMacFormatter::from(t: T) -> T
pub struct aya_log::PointerFormatter
impl<T> aya_log::Formatter<*const T> for aya_log::PointerFormatter
pub fn aya_log::PointerFormatter::format(v: *const T) -> alloc::string::String
impl<T> aya_log::Formatter<*mut T> for aya_log::PointerFormatter
pub fn aya_log::PointerFormatter::format(v: *mut T) -> alloc::string::String
impl core::marker::Freeze for aya_log::PointerFormatter
impl core::marker::Send for aya_log::PointerFormatter
impl core::marker::Sync for aya_log::PointerFormatter
impl core::marker::Unpin for aya_log::PointerFormatter
impl core::panic::unwind_safe::RefUnwindSafe for aya_log::PointerFormatter
impl core::panic::unwind_safe::UnwindSafe for aya_log::PointerFormatter
impl<T, U> core::convert::Into<U> for aya_log::PointerFormatter where U: core::convert::From<T>
pub fn aya_log::PointerFormatter::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya_log::PointerFormatter where U: core::convert::Into<T>
pub type aya_log::PointerFormatter::Error = core::convert::Infallible
pub fn aya_log::PointerFormatter::try_from(value: U) -> core::result::Result<T, <T as core::convert::TryFrom<U>>::Error>
impl<T, U> core::convert::TryInto<U> for aya_log::PointerFormatter where U: core::convert::TryFrom<T>
pub type aya_log::PointerFormatter::Error = <U as core::convert::TryFrom<T>>::Error
pub fn aya_log::PointerFormatter::try_into(self) -> core::result::Result<U, <U as core::convert::TryFrom<T>>::Error>
impl<T> core::any::Any for aya_log::PointerFormatter where T: 'static + ?core::marker::Sized
pub fn aya_log::PointerFormatter::type_id(&self) -> core::any::TypeId
impl<T> core::borrow::Borrow<T> for aya_log::PointerFormatter where T: ?core::marker::Sized
pub fn aya_log::PointerFormatter::borrow(&self) -> &T
impl<T> core::borrow::BorrowMut<T> for aya_log::PointerFormatter where T: ?core::marker::Sized
pub fn aya_log::PointerFormatter::borrow_mut(&mut self) -> &mut T
impl<T> core::convert::From<T> for aya_log::PointerFormatter
pub fn aya_log::PointerFormatter::from(t: T) -> T
pub struct aya_log::UpperHexBytesFormatter
impl aya_log::Formatter<&[u8]> for aya_log::UpperHexBytesFormatter
pub fn aya_log::UpperHexBytesFormatter::format(v: &[u8]) -> alloc::string::String
@ -309,6 +336,10 @@ impl aya_log::Formatter<[u8; 6]> for aya_log::LowerMacFormatter
pub fn aya_log::LowerMacFormatter::format(v: [u8; 6]) -> alloc::string::String
impl aya_log::Formatter<[u8; 6]> for aya_log::UpperMacFormatter
pub fn aya_log::UpperMacFormatter::format(v: [u8; 6]) -> alloc::string::String
impl<T> aya_log::Formatter<*const T> for aya_log::PointerFormatter
pub fn aya_log::PointerFormatter::format(v: *const T) -> alloc::string::String
impl<T> aya_log::Formatter<*mut T> for aya_log::PointerFormatter
pub fn aya_log::PointerFormatter::format(v: *mut T) -> alloc::string::String
impl<T> aya_log::Formatter<T> for aya_log::DefaultFormatter where T: alloc::string::ToString
pub fn aya_log::DefaultFormatter::format(v: T) -> alloc::string::String
impl<T> aya_log::Formatter<T> for aya_log::Ipv4Formatter where T: core::convert::Into<core::net::ip_addr::Ipv4Addr>

@ -3828,6 +3828,9 @@ pub fn aya::programs::flow_dissector::FlowDissectorLink::id(&self) -> Self::Id
impl core::cmp::Eq for aya::programs::flow_dissector::FlowDissectorLink
impl core::cmp::PartialEq for aya::programs::flow_dissector::FlowDissectorLink
pub fn aya::programs::flow_dissector::FlowDissectorLink::eq(&self, other: &Self) -> bool
impl core::convert::TryFrom<aya::programs::flow_dissector::FlowDissectorLink> for aya::programs::links::FdLink
pub type aya::programs::links::FdLink::Error = aya::programs::links::LinkError
pub fn aya::programs::links::FdLink::try_from(value: aya::programs::flow_dissector::FlowDissectorLink) -> core::result::Result<Self, Self::Error>
impl core::fmt::Debug for aya::programs::flow_dissector::FlowDissectorLink
pub fn aya::programs::flow_dissector::FlowDissectorLink::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
impl core::hash::Hash for aya::programs::flow_dissector::FlowDissectorLink
@ -4416,6 +4419,9 @@ pub fn aya::programs::links::FdLink::try_from(value: aya::programs::cgroup_sock:
impl core::convert::TryFrom<aya::programs::cgroup_sock_addr::CgroupSockAddrLink> for aya::programs::links::FdLink
pub type aya::programs::links::FdLink::Error = aya::programs::links::LinkError
pub fn aya::programs::links::FdLink::try_from(value: aya::programs::cgroup_sock_addr::CgroupSockAddrLink) -> core::result::Result<Self, Self::Error>
impl core::convert::TryFrom<aya::programs::flow_dissector::FlowDissectorLink> for aya::programs::links::FdLink
pub type aya::programs::links::FdLink::Error = aya::programs::links::LinkError
pub fn aya::programs::links::FdLink::try_from(value: aya::programs::flow_dissector::FlowDissectorLink) -> core::result::Result<Self, Self::Error>
impl core::convert::TryFrom<aya::programs::iter::IterLink> for aya::programs::links::FdLink
pub type aya::programs::links::FdLink::Error = aya::programs::links::LinkError
pub fn aya::programs::links::FdLink::try_from(value: aya::programs::iter::IterLink) -> core::result::Result<Self, Self::Error>

@ -3,9 +3,9 @@ mod docs;
mod public_api;
mod run;
use std::process::Command;
use std::process::{Command, Output};
use anyhow::{Context as _, Result};
use anyhow::{Context as _, Result, bail};
use cargo_metadata::{Metadata, MetadataCommand};
use clap::Parser;
use xtask::{LIBBPF_DIR, exec};
@ -33,12 +33,29 @@ fn main() -> Result<()> {
.context("failed to run cargo metadata")?;
let Metadata { workspace_root, .. } = &metadata;
// Initialize the submodules.
exec(Command::new("git").arg("-C").arg(workspace_root).args([
"submodule",
"update",
"--init",
]))?;
let mut libbpf_submodule_status = Command::new("git");
let output = libbpf_submodule_status
.arg("-C")
.arg(workspace_root)
.arg("submodule")
.arg("status")
.arg(LIBBPF_DIR)
.output()
.with_context(|| format!("failed to run {libbpf_submodule_status:?}"))?;
let Output { status, .. } = &output;
if !status.success() {
bail!("{libbpf_submodule_status:?} failed: {output:?}")
}
let Output { stdout, .. } = output;
if !stdout.starts_with(b" ") {
// Initialize the submodules.
exec(Command::new("git").arg("-C").arg(workspace_root).args([
"submodule",
"update",
"--init",
]))?;
}
let libbpf_dir = workspace_root.join(LIBBPF_DIR);
let libbpf_dir = libbpf_dir.as_std_path();

@ -245,7 +245,7 @@ pub(crate) fn run(opts: Options) -> Result<()> {
let etag_path_exists = etag_path.try_exists().with_context(|| {
format!("failed to check existence of {}", etag_path.display())
})?;
if !dest_path_exists && etag_path_exists {
if dest_path_exists != etag_path_exists {
println!(
"cargo:warning=({}).exists()={} != ({})={} (mismatch)",
dest_path.display(),
@ -274,7 +274,14 @@ pub(crate) fn run(opts: Options) -> Result<()> {
.with_context(|| format!("failed to run {curl:?}"))?;
let Output { status, .. } = &output;
if status.code() != Some(0) {
bail!("{curl:?} failed: {output:?}")
if dest_path_exists {
println!(
"cargo:warning={curl:?} failed ({status:?}); using cached {}",
dest_path.display()
);
} else {
bail!("{curl:?} failed: {output:?}")
}
}
let mut patch = Command::new("patch");

Loading…
Cancel
Save