1use base64::Engine;
68use deflate::deflate_bytes_zlib;
69use libbpf_rs::libbpf_sys::{BPF_TC_CUSTOM, BPF_TC_EGRESS, BPF_TC_INGRESS};
70use serde::{Deserialize, Serialize};
71use serde_json::Value;
72use serde_with::DefaultOnNull;
73#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
75pub struct ExportedTypesStructMemberMeta {
76 pub name: String,
78 #[serde(rename = "type")]
79 pub ty: String,
81}
82
83#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
85pub struct ExportedTypesStructMeta {
86 pub name: String,
88 pub members: Vec<ExportedTypesStructMemberMeta>,
90 pub size: u32,
92 pub type_id: u32,
94}
95#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Default)]
96pub enum SampleMapType {
98 #[serde(rename = "log2_hist")]
99 Log2Hist,
101 #[serde(rename = "linear_hist")]
102 LinearHist,
104 #[serde(rename = "default_kv")]
105 #[default]
106 DefaultKV,
108}
109
110#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
112pub struct MapSampleMeta {
113 pub interval: usize,
115 #[serde(rename = "type", default)]
117 pub ty: SampleMapType,
118 #[serde(default = "default_helpers::map_unit_default")]
120 pub unit: String,
121 #[serde(default = "default_helpers::default_bool::<false>")]
123 pub clear_map: bool,
124}
125
126#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
128pub struct OverridedStructMember {
129 pub name: String,
131 pub offset: usize,
133 pub btf_type_id: u32,
135}
136#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
138pub enum MapExportConfig {
139 #[serde(rename = "no_export")]
141 NoExport,
142 #[serde(rename = "btf_type_id")]
144 ExportUseBtf(u32),
145 #[serde(rename = "custom_members")]
147 ExportUseCustomMembers(Vec<OverridedStructMember>),
148 #[serde(rename = "default")]
150 Default,
151}
152
153impl Default for MapExportConfig {
154 fn default() -> Self {
155 Self::NoExport
156 }
157}
158
159#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Default)]
160pub struct StackTraceFieldMapping {
162 #[serde(skip_serializing_if = "Option::is_none")]
163 pub pid: Option<String>,
164 #[serde(skip_serializing_if = "Option::is_none")]
165 pub cpu_id: Option<String>,
166 #[serde(skip_serializing_if = "Option::is_none")]
167 pub comm: Option<String>,
168 #[serde(skip_serializing_if = "Option::is_none")]
169 pub kstack_sz: Option<String>,
170 #[serde(skip_serializing_if = "Option::is_none")]
171 pub ustack_sz: Option<String>,
172 #[serde(skip_serializing_if = "Option::is_none")]
173 pub kstack: Option<String>,
174 #[serde(skip_serializing_if = "Option::is_none")]
175 pub ustack: Option<String>,
176}
177
178#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
182pub enum BufferValueInterpreter {
183 #[serde(rename = "default_struct")]
184 DefaultStruct,
185 #[serde(rename = "stack_trace")]
186 StackTrace {
187 #[serde(default)]
188 field_map: StackTraceFieldMapping,
189 #[serde(default = "default_helpers::default_bool::<true>")]
190 with_symbols: bool,
191 },
192}
193
194impl Default for BufferValueInterpreter {
195 fn default() -> Self {
196 Self::DefaultStruct
197 }
198}
199
200#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
202pub struct MapMeta {
203 pub name: String,
205 pub ident: String,
207 #[serde(default = "default_helpers::default_bool::<false>")]
209 pub mmaped: bool,
210 #[serde(skip_serializing_if = "Option::is_none")]
212 pub sample: Option<MapSampleMeta>,
213 #[serde(default)]
215 pub export_config: MapExportConfig,
216 #[serde(default)]
218 pub intepreter: BufferValueInterpreter,
219}
220#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
221pub struct ProgMeta {
223 pub name: String,
225 pub attach: String,
227 pub link: bool,
229 #[serde(flatten)]
230 pub others: Value,
232}
233
234#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
235pub struct XDPProgExtraMeta {
237 #[serde(default = "default_helpers::default_i32::<1>")]
238 pub ifindex: i32,
240 #[serde(default = "default_helpers::default_u32::<0>")]
241 pub flags: u32,
243 #[serde(default)]
244 pub xdpopts: XDPOpts,
246}
247
248#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
249#[derive(Default)]
251pub struct XDPOpts {
252 #[serde(default = "default_helpers::default_i32::<0>")]
253 pub old_prog_fd: i32,
255}
256
257#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
258pub struct TCProgExtraMeta {
260 #[serde(default)]
261 pub tchook: TCHook,
263 #[serde(default)]
264 pub tcopts: TCOpts,
266}
267
268#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
269pub struct TCHook {
271 #[serde(default = "default_helpers::default_i32::<1>")]
272 pub ifindex: i32,
274 #[serde(default)]
275 pub attach_point: TCAttachPoint,
277}
278impl Default for TCHook {
279 fn default() -> Self {
280 Self {
281 ifindex: 1,
282 attach_point: TCAttachPoint::default(),
283 }
284 }
285}
286
287#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
288pub enum TCAttachPoint {
290 #[serde(rename = "BPF_TC_INGRESS")]
291 Ingress,
293 #[serde(rename = "BPF_TC_EGRESS")]
294 Egress,
296 #[serde(rename = "BPF_TC_CUSTOM")]
297 Custom,
299}
300impl Default for TCAttachPoint {
301 fn default() -> Self {
302 Self::Ingress
303 }
304}
305
306impl TCAttachPoint {
307 pub fn to_value(&self) -> u32 {
309 match self {
310 TCAttachPoint::Ingress => BPF_TC_INGRESS,
311 TCAttachPoint::Egress => BPF_TC_EGRESS,
312 TCAttachPoint::Custom => BPF_TC_CUSTOM,
313 }
314 }
315}
316
317#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
318pub struct TCOpts {
320 #[serde(default = "default_helpers::default_u32::<1>")]
321 pub handle: u32,
323 #[serde(default = "default_helpers::default_u32::<1>")]
324 pub priority: u32,
326}
327impl Default for TCOpts {
328 fn default() -> Self {
329 Self {
330 handle: 1,
331 priority: 1,
332 }
333 }
334}
335
336#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Default)]
337pub struct VariableCommandArgument {
339 #[serde(default)]
340 pub default: Option<Value>,
342 pub long: Option<String>,
344 pub short: Option<String>,
346 pub help: Option<String>,
348}
349
350#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
351pub struct DataSectionVariableMeta {
353 pub name: String,
355 #[serde(rename = "type")]
356 pub ty: String,
358 #[serde(skip_serializing_if = "Option::is_none")]
359 pub value: Option<Value>,
361 #[serde(skip_serializing_if = "Option::is_none")]
362 pub description: Option<String>,
364 #[serde(default)]
365 pub cmdarg: VariableCommandArgument,
367 #[serde(flatten)]
368 pub others: Value,
370}
371#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
372pub struct DataSectionMeta {
374 pub name: String,
376 pub variables: Vec<DataSectionVariableMeta>,
378}
379#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Default)]
380pub struct BpfSkelDoc {
384 pub version: Option<String>,
386 pub brief: Option<String>,
388 pub details: Option<String>,
390 pub description: Option<String>,
392}
393#[serde_with::serde_as]
394#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
395pub struct BpfSkeletonMeta {
397 pub data_sections: Vec<DataSectionMeta>,
399 pub maps: Vec<MapMeta>,
401 #[serde_as(deserialize_as = "DefaultOnNull")]
402 pub progs: Vec<ProgMeta>,
404 pub obj_name: String,
406 #[serde(skip_serializing_if = "Option::is_none")]
407 pub doc: Option<BpfSkelDoc>,
409}
410impl BpfSkeletonMeta {
411 pub fn find_map_by_ident(&self, ident: impl AsRef<str>) -> Option<&MapMeta> {
413 let str_ref = ident.as_ref();
414 self.maps.iter().find(|s| s.ident == str_ref)
415 }
416}
417
418#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)]
420pub struct EunomiaObjectMeta {
421 #[serde(default)]
423 pub export_types: Vec<ExportedTypesStructMeta>,
424 pub bpf_skel: BpfSkeletonMeta,
426
427 #[serde(default = "default_helpers::default_usize::<64>")]
428 pub perf_buffer_pages: usize,
430 #[serde(default = "default_helpers::default_usize::<10>")]
431 pub perf_buffer_time_ms: usize,
433 #[serde(default = "default_helpers::default_i32::<100>")]
434 pub poll_timeout_ms: i32,
436 #[serde(default = "default_helpers::default_bool::<false>")]
437 pub debug_verbose: bool,
440 #[serde(default = "default_helpers::default_bool::<false>")]
441 pub print_header: bool,
444 #[serde(default = "default_helpers::default_bool::<false>")]
449 pub enable_multiple_export_types: bool,
450}
451#[derive(Deserialize, Serialize, PartialEq, Eq)]
452pub(crate) struct ComposedObjectInner {
453 pub(crate) bpf_object: String,
454 pub(crate) bpf_object_size: usize,
455 pub(crate) meta: EunomiaObjectMeta,
456}
457
458#[derive(Clone, Debug, PartialEq, Eq)]
459pub struct ComposedObject {
469 pub bpf_object: Vec<u8>,
471 pub meta: EunomiaObjectMeta,
473}
474
475impl Serialize for ComposedObject {
476 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
477 where
478 S: serde::Serializer,
479 {
480 use serde::ser::Error;
481 let bpf_object_size = self.bpf_object.len();
482 let compressed = deflate_bytes_zlib(&self.bpf_object);
483 let bpf_object_base64 = base64::engine::general_purpose::STANDARD.encode(compressed);
484
485 let json_val = serde_json::to_value(ComposedObjectInner {
486 bpf_object: bpf_object_base64,
487 bpf_object_size,
488 meta: self.meta.clone(),
489 })
490 .map_err(|e| Error::custom(format!("Failed to serialize: {e}")))?;
491 json_val.serialize(serializer)
492 }
493}
494impl<'de> Deserialize<'de> for ComposedObject {
495 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
496 where
497 D: serde::Deserializer<'de>,
498 {
499 use serde::de::Error;
500 let json_val: ComposedObjectInner =
501 serde_json::from_value(Value::deserialize(deserializer)?)
502 .map_err(|e| Error::custom(format!("Malformed json provided: {e}")))?;
503 let base64_decoded = base64::engine::general_purpose::STANDARD
504 .decode(&json_val.bpf_object)
505 .map_err(|e| Error::custom(format!("Malformed base64: {e}")))?;
506 let uncompressed = inflate::inflate_bytes_zlib(&base64_decoded)
507 .map_err(|e| Error::custom(format!("Malformed compressed data: {e}")))?;
508 if uncompressed.len() != json_val.bpf_object_size {
509 return Err(Error::custom(format!(
510 "Unmatched size: {} in the json, but {} in the decompressed file",
511 json_val.bpf_object_size,
512 uncompressed.len()
513 )));
514 }
515 Ok(Self {
516 bpf_object: uncompressed,
517 meta: json_val.meta,
518 })
519 }
520}
521
522#[derive(Deserialize, Serialize, Debug, Clone, Default)]
525pub struct RunnerConfig {
526 #[serde(default = "default_helpers::default_bool::<false>")]
529 pub print_kernel_debug: bool,
530}
531
532pub(crate) mod default_helpers {
533 pub(crate) fn default_bool<const V: bool>() -> bool {
534 V
535 }
536 pub(crate) fn default_usize<const V: usize>() -> usize {
537 V
538 }
539 pub(crate) fn default_i32<const V: i32>() -> i32 {
540 V
541 }
542 pub(crate) fn default_u32<const V: u32>() -> u32 {
543 V
544 }
545
546 pub(crate) fn map_unit_default() -> String {
547 "(unit)".into()
548 }
549}
550
551pub mod arg_builder;
553pub mod arg_parser;
555#[cfg(test)]
556mod tests;