Skip to content

Implement handling of blocking syscalls #7

Description

@joneschrisg

From @rocallahan

The current implementation has a few problems but the main one is that it doesn't handle potentially-blocking syscalls at all. Blocking syscalls are tricky to handle; ideally when a wrapped syscall suspends its thread, the rr supervisor process would be invoked and handle the wrapped syscall much like a regular syscall --- we'd suspend the thread and to try to wake up another thread. However it's not easy to detect when a syscall will block. For example our read syscalls usually do not block, but might. My current best idea for handling this is to use perf to listen for context-switch events and when a context-switch event is triggered during a wrapped syscall, treat that as a signal that the syscall has blocked. I did some experiements monitoring context-switch events on test programs, and it looks promising, but I never got around to hacking that into rr.

This is a good idea because it doesn't introduce the concept of a "blocking syscall", which is a bit hard to define. Instead it handles all syscalls the same way. More details forthcoming.

Activity

  1. joneschrisg commented on Jun 26, 2013

    @joneschrisg
    ContributorAuthor

    I put together a PoC that signals the ptrace-er when a ptrace-ee has been context-switched a configurable number of times (we'd want that to be "1" for our purposes here). Initial testing suggests that the signals are being delivered as one would expect. The signaling works basically the same way as for the hpc interrupts, except that in our case here the trace-ee has to program the interrupt, from within the syscall buffer lib. There are some corner cases to figure out but this seems like it might work.

  2. joneschrisg commented on Jun 27, 2013

    @joneschrisg
    ContributorAuthor

    After a few more experiments, I'm now pretty confident we can make this work. My little test program is here. To make this "actually" work, we need to process the de-sched interrupts in the parent process and disarm the counter there, which is pretty straightforward but needs a bunch of nasty POSIX gloop to pull off. Will prove that out next, since that part must be bulletproof.

    @rocallahan I should have asked this before I got started, but (i) was your approach about the same as this? (iI) if not, do you happen to have your old test program around?

  3. rocallahan commented on Jun 27, 2013

    @rocallahan
    Collaborator

    You've already gone way beyond me --- I just had a little test program and manually ran "perf" to observe the progress of the context-switch counter.

  4. joneschrisg commented on Jun 27, 2013

    @joneschrisg
    ContributorAuthor

    The ptrace and UNIXbeard hackery is mostly working here, but I'm getting a seemingly-spurious desched notification that I don't understand yet. Or, there's an actual desched happening and I don't know where it's coming from. I have a hackaround that's not so bad and works, but will see if I can tell what's causing it.

  5. joneschrisg commented on Jun 27, 2013

    @joneschrisg
    ContributorAuthor

    The extra SIGIO doesn't seem to be an extra desched, because the switch counter is the same in the first and second. I also tried monitoring desched on syscalls that block for increasingly longer durations, and the extra SIGIO is always sent immediately after desched, not at arbitrary times. So I'm pretty close to ascribing this to magic for the time being and moving on.

  6. joneschrisg commented on Jun 27, 2013

    @joneschrisg
    ContributorAuthor

    Here's about the only hypothesis I have

    • child gets descheduled, bumps counter to i and schedules SIGIO
    • SIGIO notification "schedules" child, but it doesn't run
    • child is being ptraced, so we "deschedule" child to notify parent and bump counter to i+1
    • counter signal generated, but SIGIO is already pending so this one is queued
    • parent is notified and sees counter value i+1
    • parent stops delivery of first signal and shuts off counter
    • second SIGIO dequeued and delivered, notififying parent (counter is off, so no pseudo-desched possible here)
    • parent notified and sees counter value i+1 again
    • parent stops delivery of second SIGIO and we continue on

    If this is what's happening, then the current hackaround should be a reliable way of dealing with this.

  7. joneschrisg commented on Jun 27, 2013

    @joneschrisg
    ContributorAuthor

    (I also confirmed that the $ip doesn't advance during the pseudo-step, when the program being traced isn't blocked on a syscall.)

  8. joneschrisg commented on Jun 28, 2013

    @joneschrisg
    ContributorAuthor

    I have a PoC for "rpc syscalls", where a tracee requests that the tracer make syscall(s) on its behalf, here. I think this is the last piece of technology we need.

  9. rocallahan commented on Jun 28, 2013

    @rocallahan
    Collaborator

    Why is that needed?

  10. joneschrisg commented on Jun 28, 2013

    @joneschrisg
    ContributorAuthor

    We have to do a pretty tricky dance to set up the cs counter and syscall buffer region across record/replay. During recording, the counter fd has to be shared with rr, but in replay it should be emulated. This requires letting rr know the fd number that's to be shared. In both record and replay, the buffer region has to be actually opened and mmap'd by both the tracee and rr. rr also has to know it's the syscall buffer initialization code it's talking to. The cleanest way I could think of to implement this is with a fake syscall; use an unallocated syscall number (like -42) to trap into rr, then let rr decide what to do. That's what the PoC does.

    I think we could make this work by keying off syscall param pattern matching and so forth, but I'd rather get away from that.

  11. rocallahan commented on Jun 28, 2013

    @rocallahan
    Collaborator

    Sounds reasonable.

  12. joneschrisg commented on Jun 28, 2013

    @joneschrisg
    ContributorAuthor

    In other words, instead of assuming various things about the sequence of mmap/socket/recv/etc calls and patterns of parameters thereto, we could instead do something like

    int counterfd = perf_event_open(...);
    char* shmfile = "/dev/shm/rr-tracee-[tid]";
    struct msghdr msg;
    // set up msg
    syscall(MAGIC_SYSCALL_BUF_INIT, counterfd, shmfile, &msg);
    

    then rr decides what do with the params based on record vs. replay.

  13. joneschrisg commented on Jun 28, 2013

    @joneschrisg
    ContributorAuthor

    (Note to self: need to sigprocmask during this setup or things will get very hard.)

  14. 16 remaining items

  15. joneschrisg commented on Jul 3, 2013

    @joneschrisg
    ContributorAuthor

    This patch ended up being less straightforward than I was hoping, so I'll try to put up a wiki page with a walkthrough in the near future.

  16. joneschrisg commented on Jul 31, 2013

    @joneschrisg
    ContributorAuthor

    Hmm, so will we have to do these two extra ioctls around every potentially-blocking syscall? That's very unfortunate.

    These are taking ~600-800ns each, which, unfortunately, ends up being small potatoes compared to the overhead of a traced syscall. >1us per syscall doesn't make me happy but it's not significantly impacting execution time in the traces I'm looking at.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions