btf_types.rs 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. use std::{io, path::Path, process::Command, str::from_utf8};
  2. use thiserror::Error;
  3. use crate::{
  4. bindgen,
  5. getters::{generate_getters_for_items, read_getter},
  6. rustfmt,
  7. };
  8. #[derive(Error, Debug)]
  9. pub enum Error {
  10. #[error("error executing bpftool")]
  11. BpfTool(#[source] io::Error),
  12. #[error("{stderr}\nbpftool failed with exit code {code}")]
  13. BpfToolExit { code: i32, stderr: String },
  14. #[error("bindgen failed")]
  15. Bindgen,
  16. #[error("rustfmt failed")]
  17. Rustfmt(#[source] io::Error),
  18. }
  19. pub fn generate<T: AsRef<str>>(
  20. btf_file: &Path,
  21. types: &[T],
  22. probe_read_getters: bool,
  23. ) -> Result<String, Error> {
  24. let mut bindgen = bindgen::builder();
  25. let c_header = c_header_from_btf(btf_file)?;
  26. bindgen = bindgen.header_contents("kernel_types.h", &c_header);
  27. for ty in types {
  28. bindgen = bindgen.whitelist_type(ty);
  29. }
  30. let bindings = bindgen.generate().or(Err(Error::Bindgen))?.to_string();
  31. if !probe_read_getters {
  32. return Ok(bindings);
  33. }
  34. let tree = syn::parse_str::<syn::File>(&bindings).unwrap();
  35. let bpf_probe_read = syn::parse_str::<syn::Path>("::aya_bpf::helpers::bpf_probe_read").unwrap();
  36. let getters =
  37. generate_getters_for_items(&tree.items, |getter| read_getter(getter, &bpf_probe_read));
  38. let getters =
  39. rustfmt::format(&getters.to_string()).map_err(|io_error| Error::Rustfmt(io_error))?;
  40. let bindings = format!("{}\n{}", bindings, getters);
  41. Ok(bindings)
  42. }
  43. fn c_header_from_btf(path: &Path) -> Result<String, Error> {
  44. let output = Command::new("bpftool")
  45. .args(&["btf", "dump", "file"])
  46. .arg(path)
  47. .args(&["format", "c"])
  48. .output()
  49. .map_err(|io_error| Error::BpfTool(io_error))?;
  50. if !output.status.success() {
  51. return Err(Error::BpfToolExit {
  52. code: output.status.code().unwrap(),
  53. stderr: from_utf8(&output.stderr).unwrap().to_owned(),
  54. });
  55. }
  56. Ok(from_utf8(&output.stdout).unwrap().to_owned())
  57. }