Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add initial MacOS implementation #21

Merged
merged 53 commits into from
May 23, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
b4c50cb
Move memory write to be a shared module
Jake-Shadle Apr 8, 2022
d777ecc
Begin fleshing out MacOS implementation
Jake-Shadle Apr 12, 2022
977ebb2
Further fleshing out
Jake-Shadle Apr 12, 2022
cbf84f3
Add remaining streams
Jake-Shadle Apr 14, 2022
b3c8c10
Begin refactor
Jake-Shadle Apr 14, 2022
b439d66
Update branch name for path
Jake-Shadle Apr 14, 2022
9c0fe44
Make DirSection shared
Jake-Shadle Apr 14, 2022
c73f1f4
Finish up refactoring and actually get it compiling
Jake-Shadle Apr 14, 2022
cc84ce9
Add macos dump test
Jake-Shadle Apr 20, 2022
22b5d7f
Build mac in CI
Jake-Shadle Apr 20, 2022
a721515
Fixup x86_64
Jake-Shadle Apr 20, 2022
c166609
Remove unused import
Jake-Shadle Apr 20, 2022
3825266
Verify we get valid misc info
Jake-Shadle Apr 20, 2022
717312b
Add debug prints
Jake-Shadle Apr 20, 2022
396b2ac
Actually these were what I wanted
Jake-Shadle Apr 20, 2022
915caa3
Use TASK_BASIC_INFO_64 instead
Jake-Shadle Apr 20, 2022
22a5aad
Add busy loop so we don't have 0 seconds of user time
Jake-Shadle Apr 20, 2022
589bf61
uhm
Jake-Shadle Apr 20, 2022
05b10df
Check both
Jake-Shadle Apr 20, 2022
13edc95
Properly pack structs
Jake-Shadle Apr 20, 2022
aa7bcc4
oops
Jake-Shadle Apr 20, 2022
a2010cd
Use a thread to force timings?
Jake-Shadle Apr 20, 2022
97ad56c
Try harder to use user cpu time
Jake-Shadle Apr 20, 2022
3c36479
Just give up on process times for now
Jake-Shadle Apr 20, 2022
1695497
Cleanup/docs pass
Jake-Shadle Apr 21, 2022
3e473b0
Add task_dumper test for load command iteration
Jake-Shadle Apr 21, 2022
7dbd4c5
Verify test works on macos
Jake-Shadle Apr 21, 2022
5852ffa
Remove unneeded crash-context patch
Jake-Shadle Apr 21, 2022
1360967
oops, remove dbg! code
Jake-Shadle Apr 21, 2022
0f361da
Oops, we were dropping a register
Jake-Shadle Apr 25, 2022
7d4aecf
Use correct arm64 processor arch
Jake-Shadle Apr 25, 2022
ff3c98b
Remove superfluous 'what' comment
Jake-Shadle Apr 26, 2022
4342fde
Hopefully fix slide calculation
Jake-Shadle Apr 26, 2022
b57038a
Checkpoint of fail
Jake-Shadle Apr 26, 2022
f44a51c
Fix formatting
Jake-Shadle Apr 28, 2022
4ec6a9a
Using as a path dep for now
Jake-Shadle Apr 28, 2022
4af3274
Use crash-context to properly send process info
Jake-Shadle Apr 28, 2022
73c53c8
Remove minidump-processor temporarily due to openssl
Jake-Shadle Apr 28, 2022
ccada3b
Fix windows
Jake-Shadle Apr 28, 2022
fe4e0d9
Fix it better
Jake-Shadle Apr 28, 2022
b67946e
oops
Jake-Shadle Apr 28, 2022
a8e0783
Get rid of needless functions now that pid is part of context
Jake-Shadle Apr 29, 2022
b18c572
Shame
Jake-Shadle Apr 29, 2022
a563388
Fix windows test compilation
Jake-Shadle Apr 29, 2022
8fa66d8
Gracefully handle invalid addresses for thread stacks
Jake-Shadle Apr 29, 2022
9189c8d
Remove crash-context patch
Jake-Shadle Apr 29, 2022
46e3999
Add CHANGELOG.md
Jake-Shadle Apr 29, 2022
2082ff7
Reenable test with patches
Jake-Shadle May 2, 2022
41c1768
Fix build
Jake-Shadle May 2, 2022
0b62a95
Fix it for real this time
Jake-Shadle May 2, 2022
699f248
Replace cargo-audit with cargo-deny
Jake-Shadle May 2, 2022
1018093
Remove minidump* patches now that 0.11 was released
Jake-Shadle May 19, 2022
4e44566
Ensure aarch64 compiles as well
Jake-Shadle May 20, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Verify we get valid misc info
  • Loading branch information
Jake-Shadle committed Apr 27, 2022
commit 382526624623d0f72045c754fec836d605dc9212
140 changes: 7 additions & 133 deletions src/mac/streams/misc_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,109 +49,6 @@ impl mach::TaskInfo for TaskThreadsTimeInfo {
const FLAVOR: u32 = mach::task_info::TASK_THREAD_TIMES_INFO;
}

#[repr(C)]
struct VmSpace {
dummy: i32,
dummy2: *const u8,
dummy3: [i32; 5],
dummy4: [*const u8; 3],
}

#[repr(C)]
struct ExternProc {
starttime: libc::timeval, // process start time, actually a union, but that's an implementation detail
vmspace: *const VmSpace, // Address space
sigacts: *const u8, // Signal actions, state (PROC ONLY)
flag: i32, // P_* flags
stat: i8, // S* process status
pid: libc::pid_t, // pid
oppid: libc::pid_t, // save parent pid during ptrace
dupfd: i32, // sideways return value from fdopen
/* Mach related */
user_stack: *const u8, // where user stack was allocated,
exit_thread: *const c_void, // Which thread is exiting?
debugger: i32, // allow to debug
sigwait: i32, // indication to suspend
/* scheduling */
estcpu: u32, // time averaged value of cpticks
cpticks: i32, // tick of cpu time
pctcpu: u32, // %cpu for this process during swtime
wchan: *const c_void, // sleep address
wmesg: *const i8, // reason for sleep
swtime: u32, // time swapped in or out
slptime: u32, // time since last blocked
realtimer: libc::itimerval, // alarm timer
rtime: libc::timeval, // real time
uticks: u64, // statclock hits in user mode
sticks: u64, // statclock hits in system mode
iticks: u64, // statclock hits processing intr
traceflag: i32, // kernel trace points
tracep: *const c_void, // trace to vnode
siglist: i32, // DEPRECATED
textvp: *const c_void, // vnode of executable
holdcnt: i32, // if non-zero, don't swap
sigmask: libc::sigset_t, // DEPRECATED
sigignore: libc::sigset_t, // signals being ignored
sigcatch: libc::sigset_t, // signals being caught by user
priority: u8, // process priority
usrpri: u8, // user-priority based on cpu and nice
nice: i8, // process "nice" value
comm: [i8; 16 /*MAXCOMLEN*/ + 1],
pgrp: *const c_void, // pointer to process group
addr: *const c_void, // kernel virtual addr of u-area (PROC ONLY)
xstat: u16, // exit status for wait; also stop signal
acflag: u16, // accounting flags
ru: *const c_void, // exit information
}

#[repr(C)]
struct Pcred {
pc_lock: [i8; 72], // opaque content
pc_ucred: *const c_void, // current credentials
ruid: libc::uid_t, // real user id
svuid: libc::uid_t, // saved effective user id
rgid: libc::gid_t, // real group id
svgid: libc::gid_t, // saved effective group id
refcnt: i32, // number of references
}

#[repr(C)]
struct Ucred {
refcnt: i32, // reference count
uid: libc::uid_t, // effective user id
ngroups: i16, // number of groups
groups: [libc::gid_t; 16],
}

#[repr(C)]
struct EProc {
paddr: *const c_void, // address of proc
sess: *const c_void, // session pointer
pcred: Pcred, // process credentials
ucred: Ucred, // current credentials
vm: VmSpace, // address space
ppid: libc::pid_t, // parent process id
pgid: libc::gid_t, // process group id
jobc: i16, // job control counter
tdev: i32, // controlling tty dev
tpgid: libc::gid_t, // tty process group id
tsess: *const c_void, // tty session pointer
wmesg: [i8; 8], // wchan message
xsize: i32, // text size
xrssize: i16, // text rss
xccount: i16, // text references
xswrss: i16,
flag: i32,
login: [i8; 12], // short setlogin() name
spare: [i32; 4],
}

#[repr(C)]
struct KInfoProc {
kp_proc: ExternProc,
kp_eproc: EProc,
}

