Repository: libbpf/libbpf-rs (language: rust)

Issue:
Skeleton generation silently fails when using a typedefed type as a libbpf global
The following minimal example will cause skeleton generation to silently fail, producing an empty `mod.rs` file:
```c
#include "vmlinux.h"

#include <bpf/bpf_core_read.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>

typedef enum {
    TEST1 = 0x1,
    TEST2 = 0x2,
    TEST3 = 0x4,
} test_t;

const volatile test_t test;

SEC("tp_btf/execve")
int BPF_PROG(trace_execve, char *filename, pid_t pid, pid_t old_pid)
{
    return 0;
}
```
I'm using the latest `libbpf-rs` and `libbpf-cargo` from the master tree.

The following diff currently works as a workaround, but it would be nice if we could support using typedefed types directly. Is this technically feasible? At the very least this should be a hard error during skeleton generation if we cannot support it.
```diff
*** enum.bpf.c  2021-05-18 10:06:13.760039580 -0400
--- enum.bpf.working.c  2021-05-18 10:06:05.433372871 -0400
***************
*** 4,16 ****
  #include <bpf/bpf_helpers.h>
  #include <bpf/bpf_tracing.h>

! typedef enum {
      TEST1 = 0x1,
      TEST2 = 0x2,
      TEST3 = 0x4,
  } test_t;

! const volatile test_t test;

  SEC("tp_btf/execve")
  int BPF_PROG(trace_execve, char *filename, pid_t pid, pid_t old_pid)
--- 4,16 ----
  #include <bpf/bpf_helpers.h>
  #include <bpf/bpf_tracing.h>

! typedef enum test {
      TEST1 = 0x1,
      TEST2 = 0x2,
      TEST3 = 0x4,
  } test_t;

! const volatile enum test test;

  SEC("tp_btf/execve")
  int BPF_PROG(trace_execve, char *filename, pid_t pid, pid_t old_pid)
```


Discussion / hints:
I see what's going on. Skeleton gen code is not properly checking for rustfmt errors. And rustfmt is failing b/c enum does not have a name.

I'll put up a fix

Fix patch:
diff --git a/libbpf-cargo/src/btf/btf.rs b/libbpf-cargo/src/btf/btf.rs
index a16dda52..09ede567 100644
--- a/libbpf-cargo/src/btf/btf.rs
+++ b/libbpf-cargo/src/btf/btf.rs
@@ -12,14 +12,14 @@ use scroll::Pread;
 use crate::btf::c_types::*;
 use crate::btf::*;
 
-const ANON_AGGREGATE_PREFIX: &str = "__anon_";
+const ANON_PREFIX: &str = "__anon_";
 
 pub struct Btf<'a> {
     types: Vec<BtfType<'a>>,
     ptr_size: u32,
     string_table: &'a [u8],
     bpf_obj: *mut libbpf_sys::bpf_object,
-    anon_agg_count: u32,
+    anon_count: u32,
 }
 
 impl<'a> Btf<'a> {
@@ -97,7 +97,7 @@ impl<'a> Btf<'a> {
             ptr_size: ptr_size as u32,
             string_table: str_data,
             bpf_obj,
-            anon_agg_count: 0u32,
+            anon_count: 0u32,
         };
 
         // Load all types
@@ -586,8 +586,8 @@ impl<'a> Btf<'a> {
     fn load_struct(&mut self, t: &btf_type, extra: &'a [u8]) -> Result<BtfType<'a>> {
         let name = match self.get_btf_str(t.name_off as usize)? {
             "" => {
-                self.anon_agg_count += 1;
-                format!("{}{}", ANON_AGGREGATE_PREFIX, self.anon_agg_count)
+                self.anon_count += 1;
+                format!("{}{}", ANON_PREFIX, self.anon_count)
             }
             n => n.to_string(),
         };
@@ -602,8 +602,8 @@ impl<'a> Btf<'a> {
     fn load_union(&mut self, t: &btf_type, extra: &'a [u8]) -> Result<BtfType<'a>> {
         let name = match self.get_btf_str(t.name_off as usize)? {
             "" => {
-                self.anon_agg_count += 1;
-                format!("{}{}", ANON_AGGREGATE_PREFIX, self.anon_agg_count)
+                self.anon_count += 1;
+                format!("{}{}", ANON_PREFIX, self.anon_count)
             }
             n => n.to_string(),
         };
@@ -635,10 +635,17 @@ impl<'a> Btf<'a> {
         Ok(res)
     }
 
-    fn load_enum(&self, t: &btf_type, extra: &'a [u8]) -> Result<BtfType<'a>> {
+    fn load_enum(&mut self, t: &btf_type, extra: &'a [u8]) -> Result<BtfType<'a>> {
+        let name = match self.get_btf_str(t.name_off as usize)? {
+            "" => {
+                self.anon_count += 1;
+                format!("{}{}", ANON_PREFIX, self.anon_count)
+            }
+            n => n.to_string(),
+        };
+
         let mut vals = Vec::new();
         let mut off: usize = 0;
-
         for _ in 0..Btf::get_vlen(t.info) {
             let v = extra.pread::<btf_enum>(off)?;
             vals.push(BtfEnumValue {
@@ -650,7 +657,7 @@ impl<'a> Btf<'a> {
         }
 
         Ok(BtfType::Enum(BtfEnum {
-            name: self.get_btf_str(t.name_off as usize)?,
+            name,
             size: t.type_id,
             values: vals,
         }))
diff --git a/libbpf-cargo/src/btf/types.rs b/libbpf-cargo/src/btf/types.rs
index ef728b59..374e4a78 100644
--- a/libbpf-cargo/src/btf/types.rs
+++ b/libbpf-cargo/src/btf/types.rs
@@ -76,7 +76,7 @@ pub struct BtfEnumValue<'a> {
 
 #[derive(Debug)]
 pub struct BtfEnum<'a> {
-    pub name: &'a str,
+    pub name: String,
     pub size: u32,
     pub values: Vec<BtfEnumValue<'a>>,
 }
diff --git a/libbpf-cargo/src/gen.rs b/libbpf-cargo/src/gen.rs
index 457ad870..67f6c706 100644
--- a/libbpf-cargo/src/gen.rs
+++ b/libbpf-cargo/src/gen.rs
@@ -8,7 +8,7 @@ use std::path::{Path, PathBuf};
 use std::process::{Command, Stdio};
 use std::ptr;
 
-use anyhow::{bail, Context, Result};
+use anyhow::{bail, ensure, Context, Result};
 use memmap::Mmap;
 
 use crate::btf;
@@ -75,10 +75,19 @@ fn rustfmt(s: &str, rustfmt_path: Option<&PathBuf>) -> Result<String> {
     .stdout(Stdio::piped())
     .spawn()
     .context("Failed to spawn rustfmt")?;
+
+    // Send input in via stdin
     write!(cmd.stdin.take().unwrap(), "{}", s)?;
+
+    // Extract output
     let output = cmd
         .wait_with_output()
         .context("Failed to execute rustfmt")?;
+    ensure!(
+        output.status.success(),
+        "Failed to rustfmt: {}",
+        output.status
+    );
 
     Ok(String::from_utf8(output.stdout)?)
 }


Test patch:
diff --git a/libbpf-cargo/src/test.rs b/libbpf-cargo/src/test.rs
index 161f2010..9eea79d9 100644
--- a/libbpf-cargo/src/test.rs
+++ b/libbpf-cargo/src/test.rs
@@ -1898,3 +1898,87 @@ pub union __anon_4 {
             .expect("Failed to generate struct Foo defn")
     );
 }
+
+#[test]
+fn test_btf_dump_definition_anon_enum() {
+    let (_dir, proj_dir, cargo_toml) = setup_temp_project();
+
+    // Add prog dir
+    create_dir(proj_dir.join("src/bpf")).expect("failed to create prog dir");
+
+    // Add a prog
+    let mut prog = OpenOptions::new()
+        .write(true)
+        .create(true)
+        .open(proj_dir.join("src/bpf/prog.bpf.c"))
+        .expect("failed to open prog.bpf.c");
+
+    write!(
+        prog,
+        r#"
+        #include "vmlinux.h"
+        #include "bpf_helpers.h"
+
+        typedef enum {{
+            FOO = 1,
+        }} test_t;
+
+        struct Foo {{
+            test_t test;
+        }};
+
+        struct Foo foo;
+        "#,
+    )
+    .expect("failed to write prog.bpf.c");
+
+    // Lay down the necessary header files
+    add_bpf_headers(&proj_dir);
+
+    // Build the .bpf.o
+    build(true, Some(&cargo_toml), None, true).unwrap();
+
+    let obj = OpenOptions::new()
+        .read(true)
+        .open(proj_dir.as_path().join("target/bpf/prog.bpf.o").as_path())
+        .expect("failed to open object file");
+    let mmap = unsafe { Mmap::map(&obj) }.expect("Failed to mmap object file");
+    let btf = Btf::new("prog", &*mmap)
+        .expect("Failed to initialize Btf")
+        .expect("Did not find .BTF section");
+
+    assert!(btf.types().len() > 0);
+
+    // Find our struct
+    let mut struct_foo: Option<u32> = None;
+    for (idx, ty) in btf.types().iter().enumerate() {
+        match ty {
+            btf::BtfType::Struct(t) => {
+                if t.name == "Foo" {
+                    assert!(struct_foo.is_none()); // No duplicates
+                    struct_foo = Some(idx.try_into().unwrap());
+                }
+            }
+            _ => (),
+        }
+    }
+
+    assert!(struct_foo.is_some());
+
+    let foo_defn = r#"#[derive(Debug, Default, Copy, Clone)]
+#[repr(C)]
+pub struct Foo {
+    pub test: __anon_1,
+}
+#[derive(Debug, Copy, Clone, PartialEq)]
+#[repr(u32)]
+pub enum __anon_1 {
+    FOO = 1,
+}
+"#;
+    assert_eq!(
+        foo_defn,
+        btf.type_definition(struct_foo.unwrap())
+            .expect("Failed to generate union Foo defn")
+    );
+}