bpf_loader_lib/export_event/
mod.rs

1//!  SPDX-License-Identifier: MIT
2//!
3//! Copyright (c) 2023, eunomia-bpf
4//! All rights reserved.
5//!
6
7//! # Event exporter for bpf-loader-rs
8//!
9//! ## What does this module do?
10//! In a nut shell, this module provides a struct `EventExporter`, which accepts exported data from a ebpf program, verifies the data, then output them to stdout or the user-provided callback function.
11//!
12//! ## Constructors
13//! Usually, ebpf program will export data sampled from the kernel in one of the two ways:
14//! - ringbuf
15//! - bpf map
16//! There are also two constructors for EventExporter, which corresponds to the two ways, `EventExporterBuilder::build_for_ringbuf` and `EventExporterBuilder::build_for_map_sampling`
17//!
18//! ## What will be produced
19//!
20//! You can get the data you want in one of the three formats:
21//! - Json
22//! - PlainText
23//! - RawEvent
24//!
25//! Each time the EventExporter received data from the ebpf program, it will convert the data to the format you want, and call the callback function to acknowledge you the data, or print that to stdout
26//! ## Json
27//! Convert the received data to a JsonValue. With the help of BTF, we can know at which offset exists which type of data. So It's ok to construct a JsonValue representing the type we want to see. The JsonValue will be passed to the callback in string format, or printed to stdout if you don't want to provide a callback
28//!
29//! ## PlainText
30//! It's similar to JSON, except that it's not structured, only human readable texts
31//!
32//! ## RawEvent
33//! It will call the callback with the original data received from ebpf program. If no callback was provided, it will do nothing.
34
35use crate::{
36    btf_container::BtfContainer,
37    export_event::checker::check_sample_types_btf,
38    meta::{BufferValueInterpreter, ExportedTypesStructMeta, MapSampleMeta, SampleMapType},
39};
40use anyhow::{anyhow, bail, Context, Result};
41use log::debug;
42use std::{any::Any, fmt::Display, sync::Arc};
43
44use self::{
45    checker::check_export_types_btf,
46    event_handlers::{buffer, get_plain_text_checked_types_header, sample_map},
47    type_descriptor::{CheckedExportedMember, TypeDescriptor},
48};
49
50pub(crate) mod checker;
51pub(crate) mod data_dumper;
52pub(crate) mod event_handlers;
53#[cfg(test)]
54mod tests;
55/// Contains utilities to describe where to obtain the export type of a map
56pub mod type_descriptor;
57#[derive(Clone, Copy)]
58/// Describe the export format type
59pub enum ExportFormatType {
60    /// Use human-readable texts to output
61    PlainText,
62    /// Use machine-readable json to output
63    Json,
64    /// Only call the callback with raw buffer
65    RawEvent,
66}
67#[derive(Debug)]
68/// Represents a sample data that the user will receive
69pub enum ReceivedEventData<'a> {
70    /// Raw buffer. will be used on simple value sampling pairing with `ExportFormatType::RawEvent`
71    Buffer(&'a [u8]),
72    /// KeyValue Buffer. will be used on key-value sampling pairing with ` ExportFormatType::RawEvent`
73    KeyValueBuffer {
74        key: &'a [u8],
75        value: &'a [u8],
76    },
77    // Plain string. will be used on ` ExportFormatType::PlainText`
78    PlainText(&'a str),
79    // Json string. Will be used on `ExportFormatType::Json`
80    JsonText(&'a str),
81}
82impl<'a> Display for ReceivedEventData<'a> {
83    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84        match self {
85            ReceivedEventData::Buffer(b) => {
86                write!(f, "{b:?}")?;
87            }
88            ReceivedEventData::KeyValueBuffer { key, value } => {
89                write!(f, "key: {key:?} value: {value:?}")?;
90            }
91            ReceivedEventData::PlainText(s) | ReceivedEventData::JsonText(s) => {
92                write!(f, "{s}")?;
93            }
94        }
95        Ok(())
96    }
97}
98
99impl<'a> ReceivedEventData<'a> {
100    /// Simply get a &[u8] from this received data, which is the same as the one that the C++ version callback receives
101    pub fn trivally_to_plain_bytes(&self) -> &'a [u8] {
102        match self {
103            ReceivedEventData::Buffer(buf) => buf,
104            ReceivedEventData::KeyValueBuffer { value, .. } => value,
105            ReceivedEventData::PlainText(txt) => txt.as_bytes(),
106            ReceivedEventData::JsonText(txt) => txt.as_bytes(),
107        }
108    }
109}
110
111/// A handler to receive events provided by ebpf kernel program
112pub trait EventHandler {
113    fn handle_event(&self, context: Option<Arc<dyn Any>>, data: ReceivedEventData);
114}
115
116pub(crate) enum ExporterInternalImplementation {
117    BufferValueProcessor {
118        /// internal handler to process export data to a given format
119        event_processor: Box<dyn InternalBufferValueEventProcessor>,
120        /// exported value types
121        checked_types: Vec<CheckedExportedMember>,
122    },
123    KeyValueMapProcessor {
124        /// internal handler to sample map data to a given format
125        event_processor: Box<dyn InternalSampleMapProcessor>,
126        /// export map key types meta data
127        checked_key_types: Vec<CheckedExportedMember>,
128        /// export map value types meta data
129        checked_value_types: Vec<CheckedExportedMember>,
130        /// Config of the sampling map
131        sample_map_config: MapSampleMeta,
132    },
133}
134
135/// dump export event in user space
136pub struct EventExporter {
137    /// user defined handler to process export data
138    pub(crate) user_export_event_handler: Option<Arc<dyn EventHandler>>,
139    pub(crate) internal_impl: ExporterInternalImplementation,
140    /// user-defined context
141    pub(crate) user_ctx: Option<Arc<dyn Any>>,
142    pub(crate) btf_container: Arc<BtfContainer>,
143}
144
145impl EventExporter {
146    pub(crate) fn dump_data_to_user_callback_or_stdout(&self, data: ReceivedEventData) {
147        dump_data_to_user_callback_or_stdout(
148            self.user_export_event_handler.clone(),
149            self.user_ctx.clone(),
150            data,
151        );
152    }
153}
154pub(crate) fn dump_data_to_user_callback_or_stdout(
155    user_export_event_handler: Option<Arc<dyn EventHandler>>,
156    user_ctx: Option<Arc<dyn Any>>,
157    data: ReceivedEventData,
158) {
159    if let Some(callback) = user_export_event_handler.as_ref() {
160        callback.handle_event(user_ctx, data);
161    } else {
162        println!("{data}");
163    }
164}
165pub(crate) trait InternalBufferValueEventProcessor {
166    fn handle_event(&self, data: &[u8]) -> Result<()>;
167}
168
169pub(crate) trait InternalSampleMapProcessor {
170    fn handle_event(&self, key_buffer: &[u8], value_buffer: &[u8]) -> Result<()>;
171}
172
173/// The builder of the EventExporter
174pub struct EventExporterBuilder {
175    export_format: ExportFormatType,
176    export_event_handler: Option<Arc<dyn EventHandler>>,
177    user_ctx: Option<Arc<dyn Any>>,
178}
179
180impl Default for EventExporterBuilder {
181    fn default() -> Self {
182        Self {
183            export_format: ExportFormatType::PlainText,
184            export_event_handler: None,
185            user_ctx: None,
186        }
187    }
188}
189
190impl EventExporterBuilder {
191    /// Create a default Builder, with export_format defaults to `ExportFormat::PlainText`
192    pub fn new() -> Self {
193        Self::default()
194    }
195    /// Set the export format that the ebpf program will export
196    pub fn set_export_format(self, fmt: ExportFormatType) -> Self {
197        Self {
198            export_format: fmt,
199            ..self
200        }
201    }
202    /// Set a user-defined event handler callback
203    pub fn set_export_event_handler(self, handler: Arc<dyn EventHandler>) -> Self {
204        Self {
205            export_event_handler: Some(handler),
206            ..self
207        }
208    }
209    /// Set the user-defined context
210    pub fn set_user_context<T: Any>(self, ctx: T) -> Self {
211        Self {
212            user_ctx: Some(Arc::new(ctx)),
213            ..self
214        }
215    }
216    /// Build an exporter use TypeDescriptor. Which can easily specify the source to obtain the value type
217    pub fn build_for_single_value_with_type_descriptor(
218        self,
219        export_type: TypeDescriptor,
220        btf_container: Arc<BtfContainer>,
221        intepreter: &BufferValueInterpreter,
222    ) -> Result<Arc<EventExporter>> {
223        let mut checked_exported_members =
224            export_type.build_checked_exported_members(btf_container.borrow_btf())?;
225        if matches!(intepreter, BufferValueInterpreter::StackTrace { .. })
226            && !matches!(self.export_format, ExportFormatType::PlainText)
227        {
228            bail!("Intepreter `stack_trace` could only be paired with plaintext export format");
229        }
230        Ok(Arc::new_cyclic(move |me| {
231            let internal_event_processor: Box<dyn InternalBufferValueEventProcessor> =
232                match (self.export_format, intepreter) {
233                    (ExportFormatType::Json, BufferValueInterpreter::DefaultStruct) => {
234                        Box::new(buffer::JsonExportEventHandler {
235                            exporter: me.clone(),
236                        })
237                    }
238                    (
239                        ExportFormatType::PlainText,
240                        BufferValueInterpreter::StackTrace {
241                            field_map,
242                            with_symbols,
243                        },
244                    ) => {
245                        debug!("Using stack trace exporter");
246                        Box::new(buffer::PlainTextStackTraceExportEventHandler {
247                            exporter: me.clone(),
248                            field_mapping: field_map.clone(),
249                            with_symbols: *with_symbols,
250                        })
251                    }
252                    (ExportFormatType::PlainText, BufferValueInterpreter::DefaultStruct) => {
253                        let header = get_plain_text_checked_types_header(
254                            &mut checked_exported_members,
255                            "TIME     ",
256                        );
257                        dump_data_to_user_callback_or_stdout(
258                            self.export_event_handler.clone(),
259                            self.user_ctx.clone(),
260                            ReceivedEventData::PlainText(header.as_str()),
261                        );
262                        Box::new(buffer::PlainStringExportEventHandler {
263                            exporter: me.clone(),
264                        })
265                    }
266
267                    (ExportFormatType::RawEvent, BufferValueInterpreter::DefaultStruct) => {
268                        Box::new(buffer::RawExportEventHandler {
269                            exporter: me.clone(),
270                        })
271                    }
272                    (_, _) => unreachable!("Unexpected exportformattype + intepreter"),
273                };
274            EventExporter {
275                user_export_event_handler: self.export_event_handler,
276                user_ctx: self.user_ctx,
277                btf_container,
278                internal_impl: ExporterInternalImplementation::BufferValueProcessor {
279                    event_processor: internal_event_processor,
280                    checked_types: checked_exported_members,
281                },
282            }
283        }))
284    }
285    /// Build an EventExporter which is suitable for processing value-only data
286    /// export_types: The type of the data that kernel program sends. The types will be verified using BTF
287    /// btf_container: The BTF information
288    pub fn build_for_single_value(
289        self,
290        export_type: &ExportedTypesStructMeta,
291        btf_container: Arc<BtfContainer>,
292        intepreter: &BufferValueInterpreter,
293    ) -> Result<Arc<EventExporter>> {
294        let checked_members = check_export_types_btf(export_type, btf_container.borrow_btf())?;
295        Self::build_for_single_value_with_type_descriptor(
296            self,
297            TypeDescriptor::CheckedMembers(checked_members),
298            btf_container,
299            intepreter,
300        )
301    }
302    /// Build an EventExporter, but use TypeDescriptor to indicate where to fetch the value type
303    pub fn build_for_key_value_with_type_desc(
304        self,
305        key_export_type: TypeDescriptor,
306        value_export_type: TypeDescriptor,
307        sample_config: &MapSampleMeta,
308        btf_container: Arc<BtfContainer>,
309    ) -> Result<Arc<EventExporter>> {
310        let mut checked_key_types =
311            key_export_type.build_checked_exported_members(btf_container.borrow_btf())?;
312        let mut checked_value_types =
313            value_export_type.build_checked_exported_members(btf_container.borrow_btf())?;
314
315        if matches!(self.export_format, ExportFormatType::PlainText)
316            && matches!(sample_config.ty, SampleMapType::LinearHist)
317        {
318            bail!("Linear hist sampling is not supported now");
319        }
320        Ok(Arc::new_cyclic(move |me| {
321            let internal_sample_map_processor: Box<dyn InternalSampleMapProcessor> = match self
322                .export_format
323            {
324                ExportFormatType::PlainText => match sample_config.ty {
325                    SampleMapType::Log2Hist => Box::new(sample_map::Log2HistExportEventHandler {
326                        exporter: me.clone(),
327                    }),
328                    SampleMapType::DefaultKV => {
329                        let header = String::from("TIME     ");
330                        let header =
331                            get_plain_text_checked_types_header(&mut checked_key_types, header);
332                        let header =
333                            get_plain_text_checked_types_header(&mut checked_value_types, header);
334                        dump_data_to_user_callback_or_stdout(
335                            self.export_event_handler.clone(),
336                            self.user_ctx.clone(),
337                            ReceivedEventData::PlainText(header.as_str()),
338                        );
339                        Box::new(sample_map::DefaultKVStringExportEventHandler {
340                            exporter: me.clone(),
341                        })
342                    }
343                    SampleMapType::LinearHist => unreachable!(),
344                },
345                ExportFormatType::Json => Box::new(sample_map::JsonExportEventHandler {
346                    exporter: me.clone(),
347                }),
348                ExportFormatType::RawEvent => Box::new(sample_map::RawExportEventHandler {
349                    exporter: me.clone(),
350                }),
351            };
352            EventExporter {
353                user_export_event_handler: self.export_event_handler,
354                internal_impl: ExporterInternalImplementation::KeyValueMapProcessor {
355                    event_processor: internal_sample_map_processor,
356                    checked_key_types,
357                    checked_value_types,
358                    sample_map_config: sample_config.clone(),
359                },
360                user_ctx: self.user_ctx,
361                btf_container,
362            }
363        }))
364    }
365    /// Build an EventExporter to process sampling map
366    /// key_type_id: The type id of the map key
367    /// value_type_id: The type id of the map value
368    /// sample_config: Detailed configuration of the sampling map
369    /// export_types: Value types of the map, will be verified
370    /// btf_container: The btf information
371    pub fn build_for_key_value(
372        self,
373        key_type_id: u32,
374        value_type_id: u32,
375        sample_config: &MapSampleMeta,
376        export_type: &ExportedTypesStructMeta,
377        btf_container: Arc<BtfContainer>,
378    ) -> Result<Arc<EventExporter>> {
379        let btf = btf_container.borrow_btf();
380        let checked_key_types = check_sample_types_btf(btf, key_type_id, None)
381            .with_context(|| anyhow!("Failed to check key type"))?;
382        let checked_value_types =
383            check_sample_types_btf(btf, value_type_id, Some(export_type.clone()))
384                .with_context(|| anyhow!("Failed to check value type"))?;
385        self.build_for_key_value_with_type_desc(
386            TypeDescriptor::CheckedMembers(checked_key_types),
387            TypeDescriptor::CheckedMembers(checked_value_types),
388            sample_config,
389            btf_container,
390        )
391    }
392}