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

Does minidump-writer support dumping stack without crash? #37

Closed
simon-fu opened this issue Jun 24, 2022 · 7 comments · Fixed by #45
Closed

Does minidump-writer support dumping stack without crash? #37

simon-fu opened this issue Jun 24, 2022 · 7 comments · Fixed by #45

Comments

@simon-fu
Copy link

Sometimes there are bugs in the program that one thread may run into dead loop and burn cpu.
We can spawn watch-dog thread to monitor all thread and dump stack if dead-loop occur.
So it can be very helpful if minidump-writer supports dumping stack without crash.

@simon-fu
Copy link
Author

or in other words, how to make CrashContext without crashing for linux/macos/windows ?

@Jake-Shadle
Copy link
Collaborator

For Linux the crash context is optional, but you are correct that it should also be optional for windows and mac as well. That being said, it's also fairly trivial to generate a crash context for Mac and Windows manually.

// Unsafe function
use windows_sys::Win32::{
    System::{
        Diagnostics::Debug::{
            RtlCaptureContext, EXCEPTION_POINTERS, EXCEPTION_RECORD,
        },
        Threading::GetCurrentThreadId,
    },
};

let mut exception_record: EXCEPTION_RECORD = std::mem::zeroed();
let mut exception_context = std::mem::MaybeUninit::uninit();

// Get the CPU context
RtlCaptureContext(exception_context.as_mut_ptr());

let mut exception_context = exception_context.assume_init();

let exception_ptrs = EXCEPTION_POINTERS {
    ExceptionRecord: &mut exception_record,
    ContextRecord: &mut exception_context,
};

let exception_code = <a Windows windows_sys::Win32::Foundation::STATUS_* exception code>;
exception_record.ExceptionCode = exception_code;

let cc = crash_context::CrashContext {
    exception_pointers: (&exception_ptrs as *const EXCEPTION_POINTERS)
        .cast(),
    process_id: std::process::id(),
    thread_id: GetCurrentThreadId(), // or even better, the id of the thread that got stuck
    exception_code,
};
// Unsafe function
let cc = crash_context::CrashContext {
    task: mach2::traps::mach_task_self(),
    thread: mach2::mach_init::mach_thread_self(), // preferably the id of the thread that is stuck
    handler_thread: mach2::mach_init::mach_thread_self(),
    exception: None, // or optionally fill out the exception info
};

@simon-fu
Copy link
Author

simon-fu commented Jul 8, 2022

Thanks @Jake-Shadle for the reply
For linux, there are two args that MinidumpWriter::new(process: Pid, blamed_thread: Pid)
How to fill with MinidumpWriter::new with args of process and blamed_thread ?

@Jake-Shadle
Copy link
Collaborator

If you're dumping the same process you could use std::process:id() to get the process identifier, the thread identifier you either retrieve in the thread itself by calling libc::gettid() or if you have a JoinHandle or similar you can get the thread id with https://doc.rust-lang.org/std/os/unix/thread/trait.JoinHandleExt.html#tymethod.as_pthread_t

@simon-fu
Copy link
Author

simon-fu commented Jul 8, 2022

@Jake-Shadle
I use libc::gettid() and fail to build with error undefined reference to `gettid'
And I change to libc::syscall(libc::SYS_gettid) and build success
However, after cargo run it print error message:
No threads left to suspend out of 9

@Jake-Shadle
Copy link
Collaborator

That is most likely because you need to the mark the process as dumpable with libc::syscall(libc::SYS_prctl, PR_SET_DUMPABLE, 1, 0, 0, 0) otherwise most/all ptrace operations will fail. You might also run into problems using minidumper in the same process, it was never designed for that so it has no protections against suspending the thread that is currently executing, the man page doesn't say anything AFAICT about special behavior for attaching to the currently executing thread, so I'm assuming it will actually send SIGSTOP to the thread and dead lock your program, so you could try adding logic to ignore stopping the thread if it is the same thread id as the current thread.

@simon-fu
Copy link
Author

simon-fu commented Jul 8, 2022

libc::syscall(libc::SYS_prctl, PR_SET_DUMPABLE, 1, 0, 0, 0) did not work and still "No threads left to suspend out of 9"
I give up and plan to dump in another process
Thank you very much @Jake-Shadle

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants