On Fri, 16 Aug 2013, Ted Unangst wrote: > As per http://research.swtch.com/macpprof > > We deliver all prof signals to the main thread, which is unlikely to > result in accurate profiling info. I think the diff below fixes things.
How about we take an idea from FreeBSD and have hardclock() just set a flag on the thread and then have the thread send SIGPROF (and SIGVTALRM) to itself from userret(). The signal gets sent by the thread itself right before it checks for pending signals when returning to userspace, so that's absolutely as soon as possible, and it's done from process context instead of from softclock by a timeout, so no "which CPU are we on?" issues that might delay the delivery. Ted, Joel, does this solve your profiling problems? (No, this had nothing at all to do with my splitting the process and thread flags, what could possibly make you think that...) Philip Index: sys/sys/proc.h =================================================================== RCS file: /cvs/src/sys/sys/proc.h,v retrieving revision 1.170 diff -u -p -r1.170 proc.h --- sys/sys/proc.h 22 Sep 2013 17:28:33 -0000 1.170 +++ sys/sys/proc.h 4 Oct 2013 01:00:18 -0000 @@ -209,8 +209,6 @@ struct process { struct timespec ps_start; /* starting time. */ struct timeout ps_realit_to; /* real-time itimer trampoline. */ - struct timeout ps_virt_to; /* virtual itimer trampoline. */ - struct timeout ps_prof_to; /* prof itimer trampoline. */ int ps_refcnt; /* Number of references. */ }; @@ -362,6 +361,8 @@ struct proc { * These flags are per-thread and kept in p_flag */ #define P_INKTR 0x000001 /* In a ktrace op, don't recurse */ +#define P_PROFPEND 0x000002 /* SIGPROF needs to be posted */ +#define P_ALRMPEND 0x000004 /* SIGVTALRM needs to be posted */ #define P_SIGSUSPEND 0x000008 /* Need to restore before-suspend mask*/ #define P_SELECT 0x000040 /* Selecting; wakeup/waiting danger. */ #define P_SINTR 0x000080 /* Sleep is interruptible. */ @@ -380,7 +381,8 @@ struct proc { #define P_CPUPEG 0x40000000 /* Do not move to another cpu. */ #define P_BITS \ - ("\20\01INKTR\04SIGSUSPEND\07SELECT\010SINTR\012SYSTEM" \ + ("\20\01INKTR\02PROFPEND\03ALRMPEND\04SIGSUSPEND\07SELECT" \ + "\010SINTR\012SYSTEM" \ "\013TIMEOUT\016WEXIT\020OWEUPC\024SUSPSINGLE" \ "\025NOZOMBIE\027SYSTRACE\030CONTINUED\033THREAD" \ "\034SUSPSIG\035SOFTDEP\036STOPPED\037CPUPEG") Index: sys/sys/resourcevar.h =================================================================== RCS file: /cvs/src/sys/sys/resourcevar.h,v retrieving revision 1.16 diff -u -p -r1.16 resourcevar.h --- sys/sys/resourcevar.h 3 Jun 2013 16:55:22 -0000 1.16 +++ sys/sys/resourcevar.h 4 Oct 2013 01:00:18 -0000 @@ -68,8 +68,5 @@ struct plimit *limcopy(struct plimit *); void limfree(struct plimit *); void ruadd(struct rusage *, struct rusage *); - -void virttimer_trampoline(void *); -void proftimer_trampoline(void *); #endif #endif /* !_SYS_RESOURCEVAR_H_ */ Index: sys/kern/kern_clock.c =================================================================== RCS file: /cvs/src/sys/kern/kern_clock.c,v retrieving revision 1.82 diff -u -p -r1.82 kern_clock.c --- sys/kern/kern_clock.c 13 Aug 2013 05:52:23 -0000 1.82 +++ sys/kern/kern_clock.c 4 Oct 2013 01:00:19 -0000 @@ -144,40 +144,11 @@ initclocks(void) /* * hardclock does the accounting needed for ITIMER_PROF and ITIMER_VIRTUAL. * We don't want to send signals with psignal from hardclock because it makes - * MULTIPROCESSOR locking very complicated. Instead we use a small trick - * to send the signals safely and without blocking too many interrupts - * while doing that (signal handling can be heavy). - * - * hardclock detects that the itimer has expired, and schedules a timeout - * to deliver the signal. This works because of the following reasons: - * - The timeout can be scheduled with a 1 tick time because we're - * doing it before the timeout processing in hardclock. So it will - * be scheduled to run as soon as possible. - * - The timeout will be run in softclock which will run before we - * return to userland and process pending signals. - * - If the system is so busy that several VIRTUAL/PROF ticks are - * sent before softclock processing, we'll send only one signal. - * But if we'd send the signal from hardclock only one signal would - * be delivered to the user process. So userland will only see one - * signal anyway. + * MULTIPROCESSOR locking very complicated. Instead, to use an idea from + * FreeBSD, we set a flag on the thread and when it goes to return to + * userspace it signals itself. */ -void -virttimer_trampoline(void *v) -{ - struct process *pr = v; - - psignal(pr->ps_mainproc, SIGVTALRM); -} - -void -proftimer_trampoline(void *v) -{ - struct process *pr = v; - - psignal(pr->ps_mainproc, SIGPROF); -} - /* * The real-time timer, interrupting hz times per second. */ @@ -196,11 +167,15 @@ hardclock(struct clockframe *frame) */ if (CLKF_USERMODE(frame) && timerisset(&pr->ps_timer[ITIMER_VIRTUAL].it_value) && - itimerdecr(&pr->ps_timer[ITIMER_VIRTUAL], tick) == 0) - timeout_add(&pr->ps_virt_to, 1); + itimerdecr(&pr->ps_timer[ITIMER_VIRTUAL], tick) == 0) { + atomic_setbits_int(&p->p_flag, P_ALRMPEND); + need_proftick(p); + } if (timerisset(&pr->ps_timer[ITIMER_PROF].it_value) && - itimerdecr(&pr->ps_timer[ITIMER_PROF], tick) == 0) - timeout_add(&pr->ps_prof_to, 1); + itimerdecr(&pr->ps_timer[ITIMER_PROF], tick) == 0) { + atomic_setbits_int(&p->p_flag, P_PROFPEND); + need_proftick(p); + } } /* Index: sys/kern/kern_exit.c =================================================================== RCS file: /cvs/src/sys/kern/kern_exit.c,v retrieving revision 1.127 diff -u -p -r1.127 kern_exit.c --- sys/kern/kern_exit.c 14 Sep 2013 01:35:00 -0000 1.127 +++ sys/kern/kern_exit.c 4 Oct 2013 01:00:20 -0000 @@ -185,8 +185,6 @@ exit1(struct proc *p, int rv, int flags) if ((p->p_flag & P_THREAD) == 0) { timeout_del(&pr->ps_realit_to); - timeout_del(&pr->ps_virt_to); - timeout_del(&pr->ps_prof_to); #ifdef SYSVSEM semexit(pr); #endif Index: sys/kern/kern_fork.c =================================================================== RCS file: /cvs/src/sys/kern/kern_fork.c,v retrieving revision 1.153 diff -u -p -r1.153 kern_fork.c --- sys/kern/kern_fork.c 14 Aug 2013 05:26:14 -0000 1.153 +++ sys/kern/kern_fork.c 4 Oct 2013 01:00:20 -0000 @@ -183,8 +183,6 @@ process_new(struct proc *p, struct proce pr->ps_limit->p_refcnt++; timeout_set(&pr->ps_realit_to, realitexpire, pr); - timeout_set(&pr->ps_virt_to, virttimer_trampoline, pr); - timeout_set(&pr->ps_prof_to, proftimer_trampoline, pr); pr->ps_flags = parent->ps_flags & (PS_SUGID | PS_SUGIDEXEC); if (parent->ps_session->s_ttyvp != NULL && Index: sys/kern/kern_sig.c =================================================================== RCS file: /cvs/src/sys/kern/kern_sig.c,v retrieving revision 1.152 diff -u -p -r1.152 kern_sig.c --- sys/kern/kern_sig.c 1 Jun 2013 04:05:26 -0000 1.152 +++ sys/kern/kern_sig.c 4 Oct 2013 01:00:23 -0000 @@ -1735,6 +1735,20 @@ void userret(struct proc *p) { int sig; + + /* send SIGPROF or SIGVTALRM if their timers interrupted this thread */ + if (p->p_flag & P_PROFPEND) { + atomic_clearbits_int(&p->p_flag, P_PROFPEND); + KERNEL_LOCK(); + psignal(p, SIGPROF); + KERNEL_UNLOCK(); + } + if (p->p_flag & P_ALRMPEND) { + atomic_clearbits_int(&p->p_flag, P_ALRMPEND); + KERNEL_LOCK(); + psignal(p, SIGVTALRM); + KERNEL_UNLOCK(); + } while ((sig = CURSIG(p)) != 0) postsig(sig); Index: bin/ps/ps.1 =================================================================== RCS file: /cvs/src/bin/ps/ps.1,v retrieving revision 1.84 diff -u -p -r1.84 ps.1 --- bin/ps/ps.1 22 Sep 2013 17:28:34 -0000 1.84 +++ bin/ps/ps.1 4 Oct 2013 01:00:23 -0000 @@ -222,6 +222,8 @@ The thread flags (in hexadecimal), as de .Aq Pa sys/proc.h : .Bd -literal P_INKTR 0x1 writing ktrace(2) record +P_PROFPEND 0x2 this thread needs SIGPROF +P_ALRMPEND 0x4 this thread needs SIGVTALRM P_SIGSUSPEND 0x8 need to restore before-suspend mask P_SELECT 0x40 selecting; wakeup/waiting danger P_SINTR 0x80 sleep is interruptible