int main(int argc, char *argv[]) { if (argc != 2) return 0; sscanf(argv[1], "%d", &pid); printf("attaching\n"); proctrace(PTRACE_ATTACH, pid, NULL, NULL); printf("waiting for child to stop\n"); proctrace_wait(-1, NULL, 0, NULL); print_signal(); printf("waited. child should be stopped\n"); proctrace(PTRACE_SYSCALL, pid, NULL, NULL); proctrace(PTRACE_CONT, pid, NULL, NULL); printf("waiting for the system call\n"); proctrace_wait(-1, NULL, 0, NULL); print_signal(); printf("child did it's thing. continuing and waiting for child to quit...\n"); long sc = proctrace(PTRACE_PEEKUSER, pid, (void*) (4 * ORIG_EAX), NULL); printf("not minus 1: %ld\n", sc); proctrace(PTRACE_CONT, pid, NULL, NULL); proctrace_wait(-1, NULL, 0, NULL); return 0; }
static void schedready(Sched *sch, Proc *p, int locked) { Mpl pl; int pri; Schedq *rq; pl = splhi(); if(edfready(p)){ splx(pl); return; } /* if(m->externup != p) m->readied = p; *//* group scheduling, will be removed */ updatecpu(p); pri = reprioritize(p); p->priority = pri; rq = &sch->runq[pri]; p->state = Ready; queueproc(sch, rq, p, locked); if(p->trace) proctrace(p, SReady, 0); splx(pl); }
static void deadlineintr(Ureg* ureg, Timer *t) { Proc *up = externup(); /* Proc reached deadline */ extern int panicking; Sched *sch; Proc *p; if(panicking || active.exiting) return; p = t->ta; now = ms(); DPRINT("%lu deadlineintr %d[%s]\n", now, p->pid, statename[p->state]); /* If we're interrupting something other than the proc pointed to by t->a, * we've already achieved recheduling, so we need not do anything * Otherwise, we must cause a reschedule, but if we call sched() * here directly, the timer interrupt routine will not finish its business * Instead, we cause the resched to happen when the interrupted proc * returns to user space */ if(p == up){ if(up->trace) proctrace(up, SInts, 0); up->delaysched++; sch = procsched(up); sch->delayedscheds++; } }
void edfrecord(Proc *p) { int32_t used; Edf *e; if((e = edflock(p)) == nil) return; used = now - e->s; if(e->d - now <= 0) e->edfused += used; else e->extraused += used; if(e->S > 0){ if(e->S <= used){ if(p->trace) proctrace(p, SSlice, 0); DPRINT("%lu edfrecord slice used up\n", now); e->d = now; e->S = 0; }else e->S -= used; } e->s = now; edfunlock(); }
/* * SMP performs better than AMP with few cores. * So, leave this here by now. We should probably * write a unified version of runproc good enough for * both SMP and AMP. */ static Proc* smprunproc(void) { Mach *m = machp(); Schedq *rq; Proc *p; uint32_t start, now; int i; start = perfticks(); run.preempts++; loop: /* * find a process that last ran on this processor (affinity), * or one that hasn't moved in a while (load balancing). Every * time around the loop affinity goes down. */ spllo(); for(i = 0;; i++){ /* * find the highest priority target process that this * processor can run given affinity constraints. * */ for(rq = &run.runq[Nrq-1]; rq >= run.runq; rq--){ for(p = rq->head; p; p = p->rnext){ if(p->mp == nil || p->mp == sys->machptr[m->machno] || (!p->wired && i > 0)) goto found; } } /* waste time or halt the CPU */ idlehands(); /* remember how much time we're here */ now = perfticks(); m->perf.inidle += now-start; start = now; } found: splhi(); p = dequeueproc(&run, rq, p); if(p == nil) goto loop; p->state = Scheding; p->mp = sys->machptr[m->machno]; if(edflock(p)){ edfrun(p, rq == &run.runq[PriEdf]); /* start deadline timer and do admin */ edfunlock(); } if(p->trace) proctrace(p, SRun, 0); return p; }
static void release(Proc *p) { /* Called with edflock held */ Edf *e; int32_t n; int64_t nowns; e = p->edf; e->flags &= ~Yield; if(e->d - now < 0){ e->periods++; e->r = now; if((e->flags & Sporadic) == 0){ /* * Non sporadic processes stay true to their period; * calculate next release time. * Second test limits duration of while loop. */ if((n = now - e->t) > 0){ if(n < e->T) e->t += e->T; else e->t = now + e->T - (n % e->T); } }else{ /* Sporadic processes may not be released earlier than * one period after this release */ e->t = e->r + e->T; } e->d = e->r + e->D; e->S = e->C; DPRINT("%lu release %d[%s], r=%lu, d=%lu, t=%lu, S=%lu\n", now, p->pid, statename[p->state], e->r, e->d, e->t, e->S); if(p->trace){ nowns = todget(nil); proctrace(p, SRelease, nowns); proctrace(p, SDeadline, nowns + 1000LL*e->D); } }else{ DPRINT("%lu release %d[%s], too late t=%lu, called from %#p\n", now, p->pid, statename[p->state], e->t, getcallerpc()); } }
// substitute for wait4() system call pid_t proctrace_wait(pid_t pid, int *status, int options, struct rusage *rusage) { #ifdef USE_PTRACE return wait4(pid, status, options, rusage); #endif disable_signal(); if (attached_pid == 0) { if (pid == -1) { printf("I don't know what to do in proctrace_wait\n"); exit(1); } attached_pid = pid; } if (singlestep) { ctl("step"); singlestep = 0; } else { char buf[35]; sprintf(buf, "/proc/%d/wait", attached_pid); int fd = open(buf, O_WRONLY); proctrace_wait_mask = ((1ULL << 31) - 1) << 1; proctrace_wait_mask |= 1ULL; proctrace_wait_mask |= (1ULL << 40); // temp hack for strace proctrace_wait_mask |= (1ULL << 33); unsigned long long big_endian = 0; int i; for (i = 0; i < 8; ++i) { big_endian |= ((proctrace_wait_mask >> (8*i)) & ((1 << 8)-1)) << (8-i-1)*8; } long retvalue = write(fd, &big_endian, sizeof(big_endian)); if (retvalue < 0) { printf("write to wait failed, fd: %d, buf: %s\n", fd, buf); } close(fd); } if (status) { siginfo_t a; if (proctrace(PTRACE_GETSIGINFO, attached_pid, NULL, (void *)&a) == -1) { *status = 0; } else { if (a.si_signo == 0) a.si_signo = SIGSTOP; if (a.si_signo > 32) { *status = (SIGTRAP << 8) | 0177; } else { *status = (a.si_signo << 8) | 0177; } } } return attached_pid; }
void edfstop(Proc *p) { Edf *e; if(e = edflock(p)){ DPRINT("%lu edfstop %d[%s]\n", now, p->pid, statename[p->state]); if(p->trace) proctrace(p, SExpel, 0); e->flags &= ~Admitted; if(e->Timer.tt) timerdel(&e->Timer); edfunlock(); } }
void qlock(QLock *q) { Proc *up = externup(); Proc *p; uint64_t t0; cycles(&t0); if(!islo() && machp()->ilockdepth != 0){ print("qlock with ilockdepth %d,", machp()->ilockdepth); stacksnippet(); } if(up != nil && up->nlocks) print("qlock: %#p: nlocks %d", getcallerpc(&q), up->nlocks); if(!canlock(&q->use)){ lock(&q->use); slockstat(getcallerpc(&q), t0); } qlockstats.qlock++; if(!q->locked) { q->locked = 1; q->pc = getcallerpc(&q); unlock(&q->use); return; } if(up == nil) panic("qlock"); qlockstats.qlockq++; p = q->tail; if(p == 0) q->head = up; else p->qnext = up; q->tail = up; up->qnext = 0; up->state = Queueing; up->qpc = getcallerpc(&q); if(up->trace) proctrace(up, SLock, 0); unlock(&q->use); sched(); lockstat(getcallerpc(&q), t0); }
Proc* runproc(void) { Mach *m = machp(); Schedq *rq; Proc *p; uint32_t start, now; if(nosmp) return singlerunproc(); //NIX modeset cannot work without halt every cpu at boot //if(sys->nmach <= AMPmincores) else return smprunproc(); start = perfticks(); run.preempts++; rq = nil; if(m->machno != 0){ do{ spllo(); while(m->proc == nil) idlehands(); now = perfticks(); m->perf.inidle += now-start; start = now; splhi(); p = m->proc; }while(p == nil); p->state = Scheding; p->mp = sys->machptr[m->machno]; if(edflock(p)){ edfrun(p, rq == &run.runq[PriEdf]); /* start deadline timer and do admin */ edfunlock(); } if(p->trace) proctrace(p, SRun, 0); return p; } mach0sched(); return nil; /* not reached */ }
void edfrun(Proc *p, int edfpri) { Edf *e; int32_t tns; Sched *sch; e = p->edf; sch = procsched(p); /* Called with edflock held */ if(edfpri){ tns = e->d - now; if(tns <= 0 || e->S == 0){ /* Deadline reached or resources exhausted, * deschedule forthwith */ p->delaysched++; sch->delayedscheds++; e->s = now; return; } if(e->S < tns) tns = e->S; if(tns < 20) tns = 20; e->Timer.tns = 1000LL * tns; /* µs to ns */ if(e->Timer.tt == nil || e->Timer.tf != deadlineintr){ DPRINT("%lu edfrun, deadline=%lu\n", now, tns); }else{ DPRINT("v"); } if(p->trace) proctrace(p, SInte, todget(nil) + e->Timer.tns); e->Timer.tmode = Trelative; e->Timer.tf = deadlineintr; e->Timer.ta = p; timeradd(&e->Timer); }else{ DPRINT("<"); } e->s = now; }
void wlock(RWlock *q) { Proc *up = externup(); Proc *p; uint64_t t0; cycles(&t0); if(!canlock(&q->use)){ lock(&q->use); slockstat(getcallerpc(&q), t0); } qlockstats.wlock++; if(q->readers == 0 && q->writer == 0){ /* noone waiting, go for it */ q->wpc = getcallerpc(&q); q->wproc = up; q->writer = 1; unlock(&q->use); return; } /* wait */ qlockstats.wlockq++; p = q->tail; if(up == nil) panic("wlock"); if(p == nil) q->head = up; else p->qnext = up; q->tail = up; up->qnext = 0; up->state = QueueingW; if(up->trace) proctrace(up, SLock, 0); unlock(&q->use); sched(); lockstat(getcallerpc(&q), t0); }
static void schedready(Sched *sch, Proc *p, int locked) { Mpl pl; int pri; Schedq *rq; pl = splhi(); if(edfready(p)){ splx(pl); return; } updatecpu(p); pri = reprioritize(p); p->priority = pri; rq = &sch->runq[pri]; p->state = Ready; queueproc(sch, rq, p, locked); if(p->trace) proctrace(p, SReady, 0); splx(pl); }
void edfyield(void) { Proc *up = externup(); /* sleep until next release */ Edf *e; int32_t n; if((e = edflock(up)) == nil) return; if(up->trace) proctrace(up, SYield, 0); if((n = now - e->t) > 0){ if(n < e->T) e->t += e->T; else e->t = now + e->T - (n % e->T); } e->r = e->t; e->flags |= Yield; e->d = now; if (up->Timer.tt == nil){ n = e->t - now; if(n < 20) n = 20; up->Timer.tns = 1000LL * n; up->Timer.tf = releaseintr; up->Timer.tmode = Trelative; up->Timer.ta = up; up->trend = &up->sleep; timeradd(&up->Timer); }else if(up->Timer.tf != releaseintr) print("edfyield: surprise! %#p\n", up->Timer.tf); edfunlock(); sleep(&up->sleep, yfn, nil); }
void print_signal() { siginfo_t sig; proctrace(PTRACE_GETSIGINFO, pid, NULL, &sig); printf("I quit because of the signal %d\n", sig.si_signo); }
int edfready(Proc *p) { Edf *e; Sched *sch; Schedq *rq; Proc *l, *pp; int32_t n; if((e = edflock(p)) == nil) return 0; if(p->state == Wakeme && p->r){ iprint("edfready: wakeme\n"); } if(e->d - now <= 0){ /* past deadline, arrange for next release */ if((e->flags & Sporadic) == 0){ /* * Non sporadic processes stay true to their period; * calculate next release time. */ if((n = now - e->t) > 0){ if(n < e->T) e->t += e->T; else e->t = now + e->T - (n % e->T); } } if(now - e->t < 0){ /* Next release is in the future, schedule it */ if(e->Timer.tt == nil || e->Timer.tf != releaseintr){ n = e->t - now; if(n < 20) n = 20; e->Timer.tns = 1000LL * n; e->Timer.tmode = Trelative; e->Timer.tf = releaseintr; e->Timer.ta = p; timeradd(&e->Timer); DPRINT("%lu edfready %d[%s], release=%lu\n", now, p->pid, statename[p->state], e->t); } if(p->state == Running && (e->flags & (Yield|Yieldonblock)) == 0 && (e->flags & Extratime)){ /* If we were running, we've overrun our CPU allocation * or missed the deadline, continue running best-effort at low priority * Otherwise we were blocked. If we don't yield on block, we continue * best effort */ DPRINT(">"); p->basepri = PriExtra; p->fixedpri = 1; edfunlock(); return 0; /* Stick on runq[PriExtra] */ } DPRINT("%lu edfready %d[%s] wait release at %lu\n", now, p->pid, statename[p->state], e->t); p->state = Waitrelease; edfunlock(); return 1; /* Make runnable later */ } DPRINT("%lu edfready %d %s release now\n", now, p->pid, statename[p->state]); /* release now */ release(p); } edfunlock(); DPRINT("^"); sch = procsched(p); rq = &sch->runq[PriEdf]; /* insert in queue in earliest deadline order */ lock(&sch->l); l = nil; for(pp = rq->head; pp; pp = pp->rnext){ if(pp->edf->d > e->d) break; l = pp; } p->rnext = pp; if (l == nil) rq->head = p; else l->rnext = p; if(pp == nil) rq->tail = p; rq->n++; sch->nrdy++; sch->runvec |= 1 << PriEdf; p->priority = PriEdf; p->readytime = machp()->ticks; p->state = Ready; unlock(&sch->l); if(p->trace) proctrace(p, SReady, 0); return 1; }
char * edfadmit(Proc *p) { Proc *up = externup(); char *err; Edf *e; int i; Proc *r; int32_t tns; e = p->edf; if (e->flags & Admitted) return "task state"; /* should never happen */ /* simple sanity checks */ if (e->T == 0) return "T not set"; if (e->C == 0) return "C not set"; if (e->D > e->T) return "D > T"; if (e->D == 0) /* if D is not set, set it to T */ e->D = e->T; if (e->C > e->D) return "C > D"; qlock(&edfschedlock); if (err = testschedulability(p)){ qunlock(&edfschedlock); return err; } e->flags |= Admitted; edflock(p); if(p->trace) proctrace(p, SAdmit, 0); /* Look for another proc with the same period to synchronize to */ for(i=0; (r = psincref(i)) != nil; i++) { if(r->state == Dead || r == p){ psdecref(r); continue; } if (r->edf == nil || (r->edf->flags & Admitted) == 0){ psdecref(r); continue; } if (r->edf->T == e->T) break; } if (r == nil){ /* Can't synchronize to another proc, release now */ e->t = now; e->d = 0; release(p); if (p == up){ DPRINT("%lu edfadmit self %d[%s], release now: r=%lu d=%lu t=%lu\n", now, p->pid, statename[p->state], e->r, e->d, e->t); /* We're already running */ edfrun(p, 1); }else{ /* We're releasing another proc */ DPRINT("%lu edfadmit other %d[%s], release now: r=%lu d=%lu t=%lu\n", now, p->pid, statename[p->state], e->r, e->d, e->t); p->Timer.ta = p; edfunlock(); qunlock(&edfschedlock); releaseintr(nil, &p->Timer); return nil; } }else{ /* Release in synch to something else */ e->t = r->edf->t; psdecref(r); if (p == up){ DPRINT("%lu edfadmit self %d[%s], release at %lu\n", now, p->pid, statename[p->state], e->t); }else{ DPRINT("%lu edfadmit other %d[%s], release at %lu\n", now, p->pid, statename[p->state], e->t); if(e->Timer.tt == nil){ e->Timer.tf = releaseintr; e->Timer.ta = p; tns = e->t - now; if(tns < 20) tns = 20; e->Timer.tns = 1000LL * tns; e->Timer.tmode = Trelative; timeradd(&e->Timer); } } } edfunlock(); qunlock(&edfschedlock); return nil; }
/* * sleep if a condition is not true. Another process will * awaken us after it sets the condition. When we awaken * the condition may no longer be true. * * we lock both the process and the rendezvous to keep r->p * and p->r synchronized. */ void sleep(Rendez *r, int (*f)(void*), void *arg) { Mach *m = machp(); Mpl pl; pl = splhi(); if(m->externup->nlocks) print("process %d sleeps with %d locks held, last lock %#p locked at pc %#p, sleep called from %#p\n", m->externup->pid, m->externup->nlocks, m->externup->lastlock, m->externup->lastlock->_pc, getcallerpc(&r)); lock(r); lock(&m->externup->rlock); if(r->_p){ print("double sleep called from %#p, %d %d\n", getcallerpc(&r), r->_p->pid, m->externup->pid); dumpstack(); } /* * Wakeup only knows there may be something to do by testing * r->p in order to get something to lock on. * Flush that information out to memory in case the sleep is * committed. */ r->_p = m->externup; if((*f)(arg) || m->externup->notepending){ /* * if condition happened or a note is pending * never mind */ r->_p = nil; unlock(&m->externup->rlock); unlock(r); } else { /* * now we are committed to * change state and call scheduler */ if(m->externup->trace) proctrace(m->externup, SSleep, 0); m->externup->state = Wakeme; m->externup->r = r; /* statistics */ m->cs++; procsave(m->externup); mmuflushtlb(m->pml4->pa); if(setlabel(&m->externup->sched)) { /* * here when the process is awakened */ procrestore(m->externup); spllo(); } else { /* * here to go to sleep (i.e. stop Running) */ unlock(&m->externup->rlock); unlock(r); /*debug*/gotolabel(&m->sched); } } if(m->externup->notepending) { m->externup->notepending = 0; splx(pl); if(m->externup->procctl == Proc_exitme && m->externup->closingfgrp) forceclosefgrp(); error(Eintr); } splx(pl); }
void pexit(char *exitstr, int freemem) { Mach *m = machp(); Proc *p; Segment **s, **es; int32_t utime, stime; Waitq *wq, *f, *next; Fgrp *fgrp; Egrp *egrp; Rgrp *rgrp; Pgrp *pgrp; Chan *dot; if(0 && m->externup->nfullq > 0) iprint(" %s=%d", m->externup->text, m->externup->nfullq); if(0 && m->externup->nicc > 0) iprint(" [%s nicc %ud tctime %ulld actime %ulld]\n", m->externup->text, m->externup->nicc, m->externup->tctime, m->externup->actime); if(m->externup->syscalltrace != nil) free(m->externup->syscalltrace); m->externup->syscalltrace = nil; m->externup->alarm = 0; if (m->externup->tt) timerdel(m->externup); if(m->externup->trace) proctrace(m->externup, SDead, 0); /* nil out all the resources under lock (free later) */ qlock(&m->externup->debug); fgrp = m->externup->fgrp; m->externup->fgrp = nil; egrp = m->externup->egrp; m->externup->egrp = nil; rgrp = m->externup->rgrp; m->externup->rgrp = nil; pgrp = m->externup->pgrp; m->externup->pgrp = nil; dot = m->externup->dot; m->externup->dot = nil; qunlock(&m->externup->debug); if(fgrp) closefgrp(fgrp); if(egrp) closeegrp(egrp); if(rgrp) closergrp(rgrp); if(dot) cclose(dot); if(pgrp) closepgrp(pgrp); /* * if not a kernel process and have a parent, * do some housekeeping. */ if(m->externup->kp == 0) { p = m->externup->parent; if(p == 0) { if(exitstr == 0) exitstr = "unknown"; //die("bootprocessdeath"); panic("boot process died: %s", exitstr); } while(waserror()) ; wq = smalloc(sizeof(Waitq)); poperror(); wq->w.pid = m->externup->pid; utime = m->externup->time[TUser] + m->externup->time[TCUser]; stime = m->externup->time[TSys] + m->externup->time[TCSys]; wq->w.time[TUser] = tk2ms(utime); wq->w.time[TSys] = tk2ms(stime); wq->w.time[TReal] = tk2ms(sys->machptr[0]->ticks - m->externup->time[TReal]); if(exitstr && exitstr[0]) snprint(wq->w.msg, sizeof(wq->w.msg), "%s %d: %s", m->externup->text, m->externup->pid, exitstr); else wq->w.msg[0] = '\0'; lock(&p->exl); /* * Check that parent is still alive. */ if(p->pid == m->externup->parentpid && p->state != Broken) { p->nchild--; p->time[TCUser] += utime; p->time[TCSys] += stime; /* * If there would be more than 128 wait records * processes for my parent, then don't leave a wait * record behind. This helps prevent badly written * daemon processes from accumulating lots of wait * records. */ if(p->nwait < 128) { wq->next = p->waitq; p->waitq = wq; p->nwait++; wq = nil; wakeup(&p->waitr); } } unlock(&p->exl); if(wq) free(wq); } if(!freemem) addbroken(m->externup); qlock(&m->externup->seglock); es = &m->externup->seg[NSEG]; for(s = m->externup->seg; s < es; s++) { if(*s) { putseg(*s); *s = 0; } } qunlock(&m->externup->seglock); lock(&m->externup->exl); /* Prevent my children from leaving waits */ psunhash(m->externup); m->externup->pid = 0; wakeup(&m->externup->waitr); unlock(&m->externup->exl); for(f = m->externup->waitq; f; f = next) { next = f->next; free(f); } /* release debuggers */ qlock(&m->externup->debug); if(m->externup->pdbg) { wakeup(&m->externup->pdbg->sleep); m->externup->pdbg = 0; } qunlock(&m->externup->debug); /* Sched must not loop for these locks */ lock(&procalloc); lock(&pga); stopac(); edfstop(m->externup); m->externup->state = Moribund; sched(); panic("pexit"); }
static Proc* singlerunproc(void) { Mach *m = machp(); Schedq *rq; Proc *p; uint32_t start, now, skipscheds; int i; start = perfticks(); /* cooperative scheduling until the clock ticks */ if((p=m->readied) && p->mach==0 && p->state==Ready && &run.runq[Nrq-1].head == nil && &run.runq[Nrq-2].head == nil){ skipscheds++; rq = &run.runq[p->priority]; if(0)hi("runproc going to found before loop...\n"); goto found; } run.preempts++; loop: /* * find a process that last ran on this processor (affinity), * or one that hasn't moved in a while (load balancing). Every * time around the loop affinity goes down. */ spllo(); for(i = 0;; i++){ /* * find the highest priority target process that this * processor can run given affinity constraints. * */ for(rq = &run.runq[Nrq-1]; rq >= run.runq; rq--){ for(p = rq->head; p; p = p->rnext){ if(p->mp == nil || p->mp == sys->machptr[m->machno] || (!p->wired && i > 0)) { if(0)hi("runproc going to found inside loop...\n"); goto found; } } } /* waste time or halt the CPU */ idlehands(); /* remember how much time we're here */ now = perfticks(); m->perf.inidle += now-start; start = now; } found: splhi(); if(0)hi("runproc into found...\n"); p = dequeueproc(&run, rq, p); if(p == nil) { if(0)hi("runproc p=nil :(\n"); goto loop; } p->state = Scheding; if(0)hi("runproc, pm->mp = sys->machptr[m->machno]\n"); p->mp = sys->machptr[m->machno]; if(0){hi("runproc, sys->machptr[m->machno] = "); put64((uint64_t)p->mp); hi("\n");} if(edflock(p)){ edfrun(p, rq == &run.runq[PriEdf]); /* start deadline timer and do admin */ edfunlock(); } if(p->trace) proctrace(p, SRun, 0); /* avoiding warnings, this will be removed */ USED(mach0sched); USED(smprunproc); if(0){hi("runproc, returning p "); put64((uint64_t)p); hi("\n");} return p; }