Browse Source

aya-log: allow re-attach and read previously created logs

This feature is useful if someone wants to view the log contents
of a program that is already running. For e.g. a pinned program
or an XDP program attached to a net interface.
Catalin Horghidan 1 year ago
parent
commit
e66f954
1 changed files with 57 additions and 8 deletions
  1. 57 8
      aya-log/src/lib.rs

+ 57 - 8
aya-log/src/lib.rs

@@ -60,10 +60,12 @@ use std::{
 const MAP_NAME: &str = "AYA_LOGS";
 
 use aya::{
+    loaded_programs,
     maps::{
         perf::{AsyncPerfEventArray, Events, PerfBufferError},
-        MapError,
+        Map, MapData, MapError, MapInfo,
     },
+    programs::ProgramError,
     util::online_cpus,
     Ebpf, Pod,
 };
@@ -112,12 +114,54 @@ impl EbpfLogger {
         bpf: &mut Ebpf,
         logger: T,
     ) -> Result<EbpfLogger, Error> {
-        let logger = Arc::new(logger);
-        let mut logs: AsyncPerfEventArray<_> = bpf
-            .take_map(MAP_NAME)
-            .ok_or(Error::MapNotFound)?
-            .try_into()?;
+        let map = bpf.take_map(MAP_NAME).ok_or(Error::MapNotFound)?;
+        Self::read_logs_async(map, logger)?;
+        Ok(EbpfLogger {})
+    }
+
+    /// Attaches to an existing `aya-log-ebpf` log created by a running ebpf
+    /// program identified by `program_id`. The log records will be written
+    /// to the default logger. See [log::logger].
+    /// It can be used to review the log for a pinned program after the user
+    /// application exits.
+    pub fn init_from_id(program_id: u32) -> Result<EbpfLogger, Error> {
+        Self::init_from_id_with_logger(program_id, log::logger())
+    }
 
+    /// Attaches to an existing `aya-log-ebpf` log created by a running ebpf
+    /// program identified by `program_id`. The log records will be written
+    /// to the user provided logger.
+    /// It can be used to review the log for a pinned program after the user
+    /// application exits.
+    pub fn init_from_id_with_logger<T: Log + 'static>(
+        program_id: u32,
+        logger: T,
+    ) -> Result<EbpfLogger, Error> {
+        let program_info = loaded_programs()
+            .filter_map(|info| info.ok())
+            .find(|info| info.id() == program_id)
+            .ok_or(Error::ProgramNotFound)?;
+        let map = program_info
+            .map_ids()
+            .map_err(Error::ProgramError)?
+            .iter()
+            .filter_map(|id| MapInfo::from_id(*id).ok())
+            .find(|map_info| match map_info.name_as_str() {
+                Some(name) => name == MAP_NAME,
+                None => false,
+            })
+            .ok_or(Error::MapNotFound)?;
+        let map = MapData::from_id(map.id()).map_err(Error::MapError)?;
+
+        Self::read_logs_async(Map::PerfEventArray(map), logger)?;
+
+        Ok(EbpfLogger {})
+    }
+
+    fn read_logs_async<T: Log + 'static>(map: Map, logger: T) -> Result<(), Error> {
+        let mut logs: AsyncPerfEventArray<_> = map.try_into()?;
+
+        let logger = Arc::new(logger);
         for cpu_id in online_cpus().map_err(Error::InvalidOnlineCpu)? {
             let mut buf = logs.open(cpu_id, None)?;
 
@@ -134,8 +178,7 @@ impl EbpfLogger {
                 }
             });
         }
-
-        Ok(EbpfLogger {})
+        Ok(())
     }
 }
 
@@ -374,6 +417,12 @@ pub enum Error {
 
     #[error("invalid /sys/devices/system/cpu/online format")]
     InvalidOnlineCpu(#[source] io::Error),
+
+    #[error("program not found")]
+    ProgramNotFound,
+
+    #[error(transparent)]
+    ProgramError(#[from] ProgramError),
 }
 
 fn log_buf(mut buf: &[u8], logger: &dyn Log) -> Result<(), ()> {