use std::thread;
use crate::protocol::{
Context, DebugImage, DeviceContext, Map, OsContext, RuntimeContext, Stacktrace, Thread,
};
#[cfg(all(feature = "with_device_info", target_os = "macos"))]
mod model_support {
use libc;
use libc::c_void;
use regex::Regex;
use std::ptr;
lazy_static::lazy_static! {
static ref FAMILY_RE: Regex = Regex::new(r#"([a-zA-Z]+)\d"#).unwrap();
}
pub fn get_model() -> Option<String> {
unsafe {
let mut size = 0;
libc::sysctlbyname(
"hw.model\x00".as_ptr() as *const i8,
ptr::null_mut(),
&mut size,
ptr::null_mut(),
0,
);
let mut buf = vec![0u8; size as usize];
libc::sysctlbyname(
"hw.model\x00".as_ptr() as *const i8,
buf.as_mut_ptr() as *mut c_void,
&mut size,
ptr::null_mut(),
0,
);
Some(
String::from_utf8_lossy(if buf.ends_with(b"\x00") {
&buf[..size - 1]
} else {
&buf
})
.to_string(),
)
}
}
pub fn get_family() -> Option<String> {
get_model()
.as_ref()
.and_then(|model| FAMILY_RE.captures(model))
.and_then(|m| m.get(1))
.map(|group| group.as_str().to_string())
}
}
#[cfg(any(not(target_os = "macos"), not(feature = "with_device_info")))]
mod model_support {
pub fn get_model() -> Option<String> {
None
}
pub fn get_family() -> Option<String> {
None
}
}
#[cfg(feature = "with_debug_meta")]
mod findshlibs_support {
use super::*;
use std::env;
use std::ffi::CStr;
use findshlibs::{
Segment, SharedLibrary, SharedLibraryId, TargetSharedLibrary, TARGET_SUPPORTED,
};
use crate::internals::Uuid;
use crate::protocol::debugid::DebugId;
use crate::protocol::SymbolicDebugImage;
#[cfg(unix)]
pub fn find_build_id_from_binary(name: &CStr) -> Option<DebugId> {
use std::ffi::OsStr;
use std::fs::File;
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use goblin::elf::note::NT_GNU_BUILD_ID;
use goblin::elf::Elf;
use memmap::Mmap;
fn from_be(id: Uuid) -> Uuid {
let (a, b, c, d) = id.as_fields();
Uuid::from_fields(u32::from_be(a), u16::from_be(b), u16::from_be(c), d).unwrap()
}
let os_str = OsStr::from_bytes(name.to_bytes());
let path: &Path = if os_str.is_empty() {
"/proc/self/exe".as_ref()
} else {
os_str.as_ref()
};
let file = File::open(&path).ok()?;
let mmap = unsafe { Mmap::map(&file) }.ok()?;
if let Ok(elf_obj) = Elf::parse(&mmap) {
if let Some(note) = elf_obj
.iter_note_headers(&mmap)?
.filter_map(|note_result| note_result.ok())
.find(|note| note.n_type == NT_GNU_BUILD_ID && note.desc.len() >= 16)
{
let build_id = from_be(Uuid::from_slice(¬e.desc[0..16]).unwrap());
return Some(DebugId::from_uuid(build_id));
}
}
None
}
#[cfg(not(unix))]
pub fn find_build_id_from_binary(_name: &CStr) -> Option<DebugId> {
None
}
pub fn find_shlibs() -> Option<Vec<DebugImage>> {
if !TARGET_SUPPORTED {
return None;
}
let mut rv = vec![];
TargetSharedLibrary::each(|shlib| {
let maybe_debug_id = shlib
.id()
.map(|SharedLibraryId::Uuid(bytes)| DebugId::from_uuid(Uuid::from_bytes(bytes)))
.or_else(|| find_build_id_from_binary(shlib.name()));
let debug_id = match maybe_debug_id {
Some(debug_id) => debug_id,
None => return,
};
let mut lowest_addr = !0;
let mut lowest_vmaddr = !0;
let mut highest_addr = 0;
for seg in shlib.segments() {
let svma: u64 = seg.stated_virtual_memory_address().0 as u64;
let avma: u64 = seg.actual_virtual_memory_address(shlib).0 as u64;
if lowest_addr > avma {
lowest_addr = avma;
}
if highest_addr < avma {
highest_addr = avma;
}
if lowest_vmaddr > svma {
lowest_vmaddr = svma;
}
}
let mut name = shlib.name().to_string_lossy().to_string();
if name == "" {
name = env::current_exe()
.map(|x| x.display().to_string())
.unwrap_or_else(|_| "<main>".to_string());
}
rv.push(
SymbolicDebugImage {
name,
arch: None,
image_addr: lowest_addr.into(),
image_size: highest_addr - lowest_addr,
image_vmaddr: lowest_vmaddr.into(),
id: debug_id,
}
.into(),
);
});
Some(rv)
}
}
#[cfg(not(feature = "with_debug_meta"))]
mod findshlibs_support {
use super::*;
pub fn find_shlibs() -> Option<Vec<DebugImage>> {
None
}
}
pub fn device_model() -> Option<String> {
model_support::get_model()
}
pub fn device_family() -> Option<String> {
model_support::get_family()
}
pub fn cpu_arch() -> Option<String> {
use crate::constants::ARCH;
Some(ARCH.into())
}
pub fn server_name() -> Option<String> {
#[cfg(feature = "with_device_info")]
{
use hostname::get_hostname;
get_hostname()
}
#[cfg(not(feature = "with_device_info"))]
{
None
}
}
pub fn os_context() -> Option<Context> {
#[cfg(all(feature = "with_device_info", not(windows)))]
{
use uname::uname;
if let Ok(info) = uname() {
Some(
OsContext {
name: Some(info.sysname),
kernel_version: Some(info.version),
version: Some(info.release),
..Default::default()
}
.into(),
)
} else {
None
}
}
#[cfg(all(feature = "with_device_info", windows))]
{
use crate::constants::PLATFORM;
Some(
OsContext {
name: Some(PLATFORM.into()),
..Default::default()
}
.into(),
)
}
#[cfg(not(feature = "with_device_info"))]
{
None
}
}
pub fn rust_context() -> Option<Context> {
#[cfg(feature = "with_device_info")]
{
use crate::constants::{RUSTC_CHANNEL, RUSTC_VERSION};
let ctx = RuntimeContext {
name: Some("rustc".into()),
version: RUSTC_VERSION.map(|x| x.into()),
other: {
let mut map = Map::default();
if let Some(channel) = RUSTC_CHANNEL {
map.insert("channel".to_string(), channel.into());
}
map
},
}
.into();
Some(ctx)
}
#[cfg(not(feature = "with_device_info"))]
{
None
}
}
pub fn device_context() -> Option<Context> {
#[cfg(feature = "with_device_info")]
{
let model = device_model();
let family = device_family();
let arch = cpu_arch();
Some(
DeviceContext {
model,
family,
arch,
..Default::default()
}
.into(),
)
}
#[cfg(not(feature = "with_device_info"))]
{
None
}
}
pub fn debug_images() -> Vec<DebugImage> {
findshlibs_support::find_shlibs().unwrap_or_else(Vec::new)
}
pub fn current_thread(with_stack: bool) -> Thread {
let thread_id: u64 = unsafe { std::mem::transmute(thread::current().id()) };
Thread {
id: Some(thread_id.to_string().into()),
name: thread::current().name().map(|x| x.to_string()),
current: true,
stacktrace: if with_stack {
current_stacktrace()
} else {
None
},
..Default::default()
}
}
pub fn current_stacktrace() -> Option<Stacktrace> {
#[cfg(feature = "with_backtrace")]
{
use crate::backtrace_support::current_stacktrace;
current_stacktrace()
}
#[cfg(not(feature = "with_backtrace"))]
{
None
}
}