impl MinidumpWriter {
pub(crate) fn write_misc_info(
&mut self,
Expand Down Expand Up @@ -185,9 +82,13 @@ impl MinidumpWriter {
// Note that Breakpad is using `getrusage` to get process times, but that
// can only get resource usage for the current process and/or children,
// but since we're (most likely) running in a different process than the
// one that has crashed, we instead use the same method that Crashpad
// uses to get the information for the actual crashed process which is
// far more interesting and relevant
// one that has crashed, we instead use `proc_pidinfo` which allows us to
// to retrieve the process time of the actual crashed process. Note that
// this is _also_ different from how Crashpad retrieves the process times,
// it uses sysctl with `CTL_KERN, KERN_PROC, KERN_PROC_PID`, however
// the structs that are filled out by that for this info are not available
// in libc, and frankly `proc_pidinfo` was better documented (well, relatively,
// all Apple documentation is terrible)
//
// SAFETY: syscall
misc_info.process_create_time = unsafe {
Expand All @@ -211,33 +112,6 @@ impl MinidumpWriter {
} else {
0
}

// let mut mib = [libc::CTL_KERN, libc::KERN_PROC, libc::KERN_PROC_PID, pid];
// let mut kinfo_proc = std::mem::MaybeUninit::<KInfoProc>::zeroed();
// let mut len = std::mem::size_of::<KInfoProc>();

// if libc::sysctl(
// mib.as_mut_ptr().cast(),
// std::mem::size_of_val(&mib) as u32,
// kinfo_proc.as_mut_ptr().cast(),
// &mut len,
// ) != 0
// {
// return Err(std::io::Error::last_os_error().into());
// }

// let kinfo_proc = kinfo_proc.assume_init();

// // This sysctl does not return an error if the pid was not found. 10.9.5
// // xnu-2422.115.4/bsd/kern/kern_sysctl.c sysctl_prochandle() calls
// // xnu-2422.115.4/bsd/kern/kern_proc.c proc_iterate(), which provides no
// // indication of whether anything was done. To catch this, check that the PID
// // actually matches the one that we requested
// if kinfo_proc.kp_proc.p_pid != pid {
// 0
// } else {
// kinfo_proc.kp_proc.starttime.tv_sec as u32
// }
};

// The basic task info keeps the timings for all of the terminated threads
Expand Down
43 changes: 36 additions & 7 deletions tests/mac_minidump_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ fn get_crash_reason<'a, T: std::ops::Deref<Target = [u8]> + 'a>(
fn dump_external_process() {
use std::io::BufRead;

let approximate_proc_start_time = std::time::SystemTime::now()
.duration_since(std::time::SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs();
let mut child = start_child_and_return("");

let (task, thread) = {
Expand Down Expand Up @@ -73,13 +77,6 @@ fn dump_external_process() {

let md = Minidump::read_path(tmpfile.path()).expect("failed to read minidump");

let _: MinidumpModuleList = md.get_stream().expect("Couldn't find MinidumpModuleList");
let _: MinidumpThreadList = md.get_stream().expect("Couldn't find MinidumpThreadList");
let _: MinidumpMemoryList = md.get_stream().expect("Couldn't find MinidumpMemoryList");
let _: MinidumpSystemInfo = md.get_stream().expect("Couldn't find MinidumpSystemInfo");
let _: MinidumpBreakpadInfo = md.get_stream().expect("Couldn't find MinidumpBreakpadInfo");
let _: MinidumpMiscInfo = md.get_stream().expect("Couldn't find MinidumpMiscInfo");

let crash_reason = get_crash_reason(&md);

assert!(matches!(
Expand All @@ -89,4 +86,36 @@ fn dump_external_process() {
100
)
));

let _: MinidumpModuleList = md.get_stream().expect("Couldn't find MinidumpModuleList");
let _: MinidumpThreadList = md.get_stream().expect("Couldn't find MinidumpThreadList");
let _: MinidumpMemoryList = md.get_stream().expect("Couldn't find MinidumpMemoryList");
let _: MinidumpSystemInfo = md.get_stream().expect("Couldn't find MinidumpSystemInfo");
let _: MinidumpBreakpadInfo = md.get_stream().expect("Couldn't find MinidumpBreakpadInfo");

let misc_info: MinidumpMiscInfo = md.get_stream().expect("Couldn't find MinidumpMiscInfo");

if let minidump::RawMiscInfo::MiscInfo2(mi) = &misc_info.raw {
// Unfortunately the minidump format only has 32-bit precision for the
// process start time
let process_create_time = mi.process_create_time as u64;

assert!(
process_create_time >= approximate_proc_start_time
&& process_create_time <= approximate_proc_start_time + 2
);

assert!(mi.process_user_time > 0);
assert!(mi.process_kernel_time > 0);

// These aren't currently available on aarch64, or if they are, they
// are not via the same sysctlbyname mechanism. Would be nice if Apple
// documented...anything
if cfg!(target_arch = "x86_64") {
assert!(mi.processor_max_mhz > 0);
assert!(mi.processor_current_mhz > 0);
}
} else {
panic!("unexpected misc info type {:?}", misc_info);
}
}