use std::cmp::min;
use std::fmt::Debug;
use std::fmt::Formatter;
use std::fmt::Result as FmtResult;
use std::path::PathBuf;
use crate::MaybeDefault;
use crate::Pid;
#[cfg(doc)]
use super::Symbolizer;
cfg_apk! {
#[derive(Clone)]
pub struct Apk {
pub path: PathBuf,
pub debug_syms: bool,
#[doc(hidden)]
pub _non_exhaustive: (),
}
impl Apk {
#[inline]
pub fn new(path: impl Into<PathBuf>) -> Self {
Self {
path: path.into(),
debug_syms: true,
_non_exhaustive: (),
}
}
}
impl From<Apk> for Source<'static> {
#[inline]
fn from(apk: Apk) -> Self {
Self::Apk(apk)
}
}
impl Debug for Apk {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
let Self {
path,
debug_syms: _,
_non_exhaustive: (),
} = self;
f.debug_tuple(stringify!(Apk)).field(path).finish()
}
}
}
cfg_breakpad! {
#[derive(Clone)]
pub struct Breakpad {
pub path: PathBuf,
#[doc(hidden)]
pub _non_exhaustive: (),
}
impl Breakpad {
#[inline]
pub fn new(path: impl Into<PathBuf>) -> Self {
Self {
path: path.into(),
_non_exhaustive: (),
}
}
}
impl From<Breakpad> for Source<'static> {
#[inline]
fn from(breakpad: Breakpad) -> Self {
Self::Breakpad(breakpad)
}
}
impl Debug for Breakpad {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
let Self {
path,
_non_exhaustive: (),
} = self;
f.debug_tuple(stringify!(Breakpad)).field(path).finish()
}
}
}
#[derive(Clone)]
pub struct Elf {
pub path: PathBuf,
pub debug_syms: bool,
#[doc(hidden)]
pub _non_exhaustive: (),
}
impl Elf {
#[inline]
pub fn new(path: impl Into<PathBuf>) -> Self {
Self {
path: path.into(),
debug_syms: true,
_non_exhaustive: (),
}
}
}
impl From<Elf> for Source<'static> {
#[inline]
fn from(elf: Elf) -> Self {
Self::Elf(elf)
}
}
impl Debug for Elf {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
let Self {
path,
debug_syms: _,
_non_exhaustive: (),
} = self;
f.debug_tuple(stringify!(Elf)).field(path).finish()
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Kernel {
pub kallsyms: MaybeDefault<PathBuf>,
pub vmlinux: MaybeDefault<PathBuf>,
pub kaslr_offset: Option<u64>,
pub debug_syms: bool,
#[doc(hidden)]
pub _non_exhaustive: (),
}
impl Default for Kernel {
fn default() -> Self {
Self {
kallsyms: MaybeDefault::Default,
vmlinux: MaybeDefault::Default,
kaslr_offset: None,
debug_syms: true,
_non_exhaustive: (),
}
}
}
impl From<Kernel> for Source<'static> {
#[inline]
fn from(kernel: Kernel) -> Self {
Self::Kernel(kernel)
}
}
#[derive(Clone)]
pub struct Process {
pub pid: Pid,
pub debug_syms: bool,
pub perf_map: bool,
pub map_files: bool,
pub vdso: bool,
#[doc(hidden)]
pub _non_exhaustive: (),
}
impl Process {
#[inline]
pub fn new(pid: Pid) -> Self {
Self {
pid,
debug_syms: true,
perf_map: true,
map_files: true,
vdso: true,
_non_exhaustive: (),
}
}
}
impl Debug for Process {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
let Self {
pid,
debug_syms: _,
perf_map: _,
map_files: _,
vdso: _,
_non_exhaustive: (),
} = self;
f.debug_tuple(stringify!(Process))
.field(&format_args!("{pid}"))
.finish()
}
}
impl From<Process> for Source<'static> {
#[inline]
fn from(process: Process) -> Self {
Self::Process(process)
}
}
cfg_gsym! {
#[derive(Clone)]
pub enum Gsym<'dat> {
Data(GsymData<'dat>),
File(GsymFile),
}
impl Debug for Gsym<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
match self {
Self::Data(data) => Debug::fmt(data, f),
Self::File(file) => Debug::fmt(file, f),
}
}
}
impl<'dat> From<Gsym<'dat>> for Source<'dat> {
#[inline]
fn from(gsym: Gsym<'dat>) -> Self {
Self::Gsym(gsym)
}
}
#[derive(Clone)]
pub struct GsymData<'dat> {
pub data: &'dat [u8],
#[doc(hidden)]
pub _non_exhaustive: (),
}
impl<'dat> GsymData<'dat> {
#[inline]
pub fn new(data: &'dat [u8]) -> Self {
Self {
data,
_non_exhaustive: (),
}
}
}
impl Debug for GsymData<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
let Self {
data,
_non_exhaustive: (),
} = self;
f.debug_tuple(stringify!(GsymData))
.field(&data.get(0..(min(data.len(), 32))).unwrap_or_default())
.finish()
}
}
impl<'dat> From<GsymData<'dat>> for Source<'dat> {
#[inline]
fn from(gsym: GsymData<'dat>) -> Self {
Self::Gsym(Gsym::Data(gsym))
}
}
#[derive(Clone)]
pub struct GsymFile {
pub path: PathBuf,
#[doc(hidden)]
pub _non_exhaustive: (),
}
impl GsymFile {
#[inline]
pub fn new(path: impl Into<PathBuf>) -> Self {
Self {
path: path.into(),
_non_exhaustive: (),
}
}
}
impl Debug for GsymFile {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
let Self {
path,
_non_exhaustive: (),
} = self;
f.debug_tuple(stringify!(GsymFile)).field(path).finish()
}
}
impl From<GsymFile> for Source<'static> {
#[inline]
fn from(gsym: GsymFile) -> Self {
Self::Gsym(Gsym::File(gsym))
}
}
}
#[derive(Clone)]
#[non_exhaustive]
pub enum Source<'dat> {
#[cfg(feature = "apk")]
#[cfg_attr(docsrs, doc(cfg(feature = "apk")))]
Apk(Apk),
#[cfg(feature = "breakpad")]
#[cfg_attr(docsrs, doc(cfg(feature = "breakpad")))]
Breakpad(Breakpad),
Elf(Elf),
Kernel(Kernel),
Process(Process),
#[cfg(feature = "gsym")]
#[cfg_attr(docsrs, doc(cfg(feature = "gsym")))]
Gsym(Gsym<'dat>),
#[doc(hidden)]
Phantom(&'dat ()),
}
impl Debug for Source<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
match self {
#[cfg(feature = "apk")]
Self::Apk(apk) => Debug::fmt(apk, f),
#[cfg(feature = "breakpad")]
Self::Breakpad(breakpad) => Debug::fmt(breakpad, f),
Self::Elf(elf) => Debug::fmt(elf, f),
Self::Kernel(kernel) => Debug::fmt(kernel, f),
Self::Process(process) => Debug::fmt(process, f),
#[cfg(feature = "gsym")]
Self::Gsym(gsym) => Debug::fmt(gsym, f),
Self::Phantom(()) => unreachable!(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn debug_repr() {
let apk = Apk::new("/a-path/with/components.apk");
assert_eq!(format!("{apk:?}"), "Apk(\"/a-path/with/components.apk\")");
let src = Source::from(apk);
assert_eq!(format!("{src:?}"), "Apk(\"/a-path/with/components.apk\")");
let breakpad = Breakpad::new("/a-path/with/components.sym");
assert_eq!(
format!("{breakpad:?}"),
"Breakpad(\"/a-path/with/components.sym\")"
);
let src = Source::from(breakpad);
assert_eq!(
format!("{src:?}"),
"Breakpad(\"/a-path/with/components.sym\")"
);
let elf = Elf::new("/a-path/with/components.elf");
assert_eq!(format!("{elf:?}"), "Elf(\"/a-path/with/components.elf\")");
let src = Source::from(elf);
assert_eq!(format!("{src:?}"), "Elf(\"/a-path/with/components.elf\")");
let gsym_data = GsymData::new(b"12345");
assert_eq!(format!("{gsym_data:?}"), "GsymData([49, 50, 51, 52, 53])");
let gsym = Gsym::Data(gsym_data.clone());
assert_eq!(format!("{gsym:?}"), "GsymData([49, 50, 51, 52, 53])");
let gsym_file = GsymFile::new("/a-path/gsym");
assert_eq!(format!("{gsym_file:?}"), "GsymFile(\"/a-path/gsym\")");
let gsym = Gsym::File(gsym_file);
assert_eq!(format!("{gsym:?}"), "GsymFile(\"/a-path/gsym\")");
let src = Source::from(gsym);
assert_eq!(format!("{src:?}"), "GsymFile(\"/a-path/gsym\")");
let src = Source::from(Gsym::Data(gsym_data));
assert_eq!(format!("{src:?}"), "GsymData([49, 50, 51, 52, 53])");
let kernel = Kernel::default();
assert_ne!(format!("{kernel:?}"), "");
let src = Source::from(kernel);
assert_ne!(format!("{src:?}"), "");
let process = Process::new(Pid::Slf);
assert_eq!(format!("{process:?}"), "Process(self)");
let process = Process::new(Pid::from(1234));
assert_eq!(format!("{process:?}"), "Process(1234)");
let src = Source::from(process);
assert_eq!(format!("{src:?}"), "Process(1234)");
}
}